一個神奇的控件——Android CoordinatorLayout與Behavior使用指南

squtxtfzt 8年前發布 | 23K 次閱讀 安卓開發 Android開發 移動開發

介紹

CoordinatorLayout是用來協調其子view們之間動作的一個父view,而Behavior就是用來給CoordinatorLayout的子view們實現交互的。

SUM

1. CollapsingToolbarLayout_伸縮折疊工具

a. CollapsingToolbarLayout折疊或展開時,FloatingActionButton跟隨運動并且大小相應變化. CollapsingToolbarLayout是專門用來實現子布局內不同元素響應滾動細節的布局。

b. AppBarLayout是一種支持響應滾動手勢的app bar布局(比如工具欄滾出或滾入屏幕);與AppBarLayout組合的滾動布局(Recyclerview、NestedScrollView等)需要設置app:layout_behavior="@string/appbar_scrolling_view_behavior"(上面代碼中NestedScrollView控件所設置的)。沒有設置的話,AppBarLayout將不會響應滾動布局的滾動事件。。

c. CollapsingToolbarLayout和ScrollView一起使用會有滑動bug,注意要使用NestedScrollView來替代ScrollView。

Android studio中有一個Activity模板叫ScrollingActivity,它實現的就是簡單的可折疊工具欄。

ScrollingActivity的布局代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="

<android.support.design.widget.AppBarLayout
    android:id="@+id/app_bar"
    android:fitsSystemWindows="true"
    android:layout_height="180dp"
    android:layout_width="match_parent"
    android:theme="@style/AppTheme.AppBarOverlay">

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/toolbar_layout"
        android:fitsSystemWindows="true"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_scrollFlags="scroll|exitUntilCollapsed"
        app:contentScrim="?attr/colorPrimary">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_height="?attr/actionBarSize"
            android:layout_width="match_parent"
            app:layout_collapseMode="pin"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<!--可以include 抽取出來-->
<android.support.v4.widget.NestedScrollView                    
        android:layout_width="match_parent"        
        android:layout_height="match_parent"              
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        >    
    <TextView        
            android:layout_width="wrap_content"        
            android:layout_height="wrap_content"        
            android:layout_margin="@dimen/text_margin"         
            android:text="@string/large_text" />     
</android.support.v4.widget.NestedScrollView>

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="@dimen/fab_margin"
    app:layout_anchor="@id/app_bar"
    app:layout_anchorGravity="bottom|end"
    android:src="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout></code></pre>

2.AppBarLayout的子布局有5種滾動標識

就是上面代碼CollapsingToolbarLayout中配置的app:layout_scrollFlags屬性:

  1. scroll:將此布局和滾動時間關聯。這個標識要設置在其他標識之前,沒有這個標識則布局不會滾動且其他標識設置無效。
  2. enterAlways:任何向下滾動操作都會使此布局可見。這個標識通常被稱為“快速返回”模式。
  3. enterAlwaysCollapsed:假設你定義了一個最小高度(minHeight)同時enterAlways也定義了,那么view將在到達這個最小高度的時候開始顯示,并且從這個時候開始慢慢展開,當滾動到頂部的時候展開完。
  4. exitUntilCollapsed:當你定義了一個minHeight,此布局將在滾動到達這個最小高度的時候折疊。
  5. snap:當一個滾動事件結束,如果視圖是部分可見的,那么它將被滾動到收縮或展開。例如,如果視圖只有底部25%顯示,它將折疊。相反,如果它的底部75%可見,那么它將完全展開。

3.CollapsingToolbarLayout的contentScrim、statusBarScrim 屬性。

  • app:contentScrim設置折疊時工具欄布局的顏色
  • app:statusBarScrim設置折疊時狀態欄的顏色。
    默認contentScrim是colorPrimary的色值,statusBarScrim是colorPrimaryDark的色值。這個后面會用到。

4.CollapsingToolbarLayout子布局設置折疊模式,app:layout_collapseMode**

  • off:這個是默認屬性,布局將正常顯示,沒有折疊的行為。
  • pin:CollapsingToolbarLayout折疊后,此布局將固定在頂部。
  • parallax:CollapsingToolbarLayout折疊時,此布局也會有視差折疊效果

當CollapsingToolbarLayout的子布局設置了parallax模式時,我們還可以通過

app:layout_collapseParallaxMultiplier

設置視差滾動因子,值為:0~1。

5.FloatingActionButton

FloatingActionButton這個控件通過app:layout_anchor這個設置錨定在了AppBarLayout下方。FloatingActionButton源碼中有一個Behavior方法,當AppBarLayout收縮時,FloatingActionButton就會跟著做出相應變化。關于CoordinatorLayout和Behavior,我下一篇文章會和大家一起學習。

B站很早就開源了一個彈幕引擎,還起了個狂拽酷炫吊炸天的名字叫“烈焰彈幕使 ”(一看就是二次元程序猿們的作品→_→),源碼在github上,項目名叫 DanmakuFlameMaster 。

2. 自定義CoordinatorLayout的Behavior

自定義Behavior模仿知乎

1.先看下布局

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="

<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/AppTheme.AppBarOverlay">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:layout_scrollFlags="scroll|enterAlways|snap"
        android:background="?attr/colorPrimary" />
</android.support.design.widget.AppBarLayout>

<android.support.v4.widget.SwipeRefreshLayout
    android:id="@+id/behavior_demo_swipe_refresh"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/behavior_demo_recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
</android.support.v4.widget.SwipeRefreshLayout>

<!--行為,依賴于自定義Beavior-->
<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginRight="16dp"
    android:layout_marginBottom="72dp"
    android:src="@android:drawable/ic_dialog_email"
    app:layout_behavior="com.example.zcp.coordinatorlayoutdemo.behavior.MyFabBehavior"
    android:layout_gravity="bottom|right" />

<!--行為,依賴于自定義Beavior-->
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:layout_gravity="bottom"
    android:background="@color/colorPrimary"
    android:gravity="center"
    app:layout_behavior="com.example.zcp.coordinatorlayoutdemo.behavior.MyBottomBarBehavior">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textColor="#ffffff"
        android:text="這是一個底欄"/>
</LinearLayout>

</android.support.design.widget.CoordinatorLayout></code></pre>

SwipeRefreshLayout、FloatingActionButton和當做底欄的LinearLayout上有一個app:layout_behavior配置。

SwipeRefreshLayout配置的"@string/appbar_scrolling_view_behavior"是系統提供的,用來使滑動控件與AppBarLayout互動。

FloatingActionButton和底欄上配置的是我們接下來要自定義的Behavior。

先看FloatingActionButton的Behavior。

public class MyFabBehavior extends CoordinatorLayout.Behavior<View> {

private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();

private float viewY;//控件距離coordinatorLayout底部距離
private boolean isAnimate;//動畫是否在進行

public MyFabBehavior(Context context, AttributeSet attrs) {
    super(context, attrs);
}

@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {

    if (child.getVisibility() == View.VISIBLE && viewY == 0) {
        //獲取控件距離父布局(coordinatorLayout)底部距離
        viewY = coordinatorLayout.getHeight() - child.getY();
    }

    return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;//判斷是否豎直滾動
}

@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
    //大于0是向上滾動 小于0是向下滾動

    if (dy >= 0 && !isAnimate && child.getVisibility() == View.VISIBLE) {
        hide(child);
    } else if (dy < 0 && !isAnimate && child.getVisibility() == View.GONE) {
        show(child);
    }
}

//隱藏時的動畫
private void hide(final View view) {
    ViewPropertyAnimator animator = view.animate().translationY(viewY).setInterpolator(INTERPOLATOR).setDuration(200);

    animator.setListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animator) {
            isAnimate = true;
        }

        @Override
        public void onAnimationEnd(Animator animator) {
            view.setVisibility(View.GONE);
            isAnimate = false;
        }

        @Override
        public void onAnimationCancel(Animator animator) {
            show(view);
        }

        @Override
        public void onAnimationRepeat(Animator animator) {

        }
    });
    animator.start();
}

//顯示時的動畫
private void show(final View view) {
    ViewPropertyAnimator animator = view.animate().translationY(0).setInterpolator(INTERPOLATOR).setDuration(200);
    animator.setListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animator) {
            view.setVisibility(View.VISIBLE);
            isAnimate = true;
        }

        @Override
        public void onAnimationEnd(Animator animator) {
            isAnimate = false;
        }

        @Override
        public void onAnimationCancel(Animator animator) {
            hide(view);
        }

        @Override
        public void onAnimationRepeat(Animator animator) {

        }
    });
    animator.start();
}

}</code></pre>

邏輯并不復雜,我們通過重寫Behavior中關于嵌套滑動的兩個回調完成了FloatingActionButton的隱藏和顯示判斷及操作。

單獨出場的底欄也可以利用上面一樣的方法來設置隱藏或顯示,我的底欄是和AppBarLayout一起出場,所以我就讓底欄從屬于AppBarLayout活動。代碼如下:

public class MyBottomBarBehavior extends CoordinatorLayout.Behavior<View> {

public MyBottomBarBehavior(Context context, AttributeSet attrs) {
    super(context, attrs);
}

@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
    //這個方法是說明這個子控件是依賴AppBarLayout的
    return dependency instanceof AppBarLayout;
}


@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {

    float translationY = Math.abs(dependency.getTop());//獲取更隨布局的頂部位置
    child.setTranslationY(translationY);
    return true;
}

}</code></pre>

代碼量比上個還少。我們還可以通過重寫onMeasureChild()來使控件響應從屬控件的大小變化。

Behavior提供的很多,我這里用到的只是一部分,大家可以看看源碼,根據具體需求去使用。

3 .CollapsingToolbarLayout與TabLayout結合

CollapsingToolbarLayout與TabLayout組合使用的效果也不錯。

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="

<android.support.design.widget.AppBarLayout
    android:id="@+id/app_bar"
    android:layout_width="match_parent"
    android:layout_height="250dp"
    android:fitsSystemWindows="true"
    android:theme="@style/AppTheme.AppBarOverlay">

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/toolbar_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:titleEnabled="false"
        android:fitsSystemWindows="true"
        app:contentScrim="@color/colorPrimary"
        app:statusBarScrim="@android:color/transparent"
        app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
        <ImageView
            android:id="@+id/imageview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerCrop"
            android:adjustViewBounds="true"
            app:layout_collapseMode="parallax"
            app:layout_collapseParallaxMultiplier="0.7"
            android:fitsSystemWindows="true"
            android:src="@drawable/girl2"/>
        <View
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:background="@drawable/gradient"
            android:fitsSystemWindows="true" />
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="96dp"
            android:minHeight="?attr/actionBarSize"
            android:gravity="top"
            app:layout_collapseMode="pin"
            app:title="hello"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            app:titleMarginTop="15dp"
            />
        <android.support.design.widget.TabLayout
            android:id="@+id/tablayout"
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:layout_gravity="bottom" />
    </android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>


<android.support.v4.view.ViewPager
    android:id="@+id/viewpage"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">
</android.support.v4.view.ViewPager>

</android.support.design.widget.CoordinatorLayout></code></pre>

2. AppBarLayout

  1. SwipeRefreshLayout配置的"@string/appbar_scrolling_view_behavior"是系統提供的,用來使滑動控件與AppBarLayout互動。

 

來自:http://www.jianshu.com/p/4bb9ee0e0177

 

 本文由用戶 squtxtfzt 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!