Android:自定義CoordinatorLayout.behavior 簡單的仿UC首頁

      CoordinatorLayout顧名思義協調佈局,是用來協調該佈局下的子控件,最簡單地使用就是頭部伸縮和摺疊了,配合着TabLayout,只需要設置一下AppBarLayout子控件的layout_scrollFlags以及底下滑動控件的layout_behavior就行了,組合起來效果非常不錯,網上這種詳細的教程有很多,這片文章主要講述一下簡單的自定義behavior。

     在behavior中可以設置觸摸事件攔截onInterceptTouchEvent(),觸摸事件響應onTouchEvent(),View依賴另一個ViewlayoutDependsOn()、onDependentViewChanged(),嵌套滑動事件處理onStartNestedScroll()、onStopNestedScroll()、onNestedScroll()、onNestedPreScroll()以及還有兩個快速滑動的方法,

       這篇文章使用的是behavior的依賴方法,ViewlayoutDependsOn()和onDependentViewChanged()。

    ViewlayoutDependsOn(CoordinatorLayout parent, View child, View dependency):child是使用這個behavior的view,dependency則是child所依賴的view。何爲依賴?在我看來,就是dependency改變的時候,child能根據dependency的變化,做出反應,如何反應?當 ViewlayoutDependsOn 返回true的時候就可以觸發 onDependentViewChanged。

       反應動作寫在onDependentViewChanged(CoordinatorLayout parent, View child, View dependency),比方說,可以通過dependency.getX  dependency.getY獲取的座標,設置給child,該View就會跟着依賴View動起來,或者讓它反方向動,都可以,非常靈活,最後return true就會生效。

那麼來嘗試一下吧,UC中首頁漂亮的交互動畫非常的吸引人,自己也想學着做一個簡單的頁面。單獨看這個頁面的話,有三塊佈局,一個是底下的RecyclerView,一個是逐漸被遮住的導航欄,最後一個是在RecyclerView逐漸上移時候出現的UC頭條和TabLayout。如下圖(視頻用UC轉gif就變得好糊,好在還能看出動畫):

      在我看來,實現方法有兩個:1.通過監聽RecyclerView的觸摸,然後判斷是否讓他滑動,以及他在位置改變的時候,TabLayout跟着變動;2.那就是今天的主題了,自定義behavior。第一種方法以前寫的時候嘗試過,比較難的在於,滑動過程中RecyclerView高度和位置的變化,它並不是簡單的上移,單純上移的話,底下會缺一塊;再來就是觸摸事件的分發和滑動的攔截了,以前寫的時候失敗了,bug很嚴重,對於事件分發的確不是很紮實,還得多學學。採用第二種,其實也會碰到這些難點,但是今天在於簡單的實踐,最簡單地使用CoordinatorLayout。相信大家都有所瞭解,在寫了AppBarLayout,滑動佈局添加

app:layout_behavior="@string/appbar_scrolling_view_behavior"

      滑動佈局高度和滑動處理,就解決了,谷歌已經幫我們實現了。我們只需要把UC頭條和tablayout依賴RecyclerView,根據RecyclerView的座標做出改變就行了。下面給出代碼,先是自定義Behavior,我使用了NestedScrollView代替RecyclerView,插數據比較好插,要依賴RecyclerView的自己替代一下:

public class MyBehavior extends CoordinatorLayout.Behavior<View> {
    private float allDistance = 0; // Head從和RecyclerView同樣位置,移到頂部的距離
    private float dependencyDistance = 0; //RecyclerView需要移動的距離
    public MyBehavior() {
    }

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

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return dependency instanceof NestedScrollView;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        if (allDistance == 0) {
            allDistance = dependency.getY();//獲取child要移動的總距離,是dependency到頂部的距離
            Log.d("allDistance", String.valueOf(allDistance));
            dependencyDistance = allDistance - child.getHeight();//獲取滑動控件所要移動的總距離,移動到childView下方
            Log.d("recyclerDistance", String.valueOf(dependencyDistance));
            child.setY(allDistance);//初始化child的位置,和dependency位置一樣,不然child會在頂部
        } else {
            float distance = (allDistance - dependency.getY()) / dependencyDistance * allDistance;//child所需要移動的距離!
            child.setY(allDistance - distance <= 0 ? 0 : allDistance - distance);//通過距離,計算出child的座標,最後爲0
        }

        return true;
    }

}

這個behavior比較簡單,根據dependency的Y座標,計算出child的Y座標。接下來是佈局文件,比較重要:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.env.cloud.showview.ui.CoordinatorActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#00ffffff">

        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:minHeight="150dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="250dp"
                android:gravity="center"
                android:text="導航欄"
                android:textColor="#000"
                android:textSize="25sp"
                app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="2.7" />

        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:background="#fff000"
        android:orientation="vertical"
        app:layout_behavior="com.env.cloud.showview.views.behavior.MyBehavior">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="80dp"
            android:gravity="center"
            android:text="UC頭條和搜索框"
            android:textSize="25sp" />

        <android.support.design.widget.TabLayout
            android:id="@+id/tab_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <android.support.design.widget.TabItem
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:text="關注" />

            <android.support.design.widget.TabItem
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:text="推薦" />
        </android.support.design.widget.TabLayout>

    </LinearLayout>
    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:gravity="center"
                android:text="內容1"
                android:textSize="20sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:gravity="center"
                android:text="內容2"
                android:textSize="20sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:gravity="center"
                android:text="內容3"
                android:textSize="20sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:gravity="center"
                android:text="內容4"
                android:textSize="20sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:gravity="center"
                android:text="內容5"
                android:textSize="20sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:gravity="center"
                android:text="內容6"
                android:textSize="20sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:gravity="center"
                android:text="內容"
                android:textSize="20sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:gravity="center"
                android:text="內容7"
                android:textSize="20sp" />
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>

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

這邊有幾個點,需要說一下,一個是AppBarLayout設置了一個background爲全透明,原因是因爲,在摺疊完成後,AppBarLayout本來應該在LinearLayout下方,它會跑上來,變成頂層,不設置透明的話,自帶的顏色會遮住LinearLayout。LinearLayout中的點擊事件應該是不會和CollapsingToolBarLayout衝突的,我沒有嘗試,需要的小夥伴可以試一下。

然後就是在CollapsingToolbarLayout中設置,設置layout_scrollFlags爲scroll|exitUntilCollapsed,snap可選可不選,作用是回彈效果,、android:minHeight="150dp"來控制滑動View能滑動到的距離,這個高度應該和TabLayout+Uc導航欄和搜索框所在的LinearLayout高度一致,這樣的話,滑動的View就剛好能在LinearLayout下方。

設置CollapsingToolBarLayout的子控件的摺疊:

app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="2.7"

重點在於layout_collapseParallaxMultiplier,網上叫做視覺差係數,就是摺疊快慢的控制,之所以設置成這麼大,是由於,不夠大的情況下,CollapsingToolBarLayout沒有收縮完全,裏面控件會到上層顯示,把LinearLayout遮住,設置大一些的話,會完全摺疊,這就是我覺得點擊事件不會衝突的原因,因爲完全消失了,並不在LinearLayout所在的區域,自然就不會有點擊衝突。

 

關鍵點:摺疊佈局CollapsingToolbarLayout的最小高度,要和滑動出現的LinearLayout高度一致,這是爲了控制滑動控件,比如RecyclerView,NestedScrollView的滑動距離;設置layout_collapseParallaxMultiplier係數大一點,可以自己設置小一點看看效果,上面的佈局文件的話,導航欄三個字就會出現在LinearLayout所在的區域,下面上效果圖:

額,大致是這樣,後面嘗試不使用AppBarLayout,再試試吧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章