Material Design 控制元件知識梳理(4) FloatingActionButton

澤毛發表於2017-12-21

一、概述

今天,我們介紹一個比較簡單的控制元件:FloatingActionButton,相信大家一定都聽過也在網上見過類似的例子,我們就分為三個部分介紹一下FloatingActionButton的相關知識:

  • Fab的基礎使用
  • Fab和其它MD控制元件的組合
  • 通過自定義FloatingActionButton.Behavior,讓Fab根據列表的狀態顯示和隱藏

二、Fab的基礎使用

Fab本質上其實是一個ImageButton,只是它在ImageButton的基礎上增加了一些屬性,這裡介紹幾個常用的屬性:

展現屬性

  • android:srcFab的圖片,這其實是ImageView的屬性。
  • app:backgroundTintFab的背景色,如果沒有設定,那麼會取theme中的colorAccent作為背景色。
  • app:fabSizeFab的大小,可選的值包括:
  • mini
  • normal
  • automininormal都預設了固定的大小,而auto屬性則會根據螢幕的寬度來設定,在小螢幕上使用mini,而在大螢幕上使用normal,當然我們也可以直接通過layout_width/layout_height來指定。
  • app:elevationFabZ軸方向的距離,也就是深度。
  • app:borderWidthFab邊界的寬度,邊界的顏色會比背景色稍淡,如下圖所示
    Material Design 控制元件知識梳理(4)   FloatingActionButton

點選屬性

  • app:pressedTranslationZ:點選時FabZ軸的變化值。
  • app:rippleColor:點選時水波紋擴散的顏色。

三、與其它MD控制元件結合使用

3.1 和AppBarLayout聯動

通過給Fab設定app:layout_anchorlayout_anchorGravity兩個屬性,可以讓Fab跟隨AppBarLayout移動,並在合適的時候隱藏,下面是我們的佈局:

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.demo.lizejun.repotransition.FABActivity">
    <android.support.design.widget.AppBarLayout
        android:id="@+id/al_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <ImageView
            android:id="@+id/iv_title"
            android:src="@drawable/ic_bg"
            android:layout_width="match_parent"
            android:scaleType="centerCrop"
            android:layout_height="150dp"
            app:layout_scrollFlags="scroll"/>
    </android.support.design.widget.AppBarLayout>
    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@android:drawable/ic_btn_speak_now"
        android:layout_margin="10dp"
        app:backgroundTint="@color/colorPrimary"
        app:layout_anchor="@id/al_title" 
        app:layout_anchorGravity="bottom|end"/>
</android.support.design.widget.CoordinatorLayout>
複製程式碼

當我們滾動佈局的時候,Fab會跟著AppBarLayout先上移,然後消失:

Material Design 控制元件知識梳理(4)   FloatingActionButton

3.2 和BottomSheet聯動

AppBarLayout類似,我們也可以通過設定layout_anchor的方法讓FabBottomSheet聯動:

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.demo.lizejun.repotransition.FABActivity">
    <android.support.design.widget.AppBarLayout
        android:id="@+id/al_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <ImageView
            android:id="@+id/iv_title"
            android:src="@drawable/ic_bg"
            android:layout_width="match_parent"
            android:scaleType="centerCrop"
            android:layout_height="150dp"
            app:layout_scrollFlags="scroll"/>
    </android.support.design.widget.AppBarLayout>
    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
    <include android:id="@+id/bottom_sheet" layout="@layout/layout_bottom_sheet_linear"/>
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@android:drawable/ic_btn_speak_now"
        android:layout_margin="10dp"
        app:backgroundTint="@color/colorPrimary"
        app:layout_anchor="@id/bottom_sheet"
        app:layout_anchorGravity="end"/>
</android.support.design.widget.CoordinatorLayout>
複製程式碼

這裡,我們把layout_anchor設為bottom_sheet

Material Design 控制元件知識梳理(4)   FloatingActionButton

3.3 和Snackbar聯動

需要和Snackbar聯動時,不需要Fab設定layout_anchor,而是需要在Snackbar展現的時候,第一個引數傳入的是CoordinatorLayout

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fab);
        initView();
        mRootView = (CoordinatorLayout) findViewById(R.id.cl_root);
        mFab = (FloatingActionButton) findViewById(R.id.fab);
        mFab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //這裡需要傳入CoordinatorLayout
                Snackbar.make(mRootView, "點選Fab", Snackbar.LENGTH_LONG).show();
            }
        });

    }
複製程式碼

下面是展現的效果:

Material Design 控制元件知識梳理(4)   FloatingActionButton

四、Fab根據列表的狀態顯示或隱藏

Fab的內部,定義了一個Behavior,我們可以通過繼承這個Behavior來監聽CoordinatorLayout內佈局的變化,以實現Fab的顯示和隱藏,首先看我們的佈局:

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/cl_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.demo.lizejun.repotransition.FABActivity">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_content"
        android:tag="rv_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@android:drawable/ic_btn_speak_now"
        android:layout_margin="10dp"
        android:layout_gravity="bottom|end"
        app:backgroundTint="@color/colorPrimary"
        app:layout_behavior="com.demo.lizejun.repotransition.behavior.FabListBehavior"/>
</android.support.design.widget.CoordinatorLayout>
複製程式碼

我們的根佈局是一個CoordinatorLayoutRecyclerViewFab是它的兩個子ViewFab位於CoordinatorLayout的右下角,注意到,這裡我們給Fab設定了一個自定義的Behavior,正是通過這個behaviorFab可以監聽到CoordinatorLayout內佈局的滾動情況,下面是我們的Behavior

public class FabListBehavior extends FloatingActionButton.Behavior {

    private static final int MIN_CHANGED_DISTANCE = 30;

    public FabListBehavior(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) {
        return true;
    }

    @Override
    public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
        if (dyConsumed > MIN_CHANGED_DISTANCE) {
            createValueAnimator(coordinatorLayout, child, false).start();
        } else if (dyConsumed < -MIN_CHANGED_DISTANCE) {
            createValueAnimator(coordinatorLayout, child, true).start();
        }
    }

    private Animator createValueAnimator(CoordinatorLayout coordinatorLayout, final View fab, boolean dismiss) {
        int distanceToDismiss = coordinatorLayout.getBottom() - fab.getBottom() + fab.getHeight();
        int end = dismiss ? 0 : distanceToDismiss;
        float start = fab.getTranslationY();
        ValueAnimator animator = ValueAnimator.ofFloat(start, end);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                fab.setTranslationY((Float) animation.getAnimatedValue());
            }
        });
        return animator;
    }

}
複製程式碼

這裡,我們繼承於FloatingActionButton.Behavior來實現自己的Behavior,注意,必須要宣告建構函式為FabListBehavior(Context, AttributeSet),並呼叫super()方法,否則會無法例項化:

  • onStartNestedScroll決定了之後是否需要回撥onNestedScroll,這裡我們直接返回true
  • onNestedScroll:我們根據dyConsumed的正負值來判斷列表滾動的方法,然後通過改變FabtranslationY來讓它移入或者移出螢幕。
    Material Design 控制元件知識梳理(4)   FloatingActionButton

五、總結

這篇文章,介紹了Fab的基本用法、其它MD控制元件的聯動以及如何自定義FloatingActionButtonBehavior


更多文章,歡迎訪問我的 Android 知識梳理系列:

相關文章