底部Tab+FrameLayout巢狀CoordinatorLayout+Viewpager切換時佈局錯亂
頂部伸縮效果
現在要實現這麼一個效果,有一個標題欄(或者其他控制元件),下面是tablayout+viewpager,然後在viewpager裡面的內容滑動的時候tab懸浮,有點像微博熱搜的介面
向下滑動之前

向下滑動之前
向下滑動之後

向下滑動之後
CoordinatorLayout實現頭部佈局滑動時隱藏
在5.0之後,Google推出了一系列md控制元件,其中用的最多的就是CoordinatorLayout,它可以實現很酷炫的粘連效果,伸縮控制元件,直接上程式碼:
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/en_title_bg" app:layout_behavior="包名.FlingBehavior" android:fitsSystemWindows="true"> <android.support.design.widget.CollapsingToolbarLayout android:layout_width="match_parent" android:layout_height="match_parent" app:layout_scrollFlags="scroll|exitUntilCollapsed" app:titleEnabled="false"> <include layout="@layout/fragment_title_common" /> </android.support.design.widget.CollapsingToolbarLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.flyco.tablayout.SlidingTabLayout android:id="@+id/tabLayout" style="@style/frame_vp_tab" android:layout_width="match_parent" android:layout_height="32dp" android:layout_marginBottom="@dimen/dp_10" android:layout_marginTop="18dp"></com.flyco.tablayout.SlidingTabLayout> </LinearLayout> </android.support.design.widget.AppBarLayout> <android.support.v4.view.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"></android.support.v4.view.ViewPager> </android.support.design.widget.CoordinatorLayout>
這個程式碼應該一看就懂,首先需要用 AppBarLayout 來包住整個頭部佈局(包含需要伸縮和懸浮的控制元件)。 CollapsingToolbarLayout 中是需要滑動時伸縮的佈局,它的外面是需要懸浮在頂部不動的佈局。
佈局外再加一個Tab切換
如果剛才我們做的那個介面只是一個介面,然後需要在底部在加一個tab來切換呢。佈局如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <FrameLayout android:id="@+id/content" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <RadioGroup android:background="@drawable/tab_bottom_bg" android:id="@+id/radioGroup" android:layout_width="match_parent" android:layout_height="60dp" android:orientation="horizontal"> <RadioButton android:drawableTop="@drawable/icon_tab_en" android:id="@+id/tab_en" android:checked="true" style="@style/frame_tab_style" android:text="RadioButton1" /> <RadioButton android:drawableTop="@drawable/icon_tab_chinese" android:id="@+id/tab_chinese" style="@style/frame_tab_style" android:text="RadioButton2" /> <RadioButton android:drawableTop="@drawable/icon_tab_math" android:id="@+id/tab_math" style="@style/frame_tab_style" android:text="RadioButton3" /> </RadioGroup> </LinearLayout>
在FrameLayout中顯示之前寫的 CoordinatorLayout+Viewpager
需要注意的問題來了
一、底部Tab對應的子fragment
在這種情況下,底部tab對應的fragment的佈局都要用 CoordinatorLayout作為根佈局
二、子fragment的viewpager的fragment的佈局
聽起來有點繞哈,其實也就是每個tab頁面對應的子頁面的內容。內容佈局必須用支援滑動巢狀的控制元件,如 RecyclerView , NestedScrollView (要作為根佈局)
三、如果CoordinatorLayout被巢狀在其它Layout(這個就是坑)
需要去掉
android:fitsSystemWindows="true"
如果你按照我的程式碼直接複製過去,對這個引數懵懵懂懂,那你執行之後會發現,有的介面是正常的,但是有的上面懸浮的tab的marginTop失效了,而且點選下面的tab切換時,佈局會跳動,錯亂。
這一切都源於這個fitsSystemWindows。
解決滑動時不流暢的問題
appBarLayout沒有獲得fling指令導致卡頓,應該是recyclerView在分發fling的時候出現了錯誤。
新增一個檔案,命名 FlingBehavior
public final class FlingBehavior extends AppBarLayout.Behavior { private static final int TOP_CHILD_FLING_THRESHOLD = 3; private boolean isPositive; public FlingBehavior() { } public FlingBehavior(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) { if (velocityY > 0 && !isPositive || velocityY < 0 && isPositive) { velocityY = velocityY * -1; } if (target instanceof RecyclerView && velocityY < 0) { final RecyclerView recyclerView = (RecyclerView) target; final View firstChild = recyclerView.getChildAt(0); final int childAdapterPosition = recyclerView.getChildAdapterPosition(firstChild); consumed = childAdapterPosition > TOP_CHILD_FLING_THRESHOLD; } return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed); } @Override public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed) { super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed); isPositive = dy > 0; } }
將這個檔案定義在 AppBarLayout 的 app:layout_behavior 標籤中
總結
CoordinatorLayout雖然好用,但是遇到問題時卻覺得很莫名其妙。一個是如果要用CoordinatorLayout,那跟它平行的介面也要用CoordinatorLayout作為根佈局。還有,就是要注意 fitsSystemWindows 帶來的影響。