Material Design 控件知识梳理(4) - FloatingActionButton

Material Design 控件知识梳理(1) - Android Design Support Library 是什么
Material Design 控件知识梳理(2) - AppBarLayout & CollapsingToolbarLayout
Material Design 控件知识梳理(3) - BottomSheet && BottomSheetDialog && BottomSheetDialogFragment
Material Design 控件知识梳理(4) - FloatingActionButton
Material Design 控件知识梳理(5) - DrawerLayout && NavigationView
Material Design 控件知识梳理(6) - Snackbar
Material Design 控件知识梳理(7) - BottomNavigationBar
Material Design 控件知识梳理(8) - TabLayout
Material Design 控件知识梳理(9) - TextInputLayout

一、概述

今天,我们介绍一个比较简单的控件: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边界的宽度,边界的颜色会比背景色稍淡,如下图所示

点击属性

  • 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先上移,然后消失:

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

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>

我们的根布局是一个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来让它移入或者移出屏幕。

五、总结

这篇文章,介绍了Fab的基本用法、其它MD控件的联动以及如何自定义FloatingActionButtonBehavior


更多文章,欢迎访问我的 Android 知识梳理系列:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容