前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android材料设计之Behavior攻坚战

Android材料设计之Behavior攻坚战

作者头像
张风捷特烈
发布2018-12-21 16:53:25
1.2K0
发布2018-12-21 16:53:25
举报

前言

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

代码语言:javascript
复制
<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

代码语言:javascript
复制
<string name="appbar_scrolling_view_behavior" translatable="false">android.support.design.widget.AppBarLayout$ScrollingViewBehavior</string>
3.对应类

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

代码语言:javascript
复制
    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.既然安卓内部可以玩,那么我们也可以自定义玩玩
代码语言:javascript
复制
/**
 * 作者:张风捷特烈<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
代码语言:javascript
复制
<string name="behavior_first">com.toly1994.vvi_mds.v04_behavior.FirstBehavior</string>
代码语言:javascript
复制
<?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

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

三、对第一个Behavior的分析

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

1.对dependcy的操作

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

移动时变化dependcy.gif

代码语言:javascript
复制
/**
 * 确定使用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

代码语言:javascript
复制
/**
 * 当被依赖的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

代码语言:javascript
复制
/**
 * 当被依赖的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;
}
代码语言:javascript
复制
<?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

代码语言:javascript
复制
//添加成员变量
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.布局
代码语言:javascript
复制
<?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

平移

缩放

代码语言:javascript
复制
/**
 * 作者:张风捷特烈<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基本概念就这样,由于并不是非常常用,更深的用法等有需求再深究吧


本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.11.30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 本文内容:
  • 一、简单认识
    • 1.使用
      • 2.string字符串:
        • 3.对应类
        • 二、自定义Behavior
          • 1.既然安卓内部可以玩,那么我们也可以自定义玩玩
            • 2.模仿安卓内置behavior
              • 3.使用结果:
                • 4.关于几个参数
                  • 三、对第一个Behavior的分析
                    • 1.对dependcy的操作
                      • 2.几个重要的参数:
                        • 3.判断dependency位移方向,让child进行联动
                          • 4.移动方向和分度值
                          • 四、自定义FloatingActionButton伴随列表移动
                            • 1.布局
                              • 2.自定义Behavior:
                              相关产品与服务
                              容器服务
                              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档