Android CoordinatorLayout自定义Behavior笔记

参考放在首位

Behavior参考博客
Behavior参考博客

Behavior抽象类

 public abstract static class Behavior<V extends View> {
        public Behavior() {
        }

        public Behavior(Context context, AttributeSet attrs) {
        }

        public void onAttachedToLayoutParams(@NonNull CoordinatorLayout.LayoutParams params) {
        }
        //......省去众多方法
    }

Behavior是CoordinatorLayout的一个内部泛型抽象类。内部类中指定的view类型规定了哪种类型的view的可以使用才Behavior。因此,如果没有特殊需求,直接指定view为View就行了。

方法简介
  • onInterceptTouchEvent():是否拦截触摸事件
  • onTouchEvent():处理触摸事件
  • layoutDependsOn():确定使用Behavior的View要依赖的View的类型
  • onDependentViewChanged():当被依赖的View状态改变时回调
  • onDependentViewRemoved():当被依赖的View移除时回调
  • onMeasureChild():测量使用Behavior的View尺寸
  • onLayoutChild():确定使用Behavior的View位置
  • onStartNestedScroll():嵌套滑动开始(ACTION_DOWN),确定Behavior是否要监听此次事件
  • onStopNestedScroll():嵌套滑动结束(ACTION_UP或ACTION_CANCEL)
  • onNestedScroll():嵌套滑动进行中,要监听的子 View的滑动事件已经被消费
  • onNestedPreScroll():嵌套滑动进行中,要监听的子 View将要滑动,滑动事件即将被消费(但最终被谁消费,可以通过代码控制)
  • onNestedFling():要监听的子 View在快速滑动中
  • onNestedPreFling():要监听的子View即将快速滑动
    (来源第一个链接,偷懒直接复制的)
方法使用情况

使用大概分为两种情况:

  1. 某个view需要根据监听另一个的行为来控制自己的行为,这个时候我们需要重写2个方法:
public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) {
            return false;
}

public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
            return false;
}
  1. 我们的view需要根据监听CoordinatorLayout中的子view的滚动行为来改变自己的状态,现在我们就需要重写下面的方法了:
    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull TextView child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {

        return true;
    }


    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull TextView child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
    }

示例

demo示例图片

图中布局分两个部分,头部和列表部分。上滑的时候头部向上偏移,下滑的时候头部向下偏移出现。
这里头部就只使用一个TextView。

自定义Behavior步骤

  1. 自定义Behavior。(demo继承CoordinatorLayout.Behavior)
  2. 添加字符串资源。(如:< string name=“behavior_sample_footer”>demo.com.SampleHeaderBehavior</ string>)
  3. 在布局中添加 app:layout_behavior属性。

1.自定义SampleHeaderBehavior

public class SampleHeaderBehavior extends CoordinatorLayout.Behavior<TextView> {

    public SampleHeaderBehavior() {
    }

    public SampleHeaderBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }


    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull TextView child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {

        return true;
    }


    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull TextView child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
        //上滑
        if (dy > 0) {
            float offest = child.getY() - dy;
            if (Math.abs(offest) > child.getHeight()) {

                offest = -child.getHeight();
            } else {
                //头部没有完全消失,偏移让CoordinatorLayout消费
                consumed[1] = dy;
            }
            child.setTranslationY(offest);
        }

        //下滑
        if (dy < 0) {
            float offest = child.getY() - dy;
            if (offest > 0) {
                offest = 0;
            }
            child.setTranslationY(offest);
        }

    }
}

  • onNestedPreScroll方法:返回true将会回调onNestedPreScroll方法。
  • onNestedPreScroll方法:child是要设置Behavior的view,target为要监听的view。
    因为头部是根据recyclerview的滑动来改变自身位置的,所以用到了这两个方法
    dy为手指移动的距离,头部最大偏移距离为-height,最小距离为0。
    consumed[1] = dy表示滑动事件由CoordinatorLayout消费
    可以看出,下滑的时候滑动是完全交给recyclerview的,所以效果是如果recyclerview下滑的同时,头部也在向下偏移。如果代码修改如下:
  //下滑
        if (dy < 0) {
            float offest = child.getY() - dy;
            if (offest > 0) {
                offest = 0;
            } else {
                consumed[1] = dy;
            }
            child.setTranslationY(offest);
        }

能想象出是什么效果吗?

自定义RecyclerViewBehavior

要让recyclerview一直在头部的下方。

public class RecyclerViewBehavior extends CoordinatorLayout.Behavior<RecyclerView> {

    public RecyclerViewBehavior() {
    }

    public RecyclerViewBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, RecyclerView child, View dependency) {

        return dependency instanceof TextView;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, RecyclerView child, View dependency) {
        //计算列表y坐标,最小为0
        float y = dependency.getHeight() + dependency.getTranslationY();
        if (y < 0) {
            y = 0;
        }
        child.setY(y);
        return true;
    }
}

因为是根据头部的位置,来确定自身的位置,所以用了这两个方法
child就是要设置Behavior的recyclerview,根据头部位置,确定它的位置。

2.添加字符串资源

<string name="behavior_sample_header">com.example.custombehaviordemo.custumizes.SampleHeaderBehavior</string>
<string name="behavior_recyclerview">com.example.custombehaviordemo.custumizes.RecyclerViewBehavior</string>

只需要在string.xml文件夹中添加Behavior类的全路径。

3.xml布局添加Behavior属性

<?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">


    <TextView
        android:id="@+id/header"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:background="#ff0000"
        android:gravity="center"
        android:text="Hello World"
        android:textColor="#ffffff"
        android:textSize="18sp"
        app:layout_behavior="@string/behavior_sample_header" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_list"
        android:layout_width="match_parent"
        app:layout_behavior="@string/behavior_recyclerview"
        android:layout_height="wrap_content" />


</android.support.design.widget.CoordinatorLayout>

因为自定义Behavior的时候继承的是CoordinatorLayout.Behavior,所以外层父布局,用的是CoordinatorLayout。为什么CoordinatorLayout可以使用Behavior?可以看看CoordinatorLayout源码分析和自定义类似的ViewGroup的系列文章(我还没看 )。

Activity中直接正常使用。

省略代码。。。。。

最后提示:应该是个人逻辑能力问题,方法调用会理解,但是关于位置偏移的处理理解较慢。虽然花些时间,但是学会了自己想学的东西。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值