MD - 简单控件使用以及自定义Behavior

1.概述

  Material Design从Android5.0开始引入的,是一种全新的设计语言(翻译为“原材料设计”),其实是谷歌提倡的一种设计风格、理念、原则。最近发现现在很多 app 包括最早的知乎没有使用了,今天再来回顾一下控件的简单使用,效果非常简单,先上张图。

    效果展示
                

在这里插入图片描述

2.控件介绍

  2.1 CoordinatorLayout:用来协调子控件,结合AppBarLayout,FloatingActionButton等使用
  2.2 SwipeRefreshLayout:提供的下拉刷新的效果,里面只能包一个子控件可以包任意控件RecyclerView,ScrollView ,ListView。
  2.3 ToolBar:加强版的ActionBar
  2.4 TabLayout:导航书签,可以结合ViewPager使用
        app:tabIndicatorColor            导航书签下方指示线的颜色
        app:tabIndicatorHeight           指示线的高度
        app:tabSelectedTextColor         导航书签选中时的字体颜色
        app:tabMode="fixed"              显示模式
                    1:fixed             全部展示
                    2. scrollable        滑动展示
  2.5 CollapsingToolBarLayout:折叠布局,结合ToolBar使用
        android:minHeight                    最小高度
        app:layout_scrollFlags               模式
                    scroll                   想滚动就必须设置这个。
                    enterAlways              实现quick return效果, 当向下移动时,立即显示View(比如Toolbar)。
                    exitUntilCollapsed       向上滚动时收缩View,但可以固定Toolbar一直在上面。
                    enterAlwaysCollapsed     当你的View已经设置minHeight属性又使用此标志时,你的View只能以最小高度进入,只有当滚动视图到达顶部时才扩大到完整高度。
        app:contentScrim                     当完全CollapsingToolbarLayout折叠(收缩)后的背景颜色。
        app:expandedTitleGravity             Toolbar的title显示位置
        app:expandedTitleMargin              设置显示位置设置Margin值
        app:layout_collapseParallaxMultiplier视差因子   0-1之间
        app:layout_collapseMode              视差模式
                    pin:·                    固定模式
                    parallax:                折叠效果
  2.6 SnackBar:MD风格的Toast,底部弹出
  2.7 TextInputLayout:自带错误提示的文本框

  等等,不一一介绍了,今天只是简单的使用,想了解,可以去官网查看

3.效果实现

  3.1 CoordinatorLayout 和 Behavior 介绍

    CoordinatorLayout是什么? 看一下官方的介绍:Interaction behavior plugin for child views of (子视图的交互行为插件)
    Behavior 实现了用户的一个或者多个交互行为,它们可能包括拖拽、滑动、快滑或者其他一些手势。Behavior 是一个顶层抽象类,其他的一些具体行为的 Behavior 都是继承自这个类。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout 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"
        android:orientation="vertical"
        tools:context=".one_18.BehaviorActivity">
    
        <androidx.coordinatorlayout.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:background="@color/blue"
                app:layout_scrollFlags="scroll|enterAlways|snap"/>
    
    <!--        <androidx.appcompat.widget.Toolbar-->
    <!--            android:id="@+id/tool_bar"-->
    <!--            android:layout_width="match_parent"-->
    <!--            android:layout_height="wrap_content"-->
    <!--            app:title="知乎首页"-->
    <!--            app:titleTextColor="#ffffff"-->
    <!--            app:layout_scrollFlags="scroll|enterAlways|snap">-->
    <!--        </androidx.appcompat.widget.Toolbar>-->
        </com.google.android.material.appbar.AppBarLayout>
    
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
        
        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_marginBottom="70dp"
            android:layout_marginRight="16dp"
            android:layout_marginEnd="16dp"
            app:layout_behavior=".one_18.TranslationBehavior"
            android:src="@mipmap/ic_launcher">
        </com.google.android.material.floatingactionbutton.FloatingActionButton>
    
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:orientation="horizontal"
            android:background="@android:color/white"
            app:layout_behavior="@string/bottom_sheet_behavior">
    
            <ImageView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:src="@mipmap/ic_launcher"/>
            <ImageView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:src="@mipmap/ic_launcher"/>
            <ImageView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:src="@mipmap/ic_launcher"/>
            <ImageView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:src="@mipmap/ic_launcher"/>
        </LinearLayout>
    
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
    </LinearLayout>

看一下Behaviror的方法作用

 /**
     * 表示是否给应用了Behavior 的View 指定一个依赖的布局,通常,当依赖的View 布局发生变化时
     * 不管被被依赖View 的顺序怎样,被依赖的View也会重新布局
     * @param parent
     * @param child 绑定behavior 的View
     * @param dependency   依赖的view
     * @return 如果child 是依赖的指定的View 返回true,否则返回false
     */
    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return super.layoutDependsOn(parent, child, dependency);
    }

    /**
     * 当被依赖的View 状态(如:位置、大小)发生变化时,这个方法被调用
     * @param parent
     * @param child
     * @param dependency
     * @return
     */
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        return super.onDependentViewChanged(parent, child, dependency);
    }

    /**
     *  当coordinatorLayout 的子View试图开始嵌套滑动的时候被调用。当返回值为true的时候表明
     *  coordinatorLayout 充当nested scroll parent 处理这次滑动,需要注意的是只有当返回值为true
     *  的时候,Behavior 才能收到后面的一些nested scroll 事件回调(如:onNestedPreScroll、onNestedScroll等)
     *  这个方法有个重要的参数nestedScrollAxes,表明处理的滑动的方向。
     *
     * @param coordinatorLayout 和Behavior 绑定的View的父CoordinatorLayout
     * @param child  和Behavior 绑定的View
     * @param directTargetChild
     * @param target
     * @param nestedScrollAxes 嵌套滑动 应用的滑动方向,看 {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
     *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL}
     * @return
     */
    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
        return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
    }

    /**
     * 嵌套滚动发生之前被调用
     * 在nested scroll child 消费掉自己的滚动距离之前,嵌套滚动每次被nested scroll child
     * 更新都会调用onNestedPreScroll。注意有个重要的参数consumed,可以修改这个数组表示你消费
     * 了多少距离。假设用户滑动了100px,child 做了90px 的位移,你需要把 consumed[1]的值改成90,
     * 这样coordinatorLayout就能知道只处理剩下的10px的滚动。
     * @param coordinatorLayout
     * @param child
     * @param target
     * @param dx  用户水平方向的滚动距离
     * @param dy  用户竖直方向的滚动距离
     * @param consumed
     */
    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
    }

    /**
     * 进行嵌套滚动时被调用
     * @param coordinatorLayout
     * @param child
     * @param target
     * @param dxConsumed target 已经消费的x方向的距离
     * @param dyConsumed target 已经消费的y方向的距离
     * @param dxUnconsumed x 方向剩下的滚动距离
     * @param dyUnconsumed y 方向剩下的滚动距离
     */
    @Override
    public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
    }

    /**
     *  嵌套滚动结束时被调用,这是一个清除滚动状态等的好时机。
     * @param coordinatorLayout
     * @param child
     * @param target
     */
    @Override
    public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
        super.onStopNestedScroll(coordinatorLayout, child, target);
    }

    /**
     * onStartNestedScroll返回true才会触发这个方法,接受滚动处理后回调,可以在这个
     * 方法里做一些准备工作,如一些状态的重置等。
     * @param coordinatorLayout
     * @param child
     * @param directTargetChild
     * @param target
     * @param nestedScrollAxes
     */
    @Override
    public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
        super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
    }

    /**
     * 用户松开手指并且会发生惯性动作之前调用,参数提供了速度信息,可以根据这些速度信息
     * 决定最终状态,比如滚动Header,是让Header处于展开状态还是折叠状态。返回true 表
     * 示消费了fling.
     *
     * @param coordinatorLayout
     * @param child
     * @param target
     * @param velocityX x 方向的速度
     * @param velocityY y 方向的速度
     * @return
     */
    @Override
    public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) {
        return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
    }

    /**
     * 摆放子 View 的时候调用,可以重写这个方法对子View 进行重新布局
     */
    @Override
    public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
        return super.onLayoutChild(parent, child, layoutDirection);
    }
  3.2 自定义 Behavior 来实现 FloatingActionBar 的改变
    public class TranslationBehavior<T extends FloatingActionButton> extends CoordinatorLayout.Behavior<T> {
        private static final String TAG = "TranslationBehavior";
        
        /**
         * FloatingActionButton当前是否是显示状态
         */
        private boolean isOut = false;
    
        public TranslationBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        /**
         *  注意:当coordinatorLayout 的子View试图开始嵌套滑动的时候被调用。当返回值为true的时候表明
         *  coordinatorLayout 充当nested scroll parent 处理这次滑动,需要注意的是只有当返回值为true
         *  的时候,Behavior 才能收到后面的一些nested scroll 事件回调(如:onNestedPreScroll、onNestedScroll等)
         *  这个方法有个重要的参数nestedScrollAxes,表明处理的滑动的方向。
         * @param coordinatorLayout 和Behavior 绑定的View的父CoordinatorLayout
         * @param child  和Behavior 绑定的View
         * @param directTargetChild
         * @param target
         * @param axes 嵌套滑动,滑动方向, {@link ViewCompat#SCROLL_AXIS_HORIZONTAL}
         *                                 {@link ViewCompat#SCROLL_AXIS_VERTICAL}
         * @return
         */
        @Override
        public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull T child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
            Log.e(TAG,"onStartNestedScroll");
            return axes == ViewCompat.SCROLL_AXIS_VERTICAL;
        }
    
    
        /**
         * 进行嵌套滚动时被调用
         * @param coordinatorLayout
         * @param child
         * @param target
         * @param dxConsumed    target 已经消费的x方向的距离
         * @param dyConsumed    target 已经消费的y方向的距离  往上滑是正  往下滑是负
         * @param dxUnconsumed  x 方向剩下的滚动距离
         * @param dyUnconsumed  y 方向剩下的滚动距离
         * @param type
         */
        @Override
        public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull T child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
            Log.e(TAG,"onNestedScroll");
            super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
            Log.e(TAG,"dyConsumed:"+dyConsumed+"--------dyUnconsumed:"+dyUnconsumed);
            //1.将我们的child(FloatingActionButton)进行隐藏
            if (dyConsumed > 0){
                if (!isOut){
                    //2.移动距离计算  bottomMargin + 自己的高度
                    int translationY = ((CoordinatorLayout.LayoutParams)child.getLayoutParams()).bottomMargin + child.getMeasuredHeight();
                    child.animate().translationY(translationY).setDuration(500).start();
                    //3.注意:这样设置后网上滑的时候会发现FloatingActionButton会反应慢,我们可以设置一个标志位
                    isOut = true;
                }
            }else {
                if (isOut){
                    child.animate().translationY(0).setDuration(500).start();
                    isOut = false;
                }
            }
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356