Floating Action Button-Android M新控件
概述
浮動操作按鈕是Material Design 中推出的控件之一
浮動操作按鈕 (簡稱 FAB) 是: “一個特殊的promoted操作案例。因為一個浮動在UI之上的圓形圖標而顯得格外突出,同時它還具有特殊的手勢行為”
比如,如果我們在使用email app,在列出收件箱郵件列表的時候,promoted操作可能就是新建一封郵件。
浮動操作按鈕代表一個屏幕之內最基本的額操作。關于FAB按鈕的更多信息和使用案例請參考谷歌的官方設計規范。
運行效果
用法
谷歌在2015年的 I/O大會上公布了可以創建浮動操作按鈕的支持庫,但是在這之前,則須使用諸如makovkastar/FloatingActionButton 和 futuresimple/android-floating-action-button 這樣的第三方庫。
Floating Action Icons
The floating action button uses the same menu icons used for the App Bar at the top of the screen. This means the image should be single color and fit the material design guidelines. The best source for these icons is the material design icons site or the official google material icons:
Once you’ve selected the icon to use, download the image by selecting the icon and then “Icon Package” and choose the “Android” package. Note that Mac users may need to use the Unarchiver to properly unzip the icon package. Bring the various drawables into the drawable folders within your Android app.
Design Support Library
效果圖
操作步驟
首先確保你按照Design Support Library中的指導來配置。
現在你可以把android.support.design.widget.FloatingActionButton添加到布局中了。其中src屬性指的是浮動按鈕所要的圖標。
<android.support.design.widget.FloatingActionButtonandroid:src="@drawable/ic_done"app:fabSize="normal"android:layout_width="wrap_content"android:layout_height="wrap_content" />另外,如果在布局的最頂部聲明了xmlns:app=”http://schemas.android.com/apk/res-auto命名空間,你還可以定義一個fabSize屬性,該屬性決定按鈕是 normal or mini.
放置浮動操作按鈕需要使用CoordinatorLayout。CoordinatorLayout幫助我們協調它所包含的子view之間的交互,這一點在我們后面講如何根據滾動的變化讓按鈕動畫隱藏與顯示的時候有用。但是目前我們能從CoordinatorLayout得到的好處是它可以讓一個元素浮動在另一個元素之上。我們只需讓FloatingActionButton和ListView被包含在CoordinatorLayout中,然后使用layout_anchor 與 layout_anchorGravity 屬性就可以了。
<android.support.design.widget.CoordinatorLayoutandroid:id="@+id/main_content"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"><ListViewandroid:id="@+id/lvToDoList"android:layout_width="match_parent"android:layout_height="match_parent"></ListView><android.support.design.widget.FloatingActionButtonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="bottom|right"android:layout_margin="16dp"android:src="@drawable/ic_done"app:layout_anchor="@id/lvToDoList"app:layout_anchorGravity="bottom|right|end" /> </android.support.design.widget.CoordinatorLayout>按鈕應該處于屏幕的右下角。建議在手機上下方的margin設置為16dp而平板上設置為24dp。上面的例子中,使用的是16dp。
而根據谷歌的設計規范,drawable的尺寸應該是24dp。
實際上只需要指定一個布局文件,就可以看到效果了,只不過是這時候的FAB是固定在屏幕指定位置的,而無法隨之滾動,不著急,下面會介紹如何設置成可滾動的FAB
屬性介紹
- FAB 默認使用應用主題中設置的浮起色作為按鍵背景。你可以使用 app:backgroundTint 屬性,或者調用 setBackgroundTintList (ColorStateList tint) 方法改變 FAB 背景色;
- 如上文中提到的,可以使用 app:fabSize 屬性選擇普通大小或者迷你大小;
- 使用 android:src 改變 FAB 對應的 drawable;
- 使用 app:rippleColor 設置 FAB 按下時的波紋效果;
- 使用 app:borderWidth 設置 FAB 邊框寬度;
- 使用 app:elevation 設置閑置狀態下 FAB 的景深(默認是 6dp);
- 使用 app:pressedTranslationZ 設置 FAB 按下時的景深(默認是 12dp)。
浮動操作按鈕的動畫
官方效果圖
操作步驟
要讓這個過程有動畫效果,你需要利用好CoordinatorLayout,CoordinatorLayout幫助協調定義在里面的view之間的動畫。
用RecyclerView替換ListViews
目前,你需要用RecyclerView來替換ListView。就如這節所描述的,RecyclerView是ListView的繼承者。根據谷歌的這篇文章所講的,不支持CoordinatorLayout和ListView一起使用。你可以查看這篇指南,它幫助你過渡到RecyclerView
<android.support.v7.widget.RecyclerViewandroid:id="@+id/lvToDoList"android:layout_width="match_parent"android:layout_height="match_parent" </android.support.v7.widget.RecyclerView>同時你還必須把RecyclerView升級到v22版本(我在這里使用的是 23.1.1),之前的v21不支持與CoordinatorLayout一起工作,確保你的build.gradle 文件是這樣的:
我這個案例中使用了cardView
compile 'com.android.support:recyclerview-v7:23.1.1'compile 'com.android.support:cardview-v7:23.1.1'使用CoordinatorLayout
接下來,你需要現為浮動操作按鈕實現CoordinatorLayout Behavior。這個類用于定義按鈕該如何響應包含在同一CoordinatorLayout之內的其它view。
創建一個繼承自 FloatingActionButton.Behavior 名叫ScrollAwareFABBehavior.java的類。目前浮動操作按鈕默認的behavior是為Snackbar讓出空間,就如這個視頻中的效果。
繼承FloatingActionButton.Behavior
package demo.turing.com.materialdesignwidget.floatingActionButton;import android.content.Context; import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.FloatingActionButton; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.View;/*** MyApp** @author Mr.Yang on 2016-03-04 10:48.* @version 1.0* @desc*/ public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior {/*** 因為是在XML中使用app:layout_behavior定義靜態的這種行為,* 必須實現一個構造函數使布局的效果能夠正常工作。* 否則 Could not inflate Behavior subclass error messages.** @param context* @param attrs*/public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {super();}/*** 處理垂直方向上的滾動事件** @param coordinatorLayout* @param child* @param directTargetChild* @param target* @param nestedScrollAxes* @return*/@Overridepublic boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) {// Ensure we react to vertical scrollingreturn nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL ||super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target,nestedScrollAxes);}/*** 檢查Y的位置,并決定按鈕是否動畫進入或退出** @param coordinatorLayout* @param child* @param target* @param dxConsumed* @param dyConsumed* @param dxUnconsumed* @param dyUnconsumed*/@Overridepublic 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 > 0 && child.getVisibility() == View.VISIBLE) {// User scrolled down and the FAB is currently visible -> hide the FABchild.hide();} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {// User scrolled up and the FAB is currently not visible -> show the FABchild.show();}} }將CoordinatorLayout Behavior與浮動操作按鈕聯系起來,通過xml的自定義屬性pp:layout_behavior中定義它
activity_fab_animation.xml
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/main_content"android:layout_width="match_parent"android:layout_height="match_parent"><android.support.v7.widget.RecyclerViewandroid:id="@+id/rvToDoList"android:layout_width="match_parent"android:layout_height="match_parent" /><android.support.design.widget.FloatingActionButtonandroid:id="@+id/id_fab"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="bottom|right"android:layout_margin="16dp"android:src="@mipmap/ic_add_white"app:layout_anchor="@id/rvToDoList"app:layout_anchorGravity="bottom|right|end"app:layout_behavior="demo.turing.com.materialdesignwidget.floatingActionButton.ScrollAwareFABBehavior" /> </android.support.design.widget.CoordinatorLayout>FabAnimation.java
package demo.turing.com.materialdesignwidget.floatingActionButton;import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView;import demo.turing.com.materialdesignwidget.R;/*** Animating the Floating Action Button*/ public class FabAnimation extends AppCompatActivity {private RecyclerView recyclerView ;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_fab_animation);recyclerView = (RecyclerView) findViewById(R.id.rvToDoList);// 線性布局recyclerView.setLayoutManager(new LinearLayoutManager(this));recyclerView.setAdapter(new NormalRecyclerViewAdapter(this));}}NormalRecyclerViewAdapter.java
package demo.turing.com.materialdesignwidget.floatingActionButton;import android.content.Context; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView;import demo.turing.com.materialdesignwidget.R;/*** MyApp** @author Mr.Yang on 2016-03-04 11:10.* @version 1.0* @desc*/ public class NormalRecyclerViewAdapter extends RecyclerView.Adapter<NormalRecyclerViewAdapter.NormalTextViewHolder> {private final LayoutInflater mLayoutInflater;private final Context mContext;private String[] mTitles;public NormalRecyclerViewAdapter(Context context) {mTitles = context.getResources().getStringArray(R.array.titles);mContext = context;mLayoutInflater = LayoutInflater.from(context);}@Overridepublic NormalTextViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {return new NormalTextViewHolder(mLayoutInflater.inflate(R.layout.item_text, parent, false));}@Overridepublic void onBindViewHolder(NormalTextViewHolder holder, int position) {holder.mTextView.setText(mTitles[position]);}@Overridepublic int getItemCount() {return mTitles == null ? 0 : mTitles.length;}public static class NormalTextViewHolder extends RecyclerView.ViewHolder {TextView mTextView;NormalTextViewHolder(View view) {super(view);mTextView = (TextView) view.findViewById(R.id.text_view);view.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Log.d("NormalTextViewHolder", "onClick--> position = " + getPosition());}});}} }item_text.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"xmlns:card_view="http://schemas.android.com/apk/res-auto"android:id="@+id/cv_item"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="8dp"card_view:cardCornerRadius="4dp"><TextView android:id="@+id/text_view"android:layout_width="match_parent"android:layout_height="wrap_content"android:padding="20dp" /> </android.support.v7.widget.CardView>向下移動 FAB消失,向上移動時,FAB出現。
embedding(嵌入)-floatingactionbutton-in-header
效果圖
操作步驟
This can be achieved by use CoordinatorLayout as the root view. We need to specify layout_anchor for the FAB to the top view and layout_anchorGravity to to bottom|right|end like this:
<android.support.design.widget.CoordinatorLayoutxmlns: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"><LinearLayout android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><LinearLayoutandroid:id="@+id/viewA"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_weight="0.6"android:background="@android:color/holo_purple"android:orientation="horizontal"/><LinearLayoutandroid:id="@+id/viewB"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_weight="0.4"android:background="@android:color/holo_orange_light"android:orientation="horizontal"/></LinearLayout><android.support.design.widget.FloatingActionButtonandroid:id="@+id/fab"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="16dp"android:clickable="true"android:src="@mipmap/ic_add_white"app:layout_anchor="@id/viewA"app:layout_anchorGravity="bottom|right|end"/></android.support.design.widget.CoordinatorLayout>For details check out this stackoverflow post. See the CoordinatorLayout guide for more details on that layout.
Issues:
本文編寫時,FAB 支持庫仍然存在一些 bug,在 Kitkat 和 Lollipop 中分別運行示例代碼,可以看到如下結果:
Lollipop 中的 FAB:
Kitkat 中的 FAB:
Issues 1: Android 4.4 和 5.0 中邊緣顯示
很容易看出,Lollipop 中存在邊緣顯示的問題。為了解決此問題,API21+ 的版本統一定義底部與右邊緣空白為 16dp,Lollipop 以下版本統一設置為 0dp.
values/dimens.xml
<dimen name="fab_margin_right">0dp</dimen> <dimen name="fab_margin_bottom">0dp</dimen>values-v21/dimens.xml
<dimen name="fab_margin_right">16dp</dimen> <dimen name="fab_margin_bottom">16dp</dimen>布局文件的 FAB 中,也設置相應的值。
<android.support.design.widget.FloatingActionButton......android:layout_marginBottom="@dimen/fab_margin_bottom"android:layout_marginRight="@dimen/fab_margin_right"/>Issues 2: Android 5.0 中陰影顯示
再看一遍上面的截圖,會發現 Kitkat 中有陰影顯示,而 Lollipop 中并沒有。在 Lollipop 上,可以直接在 FAB 中設置:
<android.support.design.widget.FloatingActionButton......app:fabSize="normal"app:borderWidth="0dp"android:layout_marginBottom="@dimen/fab_margin_bottom"android:layout_marginRight="@dimen/fab_margin_right"/>Issues 3: FAB 中沒有旋轉動畫
https://code.google.com/p/android/issues/detail?id=176116
With Third-Party FloatingActionButton
Using makovkastar/FloatingActionButton library makes floating buttons quite simple to setup. See the library readme and the sample code for reference.
在app/build.gradle:中添加依賴
dependencies {compile 'com.melnykov:floatingactionbutton:1.2.0' }在布局中添加com.melnykov.fab.FloatingActionButton 。記得在根布局中屬性中添加xmlns:fab
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:fab="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"><ListViewandroid:id="@android:id/list"android:layout_width="match_parent"android:layout_height="match_parent" /><com.melnykov.fab.FloatingActionButtonandroid:id="@+id/fab"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="bottom|right"android:layout_margin="16dp"android:src="@drawable/ic_action_content_new"fab:fab_type="normal"fab:fab_shadow="true"fab:fab_colorNormal="@color/primary"fab:fab_colorPressed="@color/primary_pressed"fab:fab_colorRipple="@color/ripple" /> </FrameLayout>依附到list
接下來,我們可以選擇將FAB和一個ListView, ScrollView 或者 RecyclerView 關聯起來,這樣按鈕就會隨著list的向下滾動而隱藏,向上滾動而重現:
ListView listView = (ListView) findViewById(android.R.id.list); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.attachToListView(listView); // or attachToRecyclerView我們可以使用fab.attachToRecyclerView(recyclerView)來依附到一個RecyclerView,或者使用fab.attachToScrollView(scrollView)來依附到一個ScrollView。
調整按鈕類型
浮動操作按鈕有兩種大小:默認的,這應該是最常用的情況,以及mini的,這應該只用于銜接屏幕上的其他元素。
可以把FAB的按鈕類型調整為“正常”或者“mini”
<com.melnykov.fab.FloatingActionButton...fab:fab_type="mini" />FAB的顯示和隱藏
// 帶動畫的顯示和隱藏 fab.show(); fab.hide(); // 不帶動畫的 fab.show(false); fab.hide(false);監聽滾動事件
我們可以監聽所關聯的list的滾動事件,以管理FAB的狀態:
FloatingActionButton fab = (FloatingActionButton) root.findViewById(R.id.fab); fab.attachToListView(list, new ScrollDirectionListener() {@Overridepublic void onScrollDown() {Log.d("ListViewFragment", "onScrollDown()");}@Overridepublic void onScrollUp() {Log.d("ListViewFragment", "onScrollUp()");} }, new AbsListView.OnScrollListener() {@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {Log.d("ListViewFragment", "onScrollStateChanged()");}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {Log.d("ListViewFragment", "onScroll()");} });手動實現
除了使用庫之外,我們還可以自己開發動操作按鈕。關于手動實現浮動操作按鈕,可以查看big nerd ranch guide 以及 survivingwithandroid walkthrough。
參考外文:
Floating Action Buttons
總結
以上是生活随笔為你收集整理的Floating Action Button-Android M新控件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TextInputLayout-Andr
- 下一篇: 全局事件-广播(Broadcast)