view

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

2019-09-29  本文已影响0人  如愿以偿丶

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;
                }
            }
        }
    }
上一篇 下一篇

猜你喜欢

热点阅读