自定义Behavior(一)

来源:互联网 发布:女程序员物品 编辑:程序博客网 时间:2024/06/08 10:37

CoordinatorLayout,是Material风格的重要组件, 作为布局的顶层控件,协调(Coordinate)其他组件, 实现联动。在我们新建Activity的时候,可以通过ScrollingActivity模板,创建一个标准的CoordinatorLayout布局的Activity,相信大家一定见识过,网上介绍CoordinatorLayout使用方法的文章也有很多,这里我就不再说了。今天想介绍的是CoordinatorLayout的高级用法,自定义Behavior。
还记得那一串字符串吗?

app:layout_behavior="@string/appbar_scrolling_view_behavior" 

其实它并不是一个字符串资源,而是代表了一个类,就是一个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。
在Behavior中有两个概念,dependency和child,很好理解,dependency在这里指的是被依赖的View,child是指需要依赖的View,也就是说,child依赖于dependency,当dependency做出变化时,child可以做出相应的反应。

如何自定义

    public class MyBehavior extends CoordinatorLayout.Behavior<View>{        public MyBehavior(Context context, AttributeSet attrs) {            super(context, attrs);        }    }

一定要重写这个构造函数。因为CoordinatorLayout源码中parseBehavior()函数中直接反射调用这个构造函数。可以看到这里指定了一个泛型,这个泛型其实指定的就是child的泛型,因此,如果没有特殊需求,直接指定view为View就行了。

在任意View中添加:
app:layout_behavior=“完整类名”然后CoordinatorLayout就会反射生成你的Behavior。

另外一种方法如果你的自定义View默认使用一个Behavior。
在你的自定义View类上添加@DefaultBehavior(你的Behavior.class)这句注解。
你的View就默认使用这个Behavior。就像AppBarLayout一样。

@DefaultBehavior(AppBarLayout.Behavior.class)public class AppBarLayout extends LinearLayout {}

生成Behavior后第一件事就是确定依赖关系。重写Behavior的这个方法来确定你依赖哪个View。

@Overridepublic boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {    return dependency.getId() == R.id.first;

我们可以按照两种目的来实现自己的Behavior,当然也可以两种都实现

1.child监听dependency的状态变化,例如大小、位置、显示状态等
2.child监听实现了NestedScrollingChild的接口的dependency的滑动状态
第一种情况需要重写layoutDependsOn和onDependentViewChanged方法
第二种情况需要重写onStartNestedScroll和onNestedPreScroll系列方法

先来说第一种情况:
behavior1.gif

很简单,顶部、底部分别有一个导航栏,右下角一个FloatingActionButton,底部导航栏和FloatingActionButton随着顶部导航栏的变化而变化。

XML文件

<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:layout_width="match_parent"        android:layout_height="wrap_content"        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"        app:elevation="0dp">        <TextView            android:layout_width="match_parent"            android:layout_height="50dp"            android:gravity="center"            android:text="我是TopBar"            app:layout_scrollFlags="scroll|enterAlways" />    </android.support.design.widget.AppBarLayout>    <android.support.v4.widget.NestedScrollView        android:layout_width="match_parent"        android:layout_height="match_parent"        android:scrollbars="none"        app:behavior_overlapTop="30dp"        app:layout_behavior="@string/appbar_scrolling_view_behavior">        <LinearLayout            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:orientation="vertical">            <android.support.v7.widget.CardView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_margin="8dp"                app:cardElevation="8dp"                app:contentPadding="16dp">                <TextView                    android:layout_width="match_parent"                    android:layout_height="wrap_content"                    android:lineSpacingExtra="8dp"                    android:text="@string/content"                    android:textSize="18sp" />            </android.support.v7.widget.CardView>            <android.support.v7.widget.CardView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_margin="8dp"                app:cardElevation="8dp"                app:contentPadding="16dp">                <TextView                    android:layout_width="match_parent"                    android:layout_height="wrap_content"                    android:lineSpacingExtra="8dp"                    android:text="@string/content"                    android:textSize="18sp" />            </android.support.v7.widget.CardView>            <android.support.v7.widget.CardView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_margin="8dp"                app:cardElevation="8dp"                app:contentPadding="16dp">                <TextView                    android:layout_width="match_parent"                    android:layout_height="wrap_content"                    android:lineSpacingExtra="8dp"                    android:text="@string/content"                    android:textSize="18sp" />            </android.support.v7.widget.CardView>        </LinearLayout>    </android.support.v4.widget.NestedScrollView>    <TextView        android:layout_width="match_parent"        android:layout_height="50dp"        android:layout_gravity="bottom"        android:background="@color/colorPrimary"        android:gravity="center"        android:text="我是BottomBar"        app:layout_behavior=".BottomBehavior" />    <android.support.design.widget.FloatingActionButton        android:layout_width="50dp"        android:layout_height="50dp"        android:layout_gravity="bottom|right"        android:layout_marginBottom="70dp"        android:layout_marginRight="20dp"        android:src="@mipmap/ic_launcher_round"        app:backgroundTint="#ffffff"        app:elevation="5dp"        app:layout_behavior=".FloatingBehavior" /></android.support.design.widget.CoordinatorLayout>

分别给FloatingActionButton和底部导航栏设置了
app:layout_behavior=”.FloatingBehavior”和app:layout_behavior=”.BottomBehavior”
AppBarLayout的滑动隐藏显示就不说了

下面来看自定义Behavior的实现

BottomHehavior

public class BottomBehavior extends CoordinatorLayout.Behavior<View> {    public BottomBehavior(Context context, AttributeSet attrs) {        super(context, attrs);    }    @Override    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {        //设置依赖View为AppBarLayout(如果dependency的类型在布局文件中有多个,就不能这样写,而是通过id来判断)        return dependency instanceof AppBarLayout;    }    @Override    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {        float scale = Math.abs(dependency.getY()) / dependency.getHeight();        child.setTranslationY(child.getHeight() * scale );        return true;    }}

实现也很简单,在onDependentViewChanged中根据dependency.getY的变化改变child的TranslationY。FloatingBehavior和这个一样,通过scale 这个值来设置child的scaleX和scaleY即可。

滑动的情况,就留到下次再讲,嗯,就这样。
ps:自定义Behavior是可以使用自定义属性的,就和自定义控件中的用法一样。