自定义Behavior
Behavior簡(jiǎn)介
CoordinatorLayout是個(gè)很牛的布局容器,使用它可以很方面的實(shí)現(xiàn)很多效果,比如支付寶首頁(yè)的折疊效果,知乎首頁(yè)等等,Behavior是它的一個(gè)內(nèi)部抽象類(lèi),聲明Behavior屬性的View可以和他它依賴(lài)的兄弟元素交互,當(dāng)然,這些元素都必須是CoordinatorLayout的子元素。
what?
該類(lèi)是CoordinatorLayout子View之間交互的插件,有了它CoordinatorLayout的子View就可以產(chǎn)生聯(lián)動(dòng)效果。比如一個(gè)子View跟隨另一個(gè)子View的移動(dòng)而移動(dòng),或者是縮放,透明度變換等。
why?
一般情況下,如果要實(shí)現(xiàn)子View,隨著上拉消失,下拉出現(xiàn),我們需要在Activity中監(jiān)聽(tīng)滾動(dòng)事件,在里邊處理View的顯示邏輯,這樣就會(huì)導(dǎo)致Activity中代碼量變大,滾動(dòng)事件中的邏輯也會(huì)變的復(fù)雜,有了CoordinatorLayout.behavior,不需要監(jiān)聽(tīng)滾動(dòng)事件,只需要自定義一個(gè)Behavior的實(shí)現(xiàn)類(lèi),實(shí)現(xiàn)幾個(gè)方法就可以達(dá)到相應(yīng)的效果,不需要修改activity中的代碼。
how?
Behavior是CoordinatorLayout的一個(gè)內(nèi)部抽象類(lèi),里邊的方法有很多,如果不是要實(shí)現(xiàn)特別復(fù)雜的功能,只需要實(shí)現(xiàn)1,2個(gè)方法就可以了。常見(jiàn)的有兩種方式:
1. 如果要依賴(lài)于CoordinatorLayout中的某個(gè)子View實(shí)現(xiàn)某種變化,需要實(shí)現(xiàn)Behavior的layoutDependsOn()和onDependentViewChanged()方法,前者會(huì)在CoordinatorLayout中通過(guò)循環(huán)的方式,找到被依賴(lài)的View,返回true,表示找到了被依賴(lài)的View,后者在每次dependency的大小,位置變化的時(shí)候都會(huì)調(diào)用,可以在這里出child的變化邏輯。
/*** 在CoordinatorLayout中通過(guò)循環(huán)遍歷的方式找到被依賴(lài)的View** @param parent CoordinatorLayout* @param child 設(shè)置了該Behavior的View* @param dependency 被依賴(lài)的View* @return true 標(biāo)識(shí)找到了被依賴(lài)的View*/@Overridepublic boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {//...}/*** 每次dependency的大小或位置有所變化的時(shí)候都會(huì)調(diào)用該方法** @param parent* @param child* @param dependency* @return*/@Overridepublic boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {//...}2. 如果不需要依賴(lài)于任何子View,純粹只是想要根據(jù)滑動(dòng)距離,做一些操作,可以實(shí)現(xiàn)onStartNestScroll()和onNestPreScroll()。
onNestedPreScroll(CoordinatorLayout?coordinatorLayout, V child,?View?target, int dx, int dy, int[] consumed, int type)
Called when a nested scroll in progress is about to update, before the target has consumed any of the scrolled distance.
該方法是在target消費(fèi)調(diào)用任何的滾動(dòng)距離之前調(diào)用的。這個(gè)時(shí)候還沒(méi)有更新
onNestedScroll(CoordinatorLayout?coordinatorLayout, V child,?View?target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type)
Called when a nested scroll in progress has updated and the target has scrolled or attempted to scroll.
自定義Behavior的使用方式:
1. 在res/values文件夾下定義字符串引用,然后再布局文件中引用
2.直接在布局文件中使用全路徑包名
實(shí)例1:
實(shí)現(xiàn)上拉消失,下拉顯示的效果。這個(gè)效果不需要依賴(lài)任何的View,所以重寫(xiě)onStartNestScroll()和onNestPreScroll()即可,
在onStartNestScroll()中判斷View要位移的總距離,然后再onNestPreScroll()方法中進(jìn)行邊界判斷并位移即可。
具體代碼:
public class DownToHiddenBehavior extends CoordinatorLayout.Behavior<View> {private int mOffsetY = 0;public DownToHiddenBehavior() {super();}public DownToHiddenBehavior(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) child.getLayoutParams();int total = params.bottomMargin + child.getHeight(); // Log.i("onNestedPreScroll", "------>trans:" + child.getTranslationY() + " ,total:" + total);mOffsetY += dy;//dy<0:表示下拉,offsetY<0:表示下拉的距離已經(jīng)超出View的原始位置//一定要同時(shí)成立才行,否則先向上滑動(dòng),然后再快速向下滑動(dòng),View的位置會(huì)出現(xiàn)偏移。//dy<0的時(shí)候,moffsetY不一定小于0,相反也一樣if (dy < 0 && mOffsetY < 0) {mOffsetY = 0;}//邊界判斷if (dy > 0 && mOffsetY > total) {mOffsetY = total;}child.setTranslationY(mOffsetY);} }注意:這里的構(gòu)造方法一定要重載!!!不然會(huì)報(bào)錯(cuò)的。因?yàn)锽ehavior是在CoordinatorLayout中使用反射進(jìn)行調(diào)用的。需要用到兩參的構(gòu)造方法。
接下來(lái)在布局文件中引用即可
<android.support.design.widget.FloatingActionButtonapp:layout_behavior="@string/behavior_down_to_hidden"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="floatButton"android:layout_gravity="bottom|right"android:layout_marginBottom="50dp"android:layout_marginRight="50dp"/>
實(shí)例2:
實(shí)現(xiàn)View跟隨ToolBar的消失和顯示進(jìn)行縮放。向上滑動(dòng)的時(shí)候Toolbar逐漸移除屏幕,view逐漸縮小直到消失。下拉的時(shí)候,Toolbar逐漸出現(xiàn),View逐漸放大到原始大小。
很明顯,這里需要依賴(lài)于Toolbar,因此使用第一種方法,重寫(xiě)layoutDependsOn()和onDependentViewChanged()方法
具體代碼:
public class ScaleToHiddenBehavior extends CoordinatorLayout.Behavior<View> {private float mToolbarHegiht;public ScaleToHiddenBehavior(Context context, AttributeSet attrs) {super(context, attrs);}/*** 在CoordinatorLayout中通過(guò)循環(huán)遍歷的方式找到被依賴(lài)的View** @param parent CoordinatorLayout* @param child 設(shè)置了該Behavior的View* @param dependency 被依賴(lài)的View* @return true 標(biāo)識(shí)找到了被依賴(lài)的View*/@Overridepublic boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {if (mToolbarHegiht == 0) {mToolbarHegiht = dependency.getHeight();}return dependency instanceof AppBarLayout;}/*** 每次dependency的大小或位置有所變化的時(shí)候都會(huì)調(diào)用該方法** @param parent* @param child* @param dependency* @return*/@Overridepublic boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {Log.i("onDependentViewChanged", "------->height:" + mToolbarHegiht + ", top:" + dependency.getTop());float fraction = Math.abs(dependency.getTop()) / mToolbarHegiht;child.setScaleX(1 - fraction);child.setScaleY(1 - fraction);return super.onDependentViewChanged(parent, child, dependency);} }同樣在布局文件中引用該類(lèi)
<android.support.design.widget.FloatingActionButtonapp:layout_behavior="@string/behavior_scale_to_hidden"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="floatButton"android:layout_gravity="top|right"android:layout_marginTop="100dp"android:layout_marginRight="50dp"/>
實(shí)例3:
使用CoordinatorLayout實(shí)現(xiàn)支付寶首頁(yè)折疊效果 :
思路:
這里不需要自定義Behavior,但是需要監(jiān)聽(tīng)AppbarLayout的折疊距離,控制Toolbar的顯示效果。
整個(gè)Activity的布局文件activity_page_home_zfb.xml
頂部的搜索框Toolbar和需要折疊起來(lái)的布局全都放在CoolapsingToolbarLayout中,因?yàn)镃oolapsingToolbarLayout繼承自FrameLayout,想要讓Toolbar顯示在頂部,并且看起來(lái)不覆蓋折疊布局,這里設(shè)置了給需要折疊的布局設(shè)置了marginTop,值剛好就是Toolbar的高度。
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"xmlns:app="http://schemas.android.com/apk/res-auto"><android.support.design.widget.AppBarLayoutandroid:id="@+id/appbarLayout"android:layout_width="match_parent"android:layout_height="wrap_content"><android.support.design.widget.CollapsingToolbarLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"><includeandroid:id="@+id/collapsLayout"layout="@layout/include_layout_collaps"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="?attr/actionBarSize"android:orientation="horizontal"app:layout_collapseMode="parallax"app:layout_collapseParallaxMultiplier="0.7"/><android.support.v7.widget.Toolbarandroid:id="@+id/toolbarContainer"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"app:contentInsetLeft="0dp"app:contentInsetStart="0dp"app:layout_collapseMode="pin"><includelayout="@layout/include_layout_toolbar_1"android:id="@+id/toolbar1"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center_vertical"/><includeandroid:visibility="gone"layout="@layout/include_layout_toolbar_2"android:id="@+id/toolbar2"android:layout_width="match_parent"android:layout_height="match_parent"/></android.support.v7.widget.Toolbar></android.support.design.widget.CollapsingToolbarLayout></android.support.design.widget.AppBarLayout><android.support.v4.widget.NestedScrollViewandroid:id="@+id/nestScrollView"android:layout_width="match_parent"android:layout_height="wrap_content"app:layout_behavior="@string/appbar_scrolling_view_behavior"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="200dp"android:background="#bac"/><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="100dp"android:background="#abc"/><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="100dp"android:background="#cba"/><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="150dp"android:background="#bba"/><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="150dp"android:background="#aab"/><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="200dp"android:background="#ccb"/></LinearLayout></android.support.v4.widget.NestedScrollView> </android.support.design.widget.CoordinatorLayout>支付寶的效果是,剛進(jìn)入頁(yè)面折疊布局完全展開(kāi),并且Toolbar顯示的是搜索框。上拉時(shí)搜索框逐漸隱藏,然后Toolbar開(kāi)始逐漸顯示4個(gè)按鈕的布局,全程展開(kāi)的折疊布局逐漸折疊起來(lái),并且透明度逐漸降低。
這里參考了網(wǎng)上的思路,當(dāng)折疊到一半高度的時(shí)候開(kāi)始切換Toolbar的布局。
具體代碼如下:
public class ZFBHomePageActivity extends BaseActivity {private AppBarLayout mAppbarLayout;private LinearLayout mToolbar1,mToolbar2;private LinearLayout mCollapsLayout;@Overrideprotected void initView() {mAppbarLayout=findViewById(R.id.appbarLayout);mToolbar1=findViewById(R.id.toolbar1);mToolbar2=findViewById(R.id.toolbar2);mCollapsLayout=findViewById(R.id.collapsLayout);mAppbarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {@Overridepublic void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {float value=Math.abs(verticalOffset);int total=mAppbarLayout.getTotalScrollRange();int midHeight=total/2;if (value<=midHeight){//toolbar1的alpha值從1-0float fraction = value / midHeight * 1.0f;mToolbar1.setVisibility(View.VISIBLE);mToolbar2.setVisibility(View.GONE);mToolbar1.setAlpha(1-fraction);}else {//toolbar2的alpha值從0-1float fraction=(value-midHeight)/(total-midHeight)*1.0f;mToolbar1.setVisibility(View.GONE);mToolbar2.setVisibility(View.VISIBLE);mToolbar2.setAlpha(fraction);Log.i("onOffsetChanged","---->value:"+value+", fraction:"+fraction+(mToolbar2.getVisibility()==View.VISIBLE));}//折疊的布局alpha值從1-0mCollapsLayout.setAlpha(1-(value/total*1.0f));}});} 非常簡(jiǎn)單,只需要在給AppbarLayout注冊(cè)addOffsetChangedListener()監(jiān)聽(tīng),根據(jù)折疊的偏移量,在偏移量小于總偏移量的1/2時(shí),顯示搜索布局,隱藏四個(gè)按鈕,并降低搜索布局的透明度;當(dāng)偏移量大于一半時(shí),隱藏搜索布局,逐漸顯示4個(gè)按鈕。折疊布局的透明度,要開(kāi)始折疊,一直到完全折疊起來(lái)逐漸降低。總結(jié)
以上是生活随笔為你收集整理的自定义Behavior的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 熊的困惑
- 下一篇: 如何将多个PDF选择页面后再进行合并