Sunday, December 6, 2015

讓你的view跟著CoordinatorLayout舞動

文章攢寫時間︰2015/12/06 18:37
文章更新時間︰2016/01/17 16:53
文章修改次數︰2

本篇參考來源
1. INTRODUCTION TO COORDINATOR LAYOUT ON ANDROID

一、前言

藉由前一篇的教學,
了解到官方已經用很簡單的方式就讓我們做到頁面裡元件間彼此互動。
但是,
如果在前一篇的教學裡,
我們用的不是官方的Floating Action Button(以下簡稱FAB),而是第3方的。
那麼我們又該如何實作呢?

二、文章開始
首先,
我們在引用Library時,
改由Maven從repositories(雲端倉庫)下載

在您的app專案底下、build.gradle檔裡寫下該行︰
compile 'com.getbase:floatingactionbutton:1.9.1'

此時,
就能在專案中引用第3方套件的Floating Action Buton了。 

接著,
我們將剛才的專案裡,
原本使用官方的FAB佈局改成現在第3方的︰
<!--?xml version="1.0" encoding="utf-8"?-->
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
     
    <com.getbase.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="16dp"
        android:src="@drawable/ic_done">
         
    <com.getbase.floatingactionbutton.FloatingActionButton>
</android.support.design.widget.CoordinatorLayout>

然後執行它。

此時,
我們發現CoordinatorLayout完全沒有任何作用。
為什麼呢?
這是由於我們的第3方FAB套件沒有實作預設的Coordinatorlayout.Behavior


●行為!一切都是行為!(Coordinatorlayout.Behavior)
是的。

為什麼Android會知道當Snacker往上彈起時,
FAB也需要跟著向上彈起呢?
因為官方的FAB實作了Behavior(行為)。

籍由CoordinatorLayout這個強大的Framework,
開發者們完全不用自己操刀去控制2個互相牽聯的view元件,
然後一邊算A view(被依賴者,在這篇文章範例裡指的是SnackbarLayout),
一邊再控制B view(依賴者,在這篇文章範例指的是第3方的FAB),
只要實作欲依賴的B view的Behavior,
就能輕鬆達到A、B兩個view的互動。

怎麼實作呢?

首先,我們需要創建一個新的類別,
並且繼承自CoordinatorLayout.Behavior

public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior<FloatingActionButton>

為了確保我們這個實作能從xml中被inflate(擴充),
需要在這個類別裡加上含有Context與AttributeSet兩個參數的建構子︰
public FloatingActionButtonBehavior(Context context, AttributeSet attrs) {}

下一步,
Override(覆寫) layoutDependsOn()函式並且回傳true,
以確保CoordinatorLayout能幫我們明確的監聽到A view(被依賴者,SnackbarLayout)的任何畫面異動。

由於我們現在的例子中,
被依賴者是A view(範例裡的SnackbarLayout元件),
因此我們將layoutDependsOn()函式覆寫如下︰
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
  return dependency instanceof SnackbarLayout;
}

意思是︰
倘若收到的dependency(依賴者)是SnackbarLayout,
那麼我們就告訴CoordinatorLayout說︰
對對對!我就是依賴著它SnackbarLayout。

再來,
我們要告訴CoordinatorLayout︰"倒底要怎麼個互動"的實作了。

因為剛才我們已經告訴CoordinatorLayout說︰
第3方的FAB元件現在要依賴SnackbarLayout。
因此,
每當CoordinatorLayout發現到SnackbarLayout有畫面異動時,
都會藉由一隻Callback(回呼函式)通知我們現在正在實作的FloatingActionButtonBehavior類別。
這個Callback為︰
onDependentViewChanged()

因為onDependentViewChanged()這個函式,
我們可以取到被依賴者︰SnackbarLayout視圖當下的任何狀態
(因為...CoordinatorLayout幫你把整個SnackbarLayout視圖都傳過來了...你還會抓不到嗎@@)

我們現在想要做岀以下這個效果,
這也是前一篇教學原本就有的效果︰

每當Snackbar向上彈起時,
我們第3方FAB元件也能跟著向上彈起。

為了做到這個效果,
我們需要跟著Snackbar現在的Y值去位移第3方FAB元件的Y值,
簡單來說,
就是Snackbar現在彈多高、FAB就彈多高的意思啦!

依照官方文件的說明,
實作onDependentViewChanged()這個函式,
我們一樣要回傳true才能發揮作用。
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
  float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
  child.setTranslationY(translationY);
  return true;
}

這段Code的意思是︰每當被依賴者SnackBar一旦移動了多少Y值(高度),就將第3方FAB也跟著移動多少Y值。

這樣子我們就實作完了Behavior要的2個基本Callback(回呼函式)
layoutDependsOn()和onDependentViewChanged(),以及一個含有AttributeSet的建構子了。

剩下最後一步...
現在我們已經將Behavior實作完了,
接下來就只剩下︰
告訴CoordinatorLayout我們的第3方FAB有實作你要的Behavior,
請你幫我收下並做岀我要的效果!

因此,
我們將xml裡,第3方的FAB元件加上一行屬性
app:layout_behavior="com.simon.hellocoordinatorlayout.FloatingActionButtonBehavior"

整頁xml看起來會是這個樣子
<!--?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
     
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="右下角是第3方套件的FAB按鈕,該專案同時實作了CoordinatorLayout.Behivor"/>

    <com.getbase.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="16dp"
        app:fab_icon="@drawable/ic_done"
        app:layout_behavior="com.simon.hellocoordinatorlayout.FloatingActionButtonBehavior"/>
         
</android.support.design.widget.CoordinatorLayout>

差不多就是這個樣子了,
為了證明這整篇教學沒有在唬爛,
底下這是執行後的畫面︰
如果您想要使用預設的Behavior,
只需要在您的Behavior類別上,
加上DefaultBehavior這個Anntation即可。


三、總結

藉由實作CoordinatorLayout.Behavior,
我們讓 第3方FAB元件 與 SnackBar 達到了 互 動 效果。

那...如果今天有一個專案需求是︰
當列表滾動時,
上方的工具列要隱藏,
又要怎麼做?
誰是依賴者?誰又是被依賴者?

待續...XD

四、其它

附上本篇教學的原始碼