Material Design Patterns 教學 (5) - AppBarLayout
上次用過 CoordinatorLayout 控制 FAB 在 Snackbar 出現時移動,今次我們玩 AppBar。
AppBar 最初叫 ActionBar,後來改名為 Toolbar ,現在統稱叫 AppBar。AppBarLayout 即是控制內容元件滑動時 AppBar 的顯示,需要在 CoordinatorLayout 底下才能運作。
AppBarLayout
簡單的用 CoordinatorLayout 再加上 AppBarLayout 包着 Toolbar:
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
</android.support.design.widget.AppBarLayout>
然後在 RecyclerView 加上 app:layout_behavior="@string/appbar_scrolling_view_behavior",並在 Toolbar 加上 app:layout_scrollFlags="scroll|enterAlways"。這樣 CoordinatorLayout 便會在 RecyclerView 捲動時,去找自己當中受影響的 view ,在這裏即是 AppBarLayout,作用相關的反應。
整個 layout 如下:
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.thirtysparks.tutorial.designlib.AnotherFabActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_scrollFlags="scroll|enterAlways"
/>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
這樣便可輕易做到滑動 RecyclerView 時,自動顯示和隱藏 ToolBar。

隱藏選項
在 AppBarLayout 加上的 app:layout_scrollFlags 可以是:
-
enterAlways: scrolling view 任何向下的滑動,都會令AppBarLayout重新顯示,就像上面的例子。 -
enterAlwaysCollapsed: 要RecyclerView回到最頂才顯示

exitUntilCollapsed:RecyclerView向上滑動時,AppBarLayout會隱藏到minHeight的高度,當向下滑動到最頂時才再顯示全部。

snap(v23.1): 當RecyclerView滑動到將view顯示 75% 而停止時,會自動顯示整個view。不會再有一半隱藏一半顯示的情況。如app:layout_scrollFlags="scroll|snap|exitUntilCollapsed"的話,便有此效果:

CollapsingToolbarLayout
Support Design Library 還提供 CollapsingToolbarLayout,它只可是 AppBarLayout 中唯一的 child ,而它的 child view 可加上 layout_collapseMode 去決定 CollapsingToolBarLayout 被隱藏時他們自身的顯示情況。最常見的用法是在滑動時將一個大圖和文字的 AppBar 縮為純文字。

layout_collapseMode 可以是 pin 或 parallax,分別是在 CollapsingToolbarLayout 隱藏時會維持原狀和以 parallax 動畫隱藏。
好,來點例子說明。
用 CollapsingToolbarLayout 包著 ToolBar,放進 AppBarLayout 中。然後將 ImageView 和 Toolbar 的 app:layout_collapseMode 分別設為 parallax 和 pin
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
>
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|snap|exitUntilCollapsed"
app:contentScrim="#ff4444"
app:expandedTitleMarginStart="4dp"
app:expandedTitleMarginEnd="8dp"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="240dp"
android:src="@drawable/cat"
android:scaleType="centerCrop"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax"
/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
便可以做到此效果。
Floating Action Button
用之前的方法加進 FAB,同時加進 layout_anchor 指向 AppBarLayout
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:src="@drawable/ic_add_circle_outline_white"
android:elevation="6dp"
app:layout_anchor="@id/appbar_layout"
app:layout_anchorGravity="bottom|right|end"
app:pressedTranslationZ="12dp" />
這樣 AppBarLayout 隱藏的同時,FAB 也會跟著消失。簡單方便。

不過有一個問題,就是用 layout_anchor 的話,FAB 的位置會跟著 layout_anchor 的 layout 去擺放,所以以上例子中的 FAB 會永遠在 AppBarLayout 附近,而不能將它移到螢幕的底部。
要將它移回底部,只能放棄使用 layout_anchor,改回使用 CoordinatorLayout.Behavior (大家還記得在 Part 3 時用過吧?)。
public class ScrollingFabBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
private int toolbarHeight;
public ScrollingFabBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
this.toolbarHeight = Utils.getToolbarHeight(context);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton fab, View dependency) {
return dependency instanceof AppBarLayout;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton fab, View dependency) {
if (dependency instanceof AppBarLayout) {
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
int fabBottomMargin = lp.bottomMargin;
int distanceToScroll = fab.getHeight() + fabBottomMargin;
float ratio = (float)dependency.getY()/(float)toolbarHeight;
fab.setTranslationY(-distanceToScroll * ratio);
}
return true;
}
}
(註:Copied 自 Michał Z)

結語
以前要使用 OnScrollListener 去設定 Toolbar 的顯示,現在透過 CoordinatorLayout 加上 AppBarLayout 便可簡單做到,非常方便。快為你的 app 加上此漂亮效果吧。
https://github.com/goofyz/android-material-design-tutorial/tree/part5_appbarlayout