1.CoordinatorLayout是什么?
CoordinatorLayout继承自ViewGroup,并实现了NestedScrollingParent2接口,类似一个功能强大的FrameLayout;
CoordinatorLayout的产生主要有两个主要的目的:
1)在布局文件中作为最顶层的布局容器控件;
2)在CoordinatorLayout中直接为子控件提供特定的交互功能;相当于是给CoordinatorLayout里直接子控件建立依赖关系,使原本相互独立的控件,产生依赖关系,可以实现一个目标控件的改变时,另一个随着改变;
2.Behavior介绍
Behavior实际就是将一些布局的过程以及**Nested Scrolling**的过程暴露出来,利用代理和组合的模式,可以让开发者为CoordinatorLayout添加各种效果插件;
2.1Behavior是什么?
用于CoordinatorLayout子视图的交互行为插件。行为实现一个或多个用户可以在子视图上进行的交互。这些交互可能包括拖动、滑动、甩动或任何其他手势。
2.2Behavior定义
CoordinatorLayout内部类
public static abstract class Behavior<V extends View> {
public Behavior() {
}
public Behavior(Context context, AttributeSet attrs) {
}
...
}
其中有个一个泛型V,它的作用是指定要使用这个Behavior的View的类型,可以是Button、TextView等等。如果希望所有的View都可以使用则指定泛型为View即可;
2.3如何为视图指定Behavior
通过代码我们发现在CoordinatorLayout内部类定义了LayoutParams布局参数类,LayoutParams定义了Behavior行为成员变量,即加入需要为子视图添加Behavior必须是CoordinatorLayout直接子视图,否则无法设置Behavior,正确使用方式例如:
<androidx.coordinatorlayout.widget.CoordinatorLayout>
<TextView app:layout_behavior="...Behavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
CoordinatorLayout子视图布局参数类LayoutParams如下:
CoordinatorLayout内部类
//定义CoordinatorLayout子视图的布局参数
public static class LayoutParams extends MarginLayoutParams {
//定义Behavior
Behavior mBehavior;
//读取配置文件的layout_behavior参数Behavior
LayoutParams(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
final TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CoordinatorLayout_Layout);
//判断是否有Behavior属性
mBehaviorResolved = a.hasValue(
R.styleable.CoordinatorLayout_Layout_layout_behavior);
//解析Behavior
if (mBehaviorResolved) {
mBehavior = parseBehavior(context, attrs, a.getString(
R.styleable.CoordinatorLayout_Layout_layout_behavior));
}
a.recycle();
//通知已经为子视图设置Behavior行为
if (mBehavior != null) {
// If we have a Behavior, dispatch that it has been attached
mBehavior.onAttachedToLayoutParams(this);
}
}
//通过代码设置Behavior
public void setBehavior(@Nullable Behavior behavior) {
if (mBehavior != behavior) {
if (mBehavior != null) {
// 首先通知CoordinatorLayout子视图移除旧的Behavior
mBehavior.onDetachedFromLayoutParams();
}
//CoordinatorLayout子视图设置新的Behavior
mBehavior = behavior;
mBehaviorTag = null;
mBehaviorResolved = true;
//
if (behavior != null) {
// 通知CoordinatorLayout子视图绑定了新的Behavior
behavior.onAttachedToLayoutParams(this);
}
}
}
}
在LayoutParams类里面定义了Behavior成员变量mBehavior;
2.3.1通过配置文件指定
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"></TextView>
通过app:layout_behavior指定实现Behavior类的行为,在读取子视图xml配置参数时读取实现Behavior类行为类全路径(即LayoutParams构造函数内读取配置的Behavior: mBehavior = parseBehavior(context, attrs, a.getString(
R.styleable.CoordinatorLayout_Layout_layout_behavior)););
注意:appbar_scrolling_view_behavior对应的是Behavior的类全路径
<string name="appbar_scrolling_view_behavior" translatable="false">com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior</string>
2.3.2通过CoordinatorLayout内部类LayoutParams类setBehavior()方法设置
setBehavior()可以设置新的Behavior或者null移除Behavior;
示例代码:
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams)findViewById(R.id.tv_behavior).getLayoutParams();
layoutParams.setBehavior(CoordinatorLayout.Behavior);
2.4自定义Behavior关键方法介绍
自定义Behavior关键方法可以简单理解为两部分控制视图布局和视图行为监听控制(**Nested Scrolling**的过程);
2.4.1Behavior控制视图布局
layoutDependsOn确认Behavior关联的视图依赖的视图:
public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull V child,
@NonNull View dependency) {
return false;
}
负责查询使用该Behavior的View要监听哪个类型的View状态变化,如果是则返回true,那么之后其他操作就会围绕这个依赖视图而进行了;parent代表CoordinatorLayout,child代表使用该Behavior关联的View,dependency代表要监听的View;
例如:系统类com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
// We depend on any AppBarLayouts
return dependency instanceof AppBarLayout;
}
设置ScrollingViewBehavior的View依赖项是AppBarLayout布局则返回true,那么之后其他操作就会围绕这个依赖视图dependency进行;
onLayoutChild帮助Behavior控制的CoordinatorLayout子视图布局:
public boolean onLayoutChild(@NonNull CoordinatorLayout parent, @NonNull V child,
int layoutDirection) {
return false;
}
当CoordinatorLayout准备布局的时候调用onLayoutChild方法,负责对被Behavior控制的视图进行布局,就是将ViewGroup的onLayout针对该视图的部分抽出来给Behavior处理;返回true表示Behavior代理CoordinatorLayout处理孩子视图的布局,false请求默认布局Behavior(CoordinatorLayout的onLayout方法继续处理);
例如:系统类com.google.android.material.appbar.ViewOffsetBehavior
@Override
public boolean onLayoutChild(
@NonNull CoordinatorLayout parent, @NonNull V child, int layoutDirection) {
// First let lay the child out
layoutChild(parent, child, layoutDirection);
if (viewOffsetHelper == null) {
viewOffsetHelper = new ViewOffsetHelper(child);
}
viewOffsetHelper.onViewLayout();
viewOffsetHelper.applyOffsets();
if (tempTopBottomOffset != 0) {
viewOffsetHelper.setTopAndBottomOffset(tempTopBottomOffset);
tempTopBottomOffset = 0;
}
if (tempLeftRightOffset != 0) {
viewOffsetHelper.setLeftAndRightOffset(tempLeftRightOffset);
tempLeftRightOffset = 0;
}
return true;
}
返回true表示Behavior代理CoordinatorLayout完成Behavior关联的孩子视图的布局,false请求默认布局Behavior(CoordinatorLayout的onLayout方法继续处理);
onDependentViewChanged确认关联Behavior视图依赖的CoordinatorLayout下子视图的位置和尺寸是否发生变化
public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull V child,
@NonNull View dependency) {
return false;
}
根据依赖视图进行调整的方法,当依赖视图发生尺寸大小和位置变化时,这个方法就会调用;返回true表示Behavior更新了CoordinatorLayout关联Behavior子视图的位置或尺寸,false未改变CoordinatorLayout关联Behavior子视图的位置或尺寸;
2.4.2Behavior视图行为监听控制(几个关键方法回调实现)
Behavior实际上是NestedScrollingParent2(NSP)的一个代理