「Behavior」使用Behavior“协调”布局,实现酷炫交互

CoordinatorLayout 从诞生之初起,因其特殊的"协调"手段,非常适用于实现复杂的视图交互和动画,比如响应滚动事件、展开或折叠工具栏(Toolbar)等,一直以来被广大Android开发者津津乐道。而实现这一关键效果,正是仰仗CoordinatorLayout.Behavior。本文尝试追本溯源,详细讲讲Behavior到底是个啥?我该怎么用?

Behavior是什么

Android团队是这么定义Behavior的:

Behavior:CoordinatorLayout 子视图的交互行为插件。Behavior 实现了用户可以对子视图进行的一个或多个交互操作。这些交互操作可能包括拖拽、滑动、甩动或任何其他手势。

Behavior是一个抽象类,除开构造方法外,其主要包含以下几个方法:

  • onAttachedToLayoutParams(CoordinatorLayout.LayoutParams params)onDetachedFromLayoutParams()

对称式API,CoordinatorLayout调用setBehavior()方法设置behavior 时被调用,通知对应的behavior它所持有的LayoutParams实例已被 绑定/分离 。

  • onInterceptTouchEvent/ onTouchEvent

熟悉事件分发机制应该懂这两个Api的含义:是否要拦截触摸事件 以及 如何消费事件。

  • getScrimColor(CoordinatorLayout parent, V child) / getScrimOpacity(CoordinatorLayout parent, V child)

介绍这两个API前,先介绍一个前端交互上常见的概念,遮罩(Scrim):UI设计为了凸显用户当前操作/交互的元素时,往往会给该元素上面或下面盖上一层带颜色的图层,这个图层我们称之为Scrim。CoordinatorLayout作为侧重交互效果的布局容器,自然也有这个特性,对应的API就是behavior中的这两个方法,一个设置遮罩颜色,一个设置遮罩的透明度。CoordiantorLayout中的遮罩位于元素下方作为背景呈现。

  • public boolean blocksInteractionBelow(CoordinatorLayout parent, V child)

从方法名不难看出,该方法是用来决定是否要阻止位于给定child视图下方的View的交互行为的,默认当getScrimOpacity()大于0时,拦截该child视图下方的View的交互行为。

  • public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency)

指定一个视图是否依赖于 CoordinatorLayout 中的另一个视图。通过这种方式,开发者可以定义复杂的布局行为和交互模式,其中一个视图的状态或位置变化可以影响到另一个视图。三个参数分别对应父 CoordinatorLayout 的实例、子视图(child)的实例以及另一个可能被依赖的视图(dependency)的实例。它返回一个布尔值,true则表示子视图依赖于指定的视图。

  • public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency)

layoutDependsOn 方法并返回 true 表示存在依赖关系时,每当依赖视图的位置、大小或其他属性发生变化,onDependentViewChanged 方法就会被调用。这为开发者提供了一个机会,可以根据依赖视图的变化来调整当前视图(child view)的状态、位置等属性。 返回一个 boolean 值,表示子视图是否因为依赖视图的变化而发生了改变。如果返回 true,则表示子视图发生了改变,这将导致布局的再次计算和重绘。如果返回 false,则表示子视图没有因依赖视图的变化而发生改变,不需要重新布局或绘制。

  • public void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency)

onDependentViewChanged方法类似,不同的是该方法在依赖视图从CoordinatorLayout中移除时被调用。这个回调提供了一个机会,让开发者可以响应依赖视图的移除事件,执行一些清理工作或者更新当前视图(child view)的状态、位置等。

  • onMeasureChild() / onLayoutChild()

这两个方法用于在测量阶段允许开发者介入子视图(即行为关联的视图)的测量/布局过程。通过重写这两个方法,可以自定义子视图的测量/布局逻辑,从而实现特定的布局需求或调整。


Behavior中同样可以对嵌套滚动进行处理,包含了onStartNestedScrollonStartNestedScrollonNestedScrollAcceptedonStopNestedScrollonNestedScrollonNestedPreScrollonNestedFlingonNestedPreFling等方法,他们的含义和NestedScrollingParent2NestedScrollingParent3接口中的含义一致,此处不做额外介绍。

Android中内置的Behavior

Android中预定义几种Behavior,用以实现各种复杂的交互逻辑,以下是一些常见的内置Behavior

  1. FloatingActionButton.Behavior:这个行为是为 FloatingActionButton(浮动操作按钮)设计的。它能够响应 Snackbar 的出现,自动移动浮动按钮以避免遮挡 Snackbar。

  2. SwipeDismissBehavior:这是一个通用行为,可以用于任何 View。它为视图添加了滑动以消失的交互模式,类似于 Snackbar 的滑动消失效果。

  3. AppBarLayout.ScrollingViewBehavior:这个行为专为与 AppBarLayout 一起使用的滚动视图(如 RecyclerView 或 NestedScrollView)设计。它确保滚动视图和 AppBarLayout 的滚动行为协同工作,实现材料设计的滚动效果。

  4. AppBarLayout.Behavior:这是 AppBarLayout 的默认行为,支持滚动手势,使得 AppBarLayout 可以响应内容滚动,实现折叠工具栏的效果。

  5. BottomSheetBehavior:这个行为用于 BottomSheet,即从屏幕底部滑出的面板。BottomSheetBehavior 允许用户通过拖动或滑动来展开或隐藏底部面板。

  6. CollapsingToolbarLayout.Behavior:这是为 CollapsingToolbarLayout 设计的行为,它可以在滚动时实现复杂的折叠动画效果,通常与 AppBarLayout 结合使用。

  7. ... ...

介绍完了API,可以尝试一下自定义Behavior练练手。

定制自己的Behavior

作为CoordinatorLayout协调布局实现的关键,Behavior实现的交互效果大致可以分为两类:

  1. 协调子View之间的关系:重写layoutDependsOnonDependentViewChanged等方法,建立View间的依赖关系,每当被依赖视图(dependency)的位置、大小或其他属性发生变化时调整当前视图(child)的状态、位置等属性。
  2. 协调嵌套嵌套滑动:重写嵌套滑动相关API实现不同的交互。

接下来我们从具体需求出发,一个一个来看。

协调子View之间的关系:避让BottomSheetBehavior的Behavior

我们来实现这样一个Behavior:当设置了BottomSheetBehavior的View弹出时,自动避让该View(dependency),隐藏View(child)。 代码如下:

kotlin 复制代码
/**
 * 根据BottomSheetBehavior控制View的显示隐藏以避让BottomSheet
 */
class VisibilityByBottomSheetBehavior(
	context: Context,
	attributeSet: AttributeSet,
): CoordinatorLayout.Behavior<View>(context, attributeSet) {

	override fun layoutDependsOn(
		parent: CoordinatorLayout,
		child: View,
		dependency: View
	): Boolean {
		// 如果依赖的View是BottomSheet,则建立依赖关系
		return isBottomSheet(dependency)
	}

	override fun onDependentViewChanged(
		parent: CoordinatorLayout,
		child: View,
		dependency: View
	): Boolean {
		if (isBottomSheet(dependency)) {
			// 如果依赖的View的顶部位置大于等于child的底部位置,则隐藏child
			child.isVisible = dependency.top >= child.bottom
		}
		// 返回false,布局的不会再次计算和重绘
		return false
	}

	private fun isBottomSheet(view: View): Boolean {
		val lp = view.layoutParams
		return if (lp is CoordinatorLayout.LayoutParams) {
			lp.behavior is BottomSheetBehavior<*>
		} else false
	}
}

实现的效果如下:

简简单单几行代码,就实现了根据BottomSheetBehavior控制View的显示隐藏,达到避让BottomSheet的目的。

协调嵌套滑动

如果熟悉Android中嵌套滚动的相关接口(NestedScrollingParentNestedScrollingChild)的话,看到CoordinatorLayout.Behavior中的onStartNestedScrollonStartNestedScrollonNestedScrollAcceptedonStopNestedScrollonNestedScrollonNestedPreScrollonNestedFlingonNestedPreFling等方法一定不陌生,没错,CoordinatorLayout也是基于这套机制通过Behavior来协调嵌套滑动的。 不熟悉这套机制的,可以先移步熟悉这套机制:
NestedScrollParent2

自定义Behavior实现各种炫酷交互

熟悉了Behavior基础用法后,就可以使用灵活运动这套机制实现各种炫酷交互,网上相关案例也非常多,基本都是仿写各种炫酷交互,这里引用几个:
通过来模仿稀土掘金个人页面的布局来学习使用 CoordinatorLayout
一起动才够嗨!Android CoordinatorLayout 自定义 Behavior
自定义 Behavior,实现嵌套滑动、平滑切换周月视图的日历

以后UI交互提出什么复杂的交互效果时,不妨换个思路,尝试自定义Behavior来实现,也许能事半功倍。

相关推荐
GEEKVIP6 分钟前
手机使用技巧:8 个 Android 锁屏移除工具 [解锁 Android]
android·macos·ios·智能手机·电脑·手机·iphone
model20052 小时前
android + tflite 分类APP开发-2
android·分类·tflite
彭于晏6892 小时前
Android广播
android·java·开发语言
与衫3 小时前
掌握嵌套子查询:复杂 SQL 中 * 列的准确表列关系
android·javascript·sql
500了9 小时前
Kotlin基本知识
android·开发语言·kotlin
人工智能的苟富贵10 小时前
Android Debug Bridge(ADB)完全指南
android·adb
小雨cc5566ru15 小时前
uniapp+Android面向网络学习的时间管理工具软件 微信小程序
android·微信小程序·uni-app
bianshaopeng16 小时前
android 原生加载pdf
android·pdf
hhzz16 小时前
Linux Shell编程快速入门以及案例(Linux一键批量启动、停止、重启Jar包Shell脚本)
android·linux·jar
火红的小辣椒17 小时前
XSS基础
android·web安全