一、概述
今天,我們介紹一個比較簡單的控制元件:FloatingActionButton
,相信大家一定都聽過也在網上見過類似的例子,我們就分為三個部分介紹一下FloatingActionButton
的相關知識:
Fab
的基礎使用Fab
和其它MD
控制元件的組合- 通過自定義
FloatingActionButton.Behavior
,讓Fab
根據列表的狀態顯示和隱藏
二、Fab
的基礎使用
Fab
本質上其實是一個ImageButton
,只是它在ImageButton
的基礎上增加了一些屬性,這裡介紹幾個常用的屬性:
展現屬性
android:src
:Fab
的圖片,這其實是ImageView
的屬性。app:backgroundTint
:Fab
的背景色,如果沒有設定,那麼會取theme
中的colorAccent
作為背景色。app:fabSize
:Fab
的大小,可選的值包括:mini
:normal
auto
:mini
和normal
都預設了固定的大小,而auto
屬性則會根據螢幕的寬度來設定,在小螢幕上使用mini
,而在大螢幕上使用normal
,當然我們也可以直接通過layout_width/layout_height
來指定。app:elevation
:Fab
在Z
軸方向的距離,也就是深度。app:borderWidth
:Fab
邊界的寬度,邊界的顏色會比背景色稍淡,如下圖所示
點選屬性
app:pressedTranslationZ
:點選時Fab
在Z
軸的變化值。app:rippleColor
:點選時水波紋擴散的顏色。
三、與其它MD
控制元件結合使用
3.1 和AppBarLayout
聯動
通過給Fab
設定app:layout_anchor
和layout_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
先上移,然後消失:
3.2 和BottomSheet
聯動
和AppBarLayout
類似,我們也可以通過設定layout_anchor
的方法讓Fab
和BottomSheet
聯動:
<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
:
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();
}
});
}
複製程式碼
下面是展現的效果:
四、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>
複製程式碼
我們的根佈局是一個CoordinatorLayout
,RecyclerView
和Fab
是它的兩個子View
,Fab
位於CoordinatorLayout
的右下角,注意到,這裡我們給Fab
設定了一個自定義的Behavior
,正是通過這個behavior
,Fab
可以監聽到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
的正負值來判斷列表滾動的方法,然後通過改變Fab
的translationY
來讓它移入或者移出螢幕。
五、總結
這篇文章,介紹了Fab
的基本用法、其它MD
控制元件的聯動以及如何自定義FloatingActionButtonBehavior
。
更多文章,歡迎訪問我的 Android 知識梳理系列:
- Android 知識梳理目錄:www.jianshu.com/p/fd82d1899…
- 個人主頁:lizejun.cn
- 個人知識總結目錄:lizejun.cn/categories/