美文网首页Android材料设计--Material Design
Android材料设计之Behavior攻坚战

Android材料设计之Behavior攻坚战

作者: e4e52c116681 | 来源:发表于2018-11-30 13:08 被阅读38次

    本系列文章一览:


    前言

    Behavior应该和CoordinatorLayout一对的,这里单独说因为Behavior比较难一点
    经过前面的bottom_sheet_behavior、appbar_scrolling_view_behavior 应该对 behavior有一定的认识
    注意:改动自定义behavior路径时一定要改使用到的地方,不然肯定崩,一定要改!! 一定要改!!

    本文内容:
    • 1.认识Behavior的使用方式
    • 2.自定义Behavior,分析layoutDependsOn回调和onDependentViewChanged回调
    • 3.自定义Behavior,分析onNestedScroll回调和onNestedPreScroll回调

    一、简单认识

    1.使用

    在CoordinatorLayout和AppBarLayout那篇貌似也没有碰到Behavior啊
    不过仔细想一下,好像有个地方比较特殊,那就是app:layout_behavior

    <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"/>
    
    2.string字符串:

    再看一下这个string是什么鬼==>原来是一个安卓design包中内置的一个字符串
    看起来很像一个类名有没有:AppBarLayout的内部类ScrollingViewBehavior

    <string name="appbar_scrolling_view_behavior" translatable="false">android.support.design.widget.AppBarLayout$ScrollingViewBehavior</string>
    
    3.对应类

    果然有这个类:android.support.design.widget.AppBarLayout.ScrollingViewBehavior

        public static class ScrollingViewBehavior extends HeaderScrollingViewBehavior {
            public ScrollingViewBehavior() {}
            public ScrollingViewBehavior(Context context, AttributeSet attrs) {
                super(context, attrs);
                final TypedArray a = context.obtainStyledAttributes(attrs,
                        R.styleable.ScrollingViewBehavior_Layout);
                setOverlayTop(a.getDimensionPixelSize(
                        R.styleable.ScrollingViewBehavior_Layout_behavior_overlapTop, 0));
                a.recycle();
            }
            //省略n行......
        }
    

    二、自定义Behavior

    1.既然安卓内部可以玩,那么我们也可以自定义玩玩
    /**
     * 作者:张风捷特烈<br/>
     * 时间:2018/11/28 0028:17:02<br/>
     * 邮箱:1981462002@qq.com<br/>
     * 说明:最简单的behavior
     */
    public class FirstBehavior extends CoordinatorLayout.Behavior<View> {
        public FirstBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
            return dependency instanceof AppBarLayout;
        }
    
        @Override
        public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
            return true;
        }
    }
    

    2.模仿安卓内置behavior
    <string name="behavior_first">com.toly1994.vvi_mds.v04_behavior.FirstBehavior</string>
    
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout
        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">
    
        <android.support.design.widget.AppBarLayout
            android:id="@+id/al_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
                <TextView
                    android:id="@+id/id_tv_moving"
                    style="@style/TVTestCenter"
                    android:background="@color/feise"
                    app:layout_scrollFlags="scroll"
                    android:text="Flag"/>
        </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/behavior_first"/>
    
    </android.support.design.widget.CoordinatorLayout>
    
    

    3.使用结果:
    自定义Behavior.gif
    4.关于几个参数

    debug一下,应该很清楚的看出viewdependency分别对应的是什么

    debug.png
    总得来说behavior就是:
    在CoordinatorLayout中,让AppBarLayout里的首控件和添加behavior的控件进行联动,而实现酷炫逆天的效果,
    其中child为添加了behavior的那个控件,dependency为AppBarLayout。
    

    三、对第一个Behavior的分析

    目测:当dependcy移动自身高度之后onDependentViewChanged将不再回调

    1.对dependcy的操作

    既然两个View都在手上,那玩玩呗:让移动时TextView的背景色随机变化,这些知道dependcy是谁了吧

    移动时变化dependcy.gif
    /**
     * 确定使用Behavior的View要依赖的View的类型:
     * 返回false:onDependentViewChanged不触发
     *
     * @param parent     CoordinatorLayout布局容器
     * @param child      装载behavior的控件
     * @param dependency 被联动的控件
     * @return
     */
    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return dependency instanceof AppBarLayout;
    }
    
    /**
     * 当被依赖的View状态改变时回调
     *
     * @param parent     CoordinatorLayout布局容器
     * @param child      装载behavior的控件
     * @param dependency 被联动的控件
     * @return
     */
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        ViewGroup dep = (ViewGroup) dependency;//AppBarLayout
        View childTv = dep.getChildAt(0);//获取第一个孩子
        childTv.setBackgroundColor(ColUtils.randomRGB());//背景设置随机色
        return true;
    }
    

    2.几个重要的参数:

    注意:为了看一下getY和getTop的区别,这里特意setTranslationY(100)
    可以看出getY包含了setTranslationY的值,getTop不包括setTranslationY,所以按需使用(如果没有平移,随便用)
    可以看到移动的有效长度是dependency的高度,一旦超过onDependentViewChanged将不再回调

    dependency移动分析.png dependency测试.gif
    /**
     * 当被依赖的View状态改变时回调
     *
     * @param parent     CoordinatorLayout布局容器
     * @param child      装载behavior的控件
     * @param dependency 被联动的控件
     * @return
     */
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        dependency.setTranslationY(100);
        float dependencyY = dependency.getY();
        int dependencyHeight = dependency.getHeight();
        int dependencyTop = dependency.getTop();
        Log.e("onDependentViewChanged","dependencyY:" + dependencyY + ",dependencyHeight:" + dependencyHeight + ",dependencyTop:" + dependencyTop + L.l());
    
        int childHeight = child.getHeight();
        float childY = child.getY();
        int childTop = child.getTop();
    
        Log.e("onDependentViewChanged","childHeight:" + childHeight + ",childY:" + childY + ",childTop:" + childTop + L.l());
        return true;
    }
    

    3.判断dependency位移方向,让child进行联动

    这里处理很简单:将child反方向进行移动,但效果看起来还不错
    在布局中加入了一个TextView占一下视觉空间,不然空空的不好看

    联动.gif
    /**
     * 当被依赖的View状态改变时回调
     *
     * @param parent     CoordinatorLayout布局容器
     * @param child      装载behavior的控件
     * @param dependency 被联动的控件
     * @return
     */
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        child.setTranslationY(-dependency.getY());
        return true;
    }
    
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout
        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">
    
        <android.support.design.widget.AppBarLayout
            android:id="@+id/al_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
                <TextView
                    android:id="@+id/id_tv_moving"
                    style="@style/TVTestCenter"
                    android:background="@color/feise"
                    app:layout_scrollFlags="scroll"
                    android:text="Flag"/>
        </android.support.design.widget.AppBarLayout>
    
    
        <TextView
            android:id="@+id/hide"
            style="@style/TVTestCenter"
            android:background="@color/yase"
            app:layout_scrollFlags="scroll"
            android:text="Find Me"/>
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/behavior_first"/>
    
    </android.support.design.widget.CoordinatorLayout>
    

    4.移动方向和分度值
    获取移动分度值.gif
    //添加成员变量
    private float curY;
    
     /**
      * 当被依赖的View状态改变时回调
      *
      * @param parent     CoordinatorLayout布局容器
      * @param child      装载behavior的控件
      * @param dependency 被联动的控件
      * @return
      */
     @Override
     public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
         float dy = dependency.getTop() - curY; //dy>0 ----下移
         float faction;//移动的分度值
         if (dy <= 0) {//上移动
             faction = -dependency.getTop() * 1.f / dependency.getHeight();
         } else {
             faction = 1 - (-dependency.getTop() * 1.f / dependency.getHeight());
         }
    //  dependency.setTranslationY(-dependency.getTop());//让dependency不移动
        dependency.setPivotX(dependency.getWidth()/2);//旋转
        dependency.setPivotY(dependency.getHeight()/2);
        dependency.setRotation(360 * faction);//旋转
        child.setTranslationY(-curY);
        curY = dependency.getY();//更新curY
        return true;
    }
    

    四、自定义FloatingActionButton伴随列表移动

    onNestedScroll和onNestedPreScroll

    1.布局
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout
        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">
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
        <android.support.design.widget.FloatingActionButton
            android:id="@+id/id_fab_b"
            android:layout_width="50dp"
            android:layout_height="50dp"
            app:layout_behavior="@string/behavior_second"
            android:layout_gravity="bottom|end"
            android:layout_margin="@dimen/dp_32"
            android:src="@drawable/icon_love"/>
    
    </android.support.design.widget.CoordinatorLayout>
    
    
    2.自定义Behavior:
    onNestedScroll.png

    接触目标view时才会回调:onStartNestedScroll
    加了layout_behavior的View是child

    平移 缩放
    /**
     * 作者:张风捷特烈<br/>
     * 时间:2018/11/30 0030:14:34<br/>
     * 邮箱:1981462002@qq.com<br/>
     * 说明:FloatingActionButton伴随动画
     */
    public class FabFollowListBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
        private static final int MIN_DY = 30;
        private static final String TAG = "FabFollowListBehavior";
    
        public FabFollowListBehavior(Context context, AttributeSet attributeSet) {
            super(context, attributeSet);
        }
    
        /**
         * 初始时不调用,滑动时调用---一次滑动过程,之调用一次
         */
        @Override
        public boolean onStartNestedScroll(
                @NonNull CoordinatorLayout coordinatorLayout,
                @NonNull FloatingActionButton child,
                @NonNull View directTargetChild,
                @NonNull View target, int axes, int type) {
            return true;
        }
    
        /**
         * @param dyConsumed 每次回调前后的Y差值
         */
        @Override
        public void onNestedScroll(
                @NonNull CoordinatorLayout coordinatorLayout,
                @NonNull FloatingActionButton child,
                @NonNull View target, int dxConsumed,
                int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
            super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
    
            //平移隐现
            if (dyConsumed > MIN_DY) {//上滑:消失
                showOrNot(coordinatorLayout, child, false).start();
            } else if (dyConsumed < -MIN_DY) {//下滑滑:显示
                showOrNot(coordinatorLayout, child, true).start();
            }
    
            //仅滑动时消失
    //        if (dyConsumed > MIN_DY || dyConsumed < -MIN_DY) {//上滑:消失
    //            showOrNot(child).start();
    //        }
        }
    
        private Animator showOrNot(CoordinatorLayout coordinatorLayout, final View fab, boolean show) {
            //获取fab头顶的高度
            int hatHeight = coordinatorLayout.getBottom() - fab.getBottom() + fab.getHeight();
            int end = show ? 0 : hatHeight;
            float start = fab.getTranslationY();
            ValueAnimator animator = ValueAnimator.ofFloat(start, end);
            animator.addUpdateListener(animation ->
                    fab.setTranslationY((Float) animation.getAnimatedValue()));
            return animator;
        }
    
        private Animator showOrNot(final View fab) {
            //获取fab头顶的高度
            ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
    
            animator.addUpdateListener(animation -> {
                fab.setScaleX((Float) animation.getAnimatedValue());
                fab.setScaleY((Float) animation.getAnimatedValue());
            });
            return animator;
        }
    }
    

    Behavior基本概念就这样,由于并不是非常常用,更深的用法等有需求再深究吧


    后记:捷文规范

    1.本文成长记录及勘误表
    项目源码 日期 备注
    V0.1--github 2018-11-30 Android材料设计之Behavior攻坚战
    2.更多关于我
    笔名 QQ 微信 爱好
    张风捷特烈 1981462002 zdl1994328 语言
    我的github 我的简书 我的掘金 个人网站
    3.声明

    1----本文由张风捷特烈原创,转载请注明
    2----欢迎广大编程爱好者共同交流
    3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
    4----看到这里,我在此感谢你的喜欢与支持


    icon_wx_200.png

    相关文章

      网友评论

        本文标题:Android材料设计之Behavior攻坚战

        本文链接:https://www.haomeiwen.com/subject/zixhcqtx.html