Material Design Patterns 教學 (3) - Snackbar & CoordinatorLayout

相信大家有用過 ToastToast是 Android App 用來顯示簡短提示或通知的一種方法。Material Design 引入 Snackbar ,用來取代 Toast。比起 ToastSnackbar 歸屬於顯示中的 view,可以加「行動」按鍵,並且可以以左去右形式掃走。

Snackbar 用法

Snackbar 使用方法是跟 Toast 一樣:

Snackbar.make(contentView, "I am snackbar", Snackbar.LENGTH_SHORT).show();

Toast 不同的是,Snackbar 是以 view 作參數,而不是以 context。這確保 Snackbar 只在有 view 顯示時才出現,不會跟 Toast 般被濫用,在背景 Service 中執行 toast.show(),引致用戶跟本不知是那個 app 在顯示通知的問題。

要加「行動」按鍵的話可以用 setAction()

Snackbar.make(contentView, "I am snackbar", Snackbar.LENGTH_SHORT)
        .setAction("Undo", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               // action triggered
            }
        })
        .show();
![Snackbar 連「行動」鍵,但擋著 FAB 了](/content/images/2015/09/snackbar.jpg)
Snackbar 連「行動」鍵,但擋著 FAB 了

但有沒有發覺 Snackbar 會擋著 FAB ?

移動吧!FAB

這時便要使用 Design Support Library 另一猛將: CoordinatorLayout

CoordinatorLayout 是一個 FrameLayout 的強化版。透過定義 Behavior,它能協調自身各 child view 間的互動。由於 Google 的 FAB 已內建支援 CoordinatorLayout,我們只需將包著 contentViewFrameLayout 改為 android.support.design.widget.CoordinatorLayout便可解決問題。

現在 Snackbar 出現時 FAB 會跟著動了。

![](/content/images/2015/09/Snackbar_FAB.gif)
Design Library 中的 FAB 內建支援

簡單吧?但若你選用之前介紹的 open source FAB 便沒有效果。

為其他 FAB library 加上 Behavior

要自行加上其實也不難。首先新建一個 class,要 extend Behavior

public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior<FloatingActionButton>
{
}

因為要在 xml 中使用,所以需新增以下 constructor:

public FloatingActionButtonBehavior(Context context, AttributeSet attributeSet){}

我們需要使用當中兩個 methods:layoutDependsOn()onDependentViewChanged()。看看 document 便知它們的用處。

layoutDependsOn(CoordinatorLayout parent, V child, View dependency)

Determine whether the supplied child view has another specific sibling view as a layout dependency.

onDependentViewChanged(CoordinatorLayout parent, V child, View dependency)

Respond to a change in a child's dependent view
This method is called whenever a dependent view changes in size or position outside of the standard layout flow.

先 override layoutDependsOn()。因我們想留意 Snackbar 的動向,所以當 dependencySnackBarLayout 的話便 return true;

@Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
    return dependency instanceof Snackbar.SnackbarLayout;
}

onDependentViewChanged() 是實際控制 FAB 移動的 method。我們想 FAB 跟隨 Snackbar 向上移動,所以用 setTranslationY()

@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
    float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
    child.setTranslationY(translationY);
    return true;
}

這樣 FloatingActionButtonBehavior 便完成。

最後在 FAB 加上 app:layout_behavior 即可

<com.github.clans.fab.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|right"
    android:layout_marginBottom="8dp"
    android:layout_marginRight="8dp"
    android:src="@drawable/ic_add_circle_outline_white"
    app:layout_behavior="com.thirtysparks.tutorial.designlib.FloatingActionButtonBehavior"
    />
![](/content/images/2015/09/Snackbar_with_opensource_FAB.gif)
Open Source FAB 也跟著移動

結語

Snackbar 為新的 Toast ,建議大家在 Material Design 中儘量不要再用 Toast 了。CoordinatorLayout 則可方便地控制各元件間的互動行為。下次我們會看看如何使用 CoordinatorLayout 來隱藏和顯示Toolbar

https://github.com/goofyz/android-material-design-tutorial/tree/part3_snackbar_coordinatorlayout

相關連結