自定义Behavior(一)

xiaoxiao2021-02-28  253

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。

@Override public 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系列方法

先来说第一种情况:

很简单,顶部、底部分别有一个导航栏,右下角一个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是可以使用自定义属性的,就和自定义控件中的用法一样。

转载请注明原文地址: https://www.6miu.com/read-33775.html

最新回复(0)