CoordinatorLayout高级用法-自定义Behavior

在新的support design中,CoordinatorLayout可以说是最重要的一个控件了,CoordinatorLayout给我们带来了一种新的事件的处理方式——behavior,你是不是还记得我们在使用CoordinatorLayout的时候,一些子view需要一段,

app:layout_behavior="@string/appbar_scrolling_view_behavior"

这样的xml配置?当时我们不知道这是干嘛的,直接照用就行了,后来发现这玩意是一个类!而且我们还可以自定义!所以,今天这篇博客我们首先来学习一下如何自定义Behavior,之后的博客可能会看一下CoordinatorLayout是怎么处理这个Behavior的。

认识Behavior

Behavior是CoordinatorLayout的一个抽象内部类

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

      public Behavior(Context context, AttributeSet attrs) {
      }
      ...
}

有一个泛型是指定的我们应用这个Behavior的View的类型,例如上面的appbar_scrolling_view_behavior对应的字符串其实是android.support.design.widget.AppBarLayout$ScrollingViewBehavior,这个ScrollingViewBehavior内部类指定的泛型是View,所以理论上这个Behavior我们任何的View都可以使用,我们在自定义的时候,如果不是特殊的行为,也可以直接指定泛型View

在自定义Behavior的时候,我们需要关心的两组四个方法,为什么分为两组呢?看一下下面两种情况

  1. 某个view监听另一个view的状态变化,例如大小、位置、显示状态等
  2. 某个view监听CoordinatorLayout里的滑动状态

对于第一种情况,我们关心的是:

layoutDependsOnonDependentViewChanged方法,

对于第二种情况,我们关心的是:

onStartNestedScrollonNestedPreScroll方法。

对于这几个方法什么意思,我们需要干什么,稍候我们就能了解到。

初步自定义

现在我们就来根据第一种情况尝试自定义一个Behavior,这里我们实现一个简单的效果,让一个View根据另一个View上下移动。

首先我们来自定义一个Behavior,起名为DependentBehavior

public class DependentBehavior extends CoordinatorLayout.Behavior<View> {

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

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return super.layoutDependsOn(parent, child, dependency);
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        ViewCompat.offsetLeftAndRight();
        return super.onDependentViewChanged(parent, child, dependency);
    }
}

注意一下,带有参数的这个构造必须要重载,因为在CoordinatorLayout里利用反射去获取这个Behavior的时候就是拿的这个构造。我们覆写了两个方法layoutDependsOnonDependentViewChanged,这两个方法的参数都是一样的,解释一下,第一个不用说,就是当前的CoordinatorLayout,第二个参数是我们设置这个Behavior的View,第三个是我们关心的那个View。如何知道关心的哪个呢?layoutDependsOn的返回值决定了一切!

这里我们关心一个TextView好了,所以layoutDependsOn可以这么写,

@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
    return dependency instanceof TextView;
}

现在设置好了关心谁,接下来就是在这个View状态发生变化的时候,我们现在的View该做些什么了,恩,这里肯定是在onDependentViewChanged做工作了。我们的任务就是获取dependency距离底部的距离,并且设置给child,很简单。

@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
    int offset = dependency.getTop() - child.getTop();
    ViewCompat.offsetTopAndBottom(child, offset);
    return true;
}

首先我们先获取两个View的top值的差,然后让child的位置位移一下就ok啦,如此简单,那这个简单的Behavior如何用呢?

<android.support.design.widget.CoordinatorLayout
    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:fitsSystemWindows="true"
    tools:context="org.loader.mybehavior.MainActivity">

    <TextView
        android:id="@+id/depentent"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#FFFF0000"
        android:gravity="center"
        android:textColor="@android:color/white"
        android:layout_gravity="top|left"
        android:text="depentent"/>

    <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#FF00FF00"
        android:gravity="center"
        android:textColor="@android:color/white"
        android:layout_gravity="top|right"
        app:layout_behavior="org.loader.mybehavior.DependentBehavior"
        android:text="auto"/>

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

注意,第二个TextView我们设置了app:layout_behavior="org.loader.mybehavior.DependentBehavior"

值正好是我们定义的那个DependentBehavior

final TextView depentent = (TextView) findViewById(R.id.depentent);
depentent.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ViewCompat.offsetTopAndBottom(v, 5);
    }
});

在Activity中,我们每次点击第一个TextView都会去改变一下它的位置,下面让我们来看看另一个TextView的位置改变了没有。

Scroll Behavior

在学会了如何自定义Behavior后,我们接着来实现上面说的第二种情况-滑动。为了演示这种Behavior的定义,我们还是来做个无用功,让一个ScrollView跟随另一个ScrollView滑动。恩,先来看看效果吧,

从效果中我们可以看出,第二个ScrollView明显是是在跟随第一个进行滑动,现在就让我们用自定义Behavior的形式实现它。

创建一个Behavior,起名叫ScrollBehavior,


public class ScrollBehavior extends CoordinatorLayout.Behavior<View> {

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

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
        return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
    }

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

    @Override
    public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) {
        return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
    }
}

和你想的一样,我们覆写了onStartNestedScrollonNestedPreScroll方法,但是除了这两个方法外,我们还覆写了onNestedPreFling方法,这个方法是干嘛的? 估计大家已经猜出来了,这里是处理fling动作的,你想想,我们在滑动松开手的时候,ScrollView是不是还继续滑动一会,那我们也需要让跟随的那个ScrollView也要继续滑动一会,这种效果,onNestedPreFling就派上用场了。

好,接下来我们来实现代码,首先来看看onStartNestedScroll,这里的返回值表明这次滑动我们要不要关心,我们要关心什么样的滑动?当然是y轴方向上的。

@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
    return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
}

现在我们准备好了关心的滑动事件了,那如何让它滑动起来呢?还是要看onNestedPreScroll的实现

@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
    super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
    int leftScrolled = target.getScrollY();
    child.setScrollY(leftScrolled);
}

也很简单,让child的scrollY的值等于目标的scrollY的值就ok啦,那fling呢?更简单,

@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY, boolean consumed) {
    ((NestedScrollView) child).fling((int)velocityY);
    return true;
}

直接将现在的y轴上的速度传递传递给child,让他fling起来就ok了。

定义好了Behavior,就得在xml中使用了,使用方法和前面的一样。

<?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:fitsSystemWindows="true"
    android:orientation="vertical">

    <android.support.v4.widget.NestedScrollView
        android:layout_gravity="left"
        android:layout_width="wrap_content"
        android:background="#FF00FF00"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentLeft"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentLeft"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentLeft"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentLeft"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentLeft"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentLeft"/>

        </LinearLayout>

    </android.support.v4.widget.NestedScrollView>

    <android.support.v4.widget.NestedScrollView
        android:layout_gravity="right"
        android:layout_width="wrap_content"
        android:background="#FFFF0000"
        android:layout_height="match_parent"
        app:layout_behavior="org.loader.mybehavior.ScrollBehavior">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentRight"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentRight"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentRight"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentRight"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentRight"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentRight"/>

        </LinearLayout>

    </android.support.v4.widget.NestedScrollView>

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

第二个ScrollView的layout_behavior我们指定为org.loader.mybehavior.ScrollBehavior,现在就可以看到上面的效果了。

ok, 最后是文章中demo的代码下载:http://download.csdn.net/detail/qibin0506/9352989

时间: 2024-08-04 00:25:43

CoordinatorLayout高级用法-自定义Behavior的相关文章

十五天精通WCF——第九天 高级玩法之自定义Behavior

原文:十五天精通WCF--第九天 高级玩法之自定义Behavior 终于我又看完了二期爱情保卫战,太酸爽了,推荐链接:http://www.iqiyi.com/a_19rrgublqh.html?vfm=2008_aldbd,不多说,谁看谁入迷,下面言归正传, 看看这个很有意思的Behavior. 一: Behavior这个泼妇的厉害   在前面的文章中,我也清楚的说明了整个wcf通信流,而Behavior这个泼妇可以在wcf通信流中的任何地方插上一脚,蛮狠无比,利用的好,让你上天堂,利用的不

Newtonsoft.Json高级用法 1.忽略某些属性 2.默认值的处理 3.空值的处理 4.支持非公共成员 5.日期处理 6.自定义序列化的字段名称

手机端应用讲究速度快,体验好.刚好手头上的一个项目服务端接口有性能问题,需要进行优化.在接口多次修改中,实体添加了很多字段用于中间计算或者存储,然后最终用Newtonsoft.Json进行序列化返回数据,经过分析一个简单的列表接口每一行数据返回了16个字段,但是手机APP端只用到了其中7个字段,剩余9个字段的数据全部都是多余的,如果接口返回数据为40K大小,也就是说大约20K的数据为无效数据,3G网络下20K下载差不多需要1s,不返回无效数据至少可以节约1s的时间,大大提高用户体验.本篇将为大家

Newtonsoft.Json高级用法

手机端应用讲究速度快,体验好.刚好手头上的一个项目服务端接口有性能问题,需要进行优化.在接口多次修改中,实体添加了很多字段用于中间计算或者存储,然后最终用Newtonsoft.Json进行序列化返回数据,经过分析一个简单的列表接口每一行数据返回了16个字段,但是手机APP端只用到了其中7个字段,剩余9个字段的数据全部都是多余的,如果接口返回数据为40K大小,也就是说大约20K的数据为无效数据,3G网络下20K下载差不多需要1s,不返回无效数据至少可以节约1s的时间,大大提高用户体验.本篇将为大家

Git log高级用法

格式化Log输出 首先,这篇文章会展示几种git log格式化输出的例子.大多数例子只是通过标记向git log请求或多或少的信息. 如果你不喜欢默认的git log格式,你可以用git config的别名功能来给你想要的格式创建一个快捷方式. Oneline --oneline标记把每一个提交压缩到了一行中.它默认只显示提交ID和提交信息的第一行.git log --oneline的输出一般是这样的: 0e25143 Merge branch 'feature' ad8621a Fix a b

Android(java)学习笔记264:Android下的属性动画高级用法(Property Animation)

1. 大家好,在上一篇文章当中,我们学习了Android属性动画的基本用法,当然也是最常用的一些用法,这些用法足以覆盖我们平时大多情况下的动画需求了.但是,正如上篇文章当中所说到的,属性动画对补间动画进行了很大幅度的改进,之前补间动画可以做到的属性动画也能做到,补间动画做不到的现在属性动画也可以做到了.因此,今天我们就来学习一下属性动画的高级用法,看看如何实现一些补间动画所无法实现的功能. 2. ValueAnimator的高级用法: 在上篇文章中介绍补间动画缺点的时候有提到过,补间动画是只能对

Newtonsoft.Json 高级用法

基本用法 Json.NET是支持序列化和反序列化DataTable,DataSet,Entity Framework和Entity的.下面分别举例说明序列化和反序列化. DataTable: //序列化DataTable DataTable dt = new DataTable(); dt.Columns.Add("Age", Type.GetType("System.Int32")); dt.Columns.Add("Name", Type.G

git log 高级用法

转自:https://github.com/geeeeeeeeek/git-recipes/wiki/5.3-Git-log%E9%AB%98%E7%BA%A7%E7%94%A8%E6%B3%95 内容很详细.实用. 这是一篇在原文(BY atlassian)基础上演绎的译文.除非另行注明,页面上所有内容采用知识共享-署名(CC BY 2.5 AU)协议共享. 每一个版本控制系统的出现都是为了让你记录代码的变化.你可以看到项目的历史记录--谁贡献了什么.bug是什么时候引入的,还可以撤回有问题的

再谈Newtonsoft.Json高级用法

上一篇Newtonsoft.Json高级用法发布以后收到挺多回复的,本篇将分享几点挺有用的知识点和最近项目中用到的一个新点进行说明,做为对上篇文章的补充. 阅读目录 动态改变属性序列化名称 枚举值序列化问题 全局设置 总结 回到顶部 动态改变属性序列化名称 "动态改变属性序列化名称"顾名思义:在不同场景下实体字段序列化后字段名称不同,比如有下面实体A,正常序列化后json为{"Id":"123"} public class A { public

Python 内置函数sorted()有哪些高级用法?

本文和大家分享的主要是python内置函数sorted()的相关内容,一起来看看吧,希望对大家学习python http://www.maiziedu.com/land/python/有所帮助. 1.对于 Python 内置函数 sorted() ,先拿来跟list(列表)中的成员函数 list.sort() 进行下对比.在本质上,list的排序和内建函数sorted的排序是差不多的,连参数都基本上是一样的. 2.主要的区别在于, list.sort() 是对已经存在的列表进行操作,进而可以改变