上篇文章介绍了CoordinatorLayout
的基本使用 — A super-powered FrameLayout—协调布局CoordinatorLayout(一):深度基本了解
本篇文章说一说Behavior
- 是什么
- 怎么写
- 用在哪
一、Behavior
官方解释
/**
* Interaction behavior plugin for child views of {@link CoordinatorLayout}.
*
* <p>A Behavior implements one or more interactions that a user can take on a child view.
* These interactions may include drags, swipes, flings, or any other gestures.</p>
*
* @param <V> The View type that this Behavior operates on
*/
CoordinatorLayout直接子view之间相互作用行为的插件.
行为实现了一个或多个用户可以在子view上进行的交互.
这些交互可能包括拖动、滑动、甩动或任何其他手势.@param <V> 此行为操作的view类型.
二、自定义Behavior
2.1 初识 CoordinatorLayout.Behavior
方法是真的多,就这还删除了被标记@Deprecated
的方法,后面会挑捡些重要的方法着重说明
public static abstract class Behavior<V extends View> {
public Behavior() {}
public Behavior(Context context, AttributeSet attrs) {}
public void onAttachedToLayoutParams(@NonNull CoordinatorLayout.LayoutParams params) {}
public void onDetachedFromLayoutParams() {}
public boolean onInterceptTouchEvent(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull MotionEvent ev) { return false; }
public boolean onTouchEvent(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull MotionEvent ev) { return false; }
@ColorInt
public int getScrimColor(@NonNull CoordinatorLayout parent, @NonNull V child) { return Color.BLACK; }
@FloatRange(from = 0, to = 1)
public float getScrimOpacity(@NonNull CoordinatorLayout parent, @NonNull V child) { return 0.f; }
public boolean blocksInteractionBelow(@NonNull CoordinatorLayout parent, @NonNull V child) { return getScrimOpacity(parent, child) > 0.f; }
public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull View dependency) { return false; }
public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull View dependency) { return false; }
public void onDependentViewRemoved(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull View dependency) { }
public boolean onMeasureChild(@NonNull CoordinatorLayout parent, @NonNull V child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { return false; }
public boolean onLayoutChild(@NonNull CoordinatorLayout parent, @NonNull V child, int layoutDirection) { return false; }
public static void setTag(@NonNull View child, @Nullable Object tag) { }
public static Object getTag(@NonNull View child) { }
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, @ScrollAxis int axes, @NestedScrollType int type) { }
public void onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target,@ScrollAxis int axes, @NestedScrollType int type) { }
public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, @NestedScrollType int type) { }
public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @NestedScrollType int type, @NonNull int[] consumed) { }
public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, @NestedScrollType int type) { }
public boolean onNestedFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, float velocityX, float velocityY, boolean consumed) { return false; }
public boolean onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, float velocityX, float velocityY) { return false; }
public WindowInsetsCompat onApplyWindowInsets(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull WindowInsetsCompat insets) { return insets; }
public boolean onRequestChildRectangleOnScreen(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull Rect rectangle, boolean immediate) { return false; }
public void onRestoreInstanceState(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull Parcelable state) {// no-op}
public Parcelable onSaveInstanceState(@NonNull CoordinatorLayout parent, @NonNull V child) { return BaseSavedState.EMPTY_STATE; }
public boolean getInsetDodgeRect(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull Rect rect) { return false; }
}
2.2 CoordinatorLayout.Behavior<V extends View>
的泛型、方法说明
2.2.1 泛型说明
官方释义:此Behavior操作的view类型.
这个Behavior是用在哪种类型的View
上,TextView
、ImageView
、Button
等等都可以,如果确定了某一种类型,就需要用在对应的那一种类型上;或者直接使用View
作为泛型类型,通吃。
2.2.2 方法说明
-
Behavior()无参构造方法
用于手动实例化 -
Behavior(Context context, AttributeSet attrs)2个参构造方法
如需再xml中设置,需要重写 -
void onAttachedToLayoutParams(CoordinatorLayout.LayoutParams params)
当 Behavior 已附加到 LayoutParams 实例时调用
-void onDetachedFromLayoutParams()
当 Behavior 与其持有的 LayoutParams 实例分离时调用 -
boolean onInterceptTouchEvent(CoordinatorLayout parent,V child,MotionEvent ev)
拦截CoordinatorLayout 触摸事件,在CoordinatorLayout 触摸事件分发到子view之前响应,如果此行为想要拦截并接管事件流,则为 true。默认返回 false -
boolean onTouchEvent(CoordinatorLayout parent, V child,MotionEvent ev)
一旦 Behavior 拦截到触摸事件,则事件流将被发送到此方法;如果Behavior处理此触摸事件并希望继续接收此流中的事件,则为 true。默认返回 false -
boolean layoutDependsOn(CoordinatorLayout parent, V child,View dependency)
设置此Behavior的view需要依赖的view -
boolean onDependentViewChanged(CoordinatorLayout parent, V child,View dependency)
当依赖view的大小或位置在发生变化时,调用此方法。 -
void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency)
当依赖的view从父视图中删除后调用此方法 -
onMeasureChild(CoordinatorLayout parent, V child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)
当父 CoordinatorLayout 即将测量给定的子视图时调用,此方法可用于对子视图执行自定义或修改的测量,以代替默认的子测量行为 -
boolean onLayoutChild(CoordinatorLayout parent, V child,int layoutDirection)
此方法可用于执行子视图的自定义或修改布局,以代替默认的子布局行为 -
boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,V child, View directTargetChild, View target, @ScrollAxis int axes, @NestedScrollType int type)
嵌套滑动开始,如果behavior消费此次嵌套滑动,返回 true -
void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout,V child, View directTargetChild, View target,@ScrollAxis int axes, @NestedScrollType int type)
当 CoordinatorLayout 接受嵌套滚动时调用 -
void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, @NestedScrollType int type)
当嵌套滚动结束时调用 -
void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @NestedScrollType int type, @NonNull int[] consumed)
嵌套滑动进行中,要监听的子 View的滑动事件已经被消费 -
void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed, @NestedScrollType int type)
嵌套滑动进行中,要监听的子 View将要滑动,滑动事件即将被消费 -
boolean onNestedFling(CoordinatorLayout coordinatorLayout,V child, View target, float velocityX, float velocityY,boolean consumed)
当嵌套滚动子项开始投掷或将是“投掷”的动作时调用 -
boolean onNestedPreFling( CoordinatorLayout coordinatorLayout,V child, View target, float velocityX, float velocityY)
当嵌套滚动子项即将开始“投掷”时调用
2.3 自定义CoordinatorLayout.Behavior<V extends View>
2.3.1 明确需求,自定义Behavior一般情况下分为
-
此view随着依赖view的状态(位置等)变化而变化
class CustomBehavior : CoordinatorLayout.Behavior<View> {
companion object {
private const val TAG = "mr_gu"
}
// 列表顶部和title底部重合时,列表的滑动距离。
private var deltaY = 0f
constructor()
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
/**
* 实现Behavior必须要要复写的方法
*/
override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
Log.i(TAG, "layoutDependsOn: ")
return dependency is NestedScrollView
}
/**
* 当依赖的view状态发生变化时,设置此behavior的view应当作出的变化
*
* 这里实现的是,当NestedScrollView的位置变化时,设置此behavior的view的随着变化
*/
override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
if (deltaY == 0f) {
deltaY = dependency.y - child.height
}
var dy = dependency.y - child.height
dy = if (dy < 0) 0f else dy
val y = -(dy / deltaY) * child.height
child.translationY = y
Log.i(TAG, "onDependentViewChanged: ")
return true
}
}
-
此view随着依赖view的滑动状态变化而变化
// 滑动状态透明度为0.5f,停止滑动透明度1f
class CustomBehavior : CoordinatorLayout.Behavior<View> {
companion object {
private const val TAG = "mr_gu"
}
// 列表顶部和title底部重合时,列表的滑动距离。
private var deltaY = 0f
constructor()
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
/**
* 实现Behavior必须要要复写的方法
*/
override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
Log.i(TAG, "layoutDependsOn: ")
return dependency is NestedScrollView
}
override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: View,
directTargetChild: View, target: View, axes: Int, type: Int): Boolean {
Log.i(TAG, "onStartNestedScroll: ")
// 只分发垂直滑动
return axes == ViewCompat.SCROLL_AXIS_VERTICAL
}
override fun onStopNestedScroll(coordinatorLayout: CoordinatorLayout, child: View, target: View, type: Int) {
super.onStopNestedScroll(coordinatorLayout, child, target, type)
child.visibility = View.VISIBLE
child.alpha = 1f
Log.i(TAG, "onStopNestedScroll: ")
}
override fun onNestedScroll(coordinatorLayout: CoordinatorLayout, child: View, target: View, dxConsumed: Int,
dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int, consumed: IntArray) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed, type, consumed)
Log.i(TAG, "onNestedScroll: ")
// 也可以拿到当次滑动的距离
child.alpha = 0.5f
}
}
-
1 + 2
三、总结与思考
使用Behavior可以较为方便的实现页面中view间相互作用的效果,文章中的例子都是最简单的效果,面对设计小姐姐天马行空的设计和想法,Behavior可能会帮助你很好的应对,不至于扎耳挠腮,骂骂咧咧,还容易掉头发
Behavior是底层逻辑是什么样的,为什么在xml中简单的配置个Behavior
属性就可以实现,答案都在源码中,read the fucking resource code ,can help you grow quickly
【本文参考】
SheHuan —— Android CoordinatorLayout之自定义Behavior
如果文章对你有帮助,点个赞再走呗
如果文章中存在错误,还望评论区指出
一起成长,共同进步