日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

android 日历动画效果,Android仿 MIUI日历

發布時間:2023/12/20 Android 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android 日历动画效果,Android仿 MIUI日历 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

這個日歷是一個仿MIUI交互的日歷,盡可能實現MIUI日歷的交互設計,加入了一些自定義屬性,如設置默認視圖,設置一周的第一天是周日還是周一等。這個日歷是在之前我寫的那個日歷基礎上改的,里面的關于繪制的部分和之前是一樣的,這篇文章就不再說了,這次主要說一下怎么實現miui日歷的滑動效果。

效果圖

項目地址

日歷實現思路

1、視圖切換。周日歷顯示的位置是不變的,一直都在布局的頂部,可以固定在頂部,NCalendar類主要控制月日歷MonthCalendar和NestedScrollingChild,隨著滑動位置的變化控制周日歷的顯示和隱藏。

2、滑動的處理。計算不同選中日期月日歷MonthCalendar和NestedScrollingChild的子類所需要滑動的距離,使用View的offsetTopAndBottom(int offset)方法完成滑動。

具體實現

1、初始化NCalendar類

在NCalendar類的構造方法中,首先new了一個月日歷MonthCalendar和一個周日歷WeekCalendar,并確定這兩個日歷的高度,月日歷的高度可以通過自定義屬性設置,默認為300dp,周日歷則為月日歷的五分之一,然后把月日歷MonthCalendar和周日歷WeekCalendar通過addView方法添加到NCalendar中,在onLayout中排列各自的位置。代碼public?NCalendar(Context?context,?@Nullable?AttributeSet?attrs,?int?defStyleAttr)?{????????super(context,?attrs,?defStyleAttr);

monthCalendar?=?new?MonthCalendar(context,?attrs);

weekCalendar?=?new?WeekCalendar(context,?attrs);

weekHeigh?=?monthHeigh?/?5;

monthCalendar.setLayoutParams(new?FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,?monthHeigh));

weekCalendar.setLayoutParams(new?FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,?weekHeigh));

addView(monthCalendar);

addView(weekCalendar);

post(new?Runnable()?{????????????@Override

public?void?run()?{

weekCalendar.setVisibility(STATE?==?MONTH???INVISIBLE?:?VISIBLE);

}

});

}

實際項目中,構造方法中還有設置默認視圖,初始化動畫等,這里只貼出來關鍵代碼。

2、onNestedPreScroll

NCalendar實現了NestedScrollingParent,主要的交互都在這兩個方法中完成,onNestedPreScroll和onStopNestedScroll,前一個是嵌套滑動時調用,后一個是停止滑動的回調。我們需要在onNestedPreScroll中完成上滑和下滑時,改變月日歷MonthCalendar和NestedScrollingChild的位置,在onStopNestedScroll中完成手指離開后自動滑動。miui日歷的上滑操作是先讓月日歷滑動到選中的選中的那一行,再向上移動NestedScrollingChild,下滑時也是先移動月日歷,再移動NestedScrollingChild。NClendar在處理這個滑動時分了四種情況:月日歷和NestedScrollingChild同時上滑。

月日歷上滑到一定位置后,NestedScrollingChild單獨上滑。

月日歷和NestedScrollingChild同時下滑。

月日歷下滑到一定位置后,NestedScrollingChild單獨下滑。

這四種情況判斷的條件就是月日歷和NestedScrollingChild距離頂部的位置。NestedScrollingChild距頂部距離的判斷比較簡單,月視圖時,距離就是月日歷的高度,周視圖時就是周日歷的高度。月日歷距頂部的距離稍微復雜一下,需要先計算出來月日歷總共需要滑動的距離monthCalendarOffset,再通過offsetTopAndBottom移動,移動的時候,不斷獲取月日歷的getTop(),當getTop()達到總的偏移量時,就說明月日歷已經移動到指定位置,接下來就是NestedScrollingChild單獨滑動。上滑下滑都是這個邏輯。代碼:@Override????public?void?onNestedPreScroll(View?target,?int?dx,?int?dy,?int[]?consumed)?{????????int?monthTop?=?monthCalendar.getTop();????????int?nestedScrollingChildTop?=?nestedScrollingChild.getTop();

monthCalendarOffset?=?getMonthCalendarOffset();????????//4種情況

if?(dy?>?0?&&?Math.abs(monthTop)?

int?offset?=?getOffset(dy,?monthCalendarOffset?-?Math.abs(monthTop));

monthCalendar.offsetTopAndBottom(-offset);

nestedScrollingChild.offsetTopAndBottom(-offset);

consumed[1]?=?dy;

}?else?if?(dy?>?0?&&?nestedScrollingChildTop?>?weekHeigh)?{????????????//月日歷滑動到位置后,nestedScrollingChild繼續上滑,覆蓋一部分月日歷

int?offset?=?getOffset(dy,?nestedScrollingChildTop?-?weekHeigh);

nestedScrollingChild.offsetTopAndBottom(-offset);

consumed[1]?=?dy;

}?else?if?(dy?

int?offset?=?getOffset(Math.abs(dy),?Math.abs(monthTop));

monthCalendar.offsetTopAndBottom(offset);

nestedScrollingChild.offsetTopAndBottom(offset);

consumed[1]?=?dy;

}?else?if?(dy?

int?offset?=?getOffset(Math.abs(dy),?monthHeigh?-?nestedScrollingChildTop);

nestedScrollingChild.offsetTopAndBottom(offset);

consumed[1]?=?dy;

}????????//nestedScrollingChild滑動到周位置后,標記狀態,同時周日顯示

if?(nestedScrollingChildTop?==?weekHeigh)?{

STATE?=?WEEK;

weekCalendar.setVisibility(VISIBLE);

}????????//周狀態,下滑顯示月日歷,把周日歷隱掉

if?(STATE?==?WEEK?&&?dy?

weekCalendar.setVisibility(INVISIBLE);

}????????//徹底滑到月日歷,標記狀態

if?(nestedScrollingChildTop?==?monthHeigh)?{

STATE?=?MONTH;

}

}

根據需求,需要判斷NestedScrollingChild的條目已經不能再滑動時才移動NestedScrollingChild本身。在滑動過程中,要標記當前視圖的狀態?MONTH?或者WEEK。其中計算monthCalendarOffset的方法://月日歷需要滑動的距離,

private?int?getMonthCalendarOffset()?{

NMonthView?currectMonthView?=?monthCalendar.getCurrectMonthView();????????//該月有幾行

int?rowNum?=?currectMonthView.getRowNum();????????//現在選中的是第幾行

int?selectRowIndex?=?currectMonthView.getSelectRowIndex();????????//month需要移動selectRowIndex*h/rowNum?,計算時依每個行高的中點計算

int?monthCalendarOffset?=?selectRowIndex?*?currectMonthView.getDrawHeight()?/?rowNum;????????return?monthCalendarOffset;

}

計算方法是,得到當前月的View?NMonthView?,再通過該月的行數(5或6)、被選中的日期在哪一行以及NMonthView?的繪制高度算出月日歷需要移動的距離。月日歷的繪制高度和月日歷的高度不是一個數值,因為當月份有6行時,公歷日期繪制在一行的中間位置,下面的農歷就沒有太多的地方繪制,在最后一行的農歷就會和月日歷底部非常接而影響美觀,為了避免這種情況,日歷View繪制的時候,把繪制高度比日歷高度小了一點,這里需要計算的移動量是由繪制區域的行高決定。

3、onStopNestedScroll

這個方法處理自動滑動的問題。在滑動過程中如果松手,日歷要自動回到對應的位置,對應的位置就是說,滑動的距離小時,還回到原來的位置,滑動的距離大時,回到相反的位置。這里的動畫用的是ValueAnimator,在初始化NCalendar時,new了兩個ValueAnimator對象,在onStopNestedScroll回調時,分別給他們的起始值和結束值,再通過動畫中得到的getAnimatedValue值,計算偏移量,執行offsetTopAndBottom方法,完成動畫。@Override

public?void?onStopNestedScroll(View?target)?{?????????//停止滑動的時候,距頂部的距離

int?monthCalendarTop?=?monthCalendar.getTop();????????int?nestedScrollingChildTop?=?nestedScrollingChild.getTop();????????if?(monthCalendarTop?==?0?&&?nestedScrollingChildTop?==?monthHeigh)?{????????????return;

}????????if?(monthCalendarTop?==?-monthCalendarOffset?&&?nestedScrollingChildTop?==?weekHeigh)?{????????????return;

}????????if?(STATE?==?MONTH)?{????????//nestedScrollingChild移動的超過周高度時才會滑動到周

if?(monthHeigh?-?nestedScrollingChildTop?

autoScroll(monthCalendarTop,?0,?nestedScrollingChildTop,?monthHeigh);

}?else?{

autoScroll(monthCalendarTop,?-monthCalendarOffset,?nestedScrollingChildTop,?weekHeigh);

}

}?else?{??????????//nestedScrollingChild移動的超過周高度時才會滑動到月

if?(nestedScrollingChildTop?

autoScroll(monthCalendarTop,?-monthCalendarOffset,?nestedScrollingChildTop,?weekHeigh);

}?else?{

autoScroll(monthCalendarTop,?0,?nestedScrollingChildTop,?monthHeigh);

}

}

}

autoScroll方法://自動滑動

private?void?autoScroll(int?startMonth,?int?endMonth,?int?startChild,?int?endChild)?{

monthValueAnimator.setIntValues(startMonth,?endMonth);

monthValueAnimator.setDuration(duration);

monthValueAnimator.start();

nestedScrollingChildValueAnimator.setIntValues(startChild,?endChild);

nestedScrollingChildValueAnimator.setDuration(duration);

nestedScrollingChildValueAnimator.start();

}

ValueAnimator動畫的回調:@Override

public?void?onAnimationUpdate(ValueAnimator?animation)?{????????if?(animation?==?monthValueAnimator)?{????????????int?animatedValue?=?(int)?animation.getAnimatedValue();????????????int?top?=?monthCalendar.getTop();????????????int?i?=?animatedValue?-?top;

monthCalendar.offsetTopAndBottom(i);

}????????if?(animation?==?nestedScrollingChildValueAnimator)?{????????????int?animatedValue?=?(int)?animation.getAnimatedValue();????????????int?top?=?nestedScrollingChild.getTop();????????????int?i?=?animatedValue?-?top;

nestedScrollingChild.offsetTopAndBottom(i);

}

}

到此,交互的部分就結束了。

其他問題

寫完 以上這些,這個日歷算是基本完工,但是還是有不少bug的,其他的問題,就在寫的過程中解決的一些bug。

1、滑動過快的問題

快速滑動nestedScrollingChild時,有時會出現不友好的情況,這有兩個地方做了限制。

一個是NestedScrollingParent中的onNestedPreFling方法:@Override

public?boolean?onNestedPreFling(View?target,?float?velocityX,?float?velocityY)?{????????//防止快速滑動

int?nestedScrollingChildTop?=?nestedScrollingChild.getTop();????????if?(nestedScrollingChildTop?>?weekHeigh)?{????????????return?true;

}????????return?false;

}

上面的方法主要限制nestedScrollingChild的快速滑動。還有一個地方是月日歷的快速移動,是滑動到邊界的問題:private?int?getOffset(int?offset,?int?maxOffset)?{????????if?(offset?>?maxOffset)?{????????????return?maxOffset;

}????????return?offset;

}

這個方法是獲取偏移量的時候,如果得到的數值大于需要的最大值,則返回最大值,防止出現view越界的情況。

2、翻頁閃爍的問題?onLayout

這個問題,是當滑動到相應位置后,左右翻頁月日歷,會出現月日歷返回到原來移動之前的位置上,造成閃爍,并且此時位置也亂了。這時就需要在onLayout重新確定月日歷和nestedScrollingChild的位置,需要在每次操作之后執行requestLayout()方法:@Override

protected?void?onLayout(boolean?changed,?int?l,?int?t,?int?r,?int?b)?{????????//??super.onLayout(changed,?l,?t,?r,?b);

if?(STATE?==?MONTH)?{

monthCalendarTop?=?monthCalendar.getTop();

childViewTop?=?nestedScrollingChild.getTop()?==?0???monthHeigh?:?nestedScrollingChild.getTop();

}?else?{

monthCalendarTop?=?-getMonthCalendarOffset();

childViewTop?=?nestedScrollingChild.getTop()?==?0???weekHeigh?:?nestedScrollingChild.getTop();

}

monthCalendar.layout(0,?monthCalendarTop,?r,?monthHeigh?+?monthCalendarTop);

ViewGroup.LayoutParams?layoutParams?=?nestedScrollingChild.getLayoutParams();

nestedScrollingChild.layout(0,?childViewTop,?r,?layoutParams.height?+?childViewTop);

}

3、滑動到周日歷底部空白的問題?onMeasure

onMeasure的問題,當日歷滑動到周日歷的之后,NestedScrollingChild下方會出現空白,這個空白是由于NestedScrollingChild上移造成的,因為NestedScrollingChild高度一定,上移以后,下面沒有東西了自然就會留空。我們可以把NestedScrollingChild的高度變高,這樣上滑之后,之前沒有顯示的那部分就會顯示出來,就不會有空白了。觀察之后會發現,NestedScrollingChild移動到周日歷的位置后,整個View上面是周日歷,下面是NestedScrollingChild,所以我們可以把NestedScrollingChild的高度變成整個View的高度和周日歷高度之差,這樣就可以了:@Override

protected?void?onMeasure(int?widthMeasureSpec,?int?heightMeasureSpec)?{????????super.onMeasure(widthMeasureSpec,?heightMeasureSpec);

ViewGroup.LayoutParams?layoutParams?=?nestedScrollingChild.getLayoutParams();

layoutParams.height?=?getMeasuredHeight()?-?weekHeigh;

}

至此,這個日歷算是完成了。

總結

以上是生活随笔為你收集整理的android 日历动画效果,Android仿 MIUI日历的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。