Material Design Patterns 教學 (5) - AppBarLayout

上次用過 CoordinatorLayout 控制 FAB 在 Snackbar 出現時移動,今次我們玩 AppBar

AppBar 最初叫 ActionBar,後來改名為 Toolbar ,現在統稱叫 AppBarAppBarLayout 即是控制內容元件滑動時 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 可以是 pinparallax,分別是在 CollapsingToolbarLayout 隱藏時會維持原狀和以 parallax 動畫隱藏。

好,來點例子說明。

CollapsingToolbarLayout 包著 ToolBar,放進 AppBarLayout 中。然後將 ImageViewToolbarapp:layout_collapseMode 分別設為 parallaxpin

<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_anchorlayout 去擺放,所以以上例子中的 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

相關連結