Behaviour源码分析
2019-11-01 本文已影响0人
Magic旭
Behaviour如何进行实例化
简单介绍下CoordinatorLayout的LayoutParams每次被子View触发后,就会走到Behaviour的实例化方法,具体看下图。
实例化过程如图所示:
实例化过程
- 关键代码
static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
……
try {
Map<String, Constructor<Behavior>> constructors = sConstructors.get();
if (constructors == null) {
constructors = new HashMap<>();
sConstructors.set(constructors);
}
Constructor<Behavior> c = constructors.get(fullName);
if (c == null) {
final Class<Behavior> clazz = (Class<Behavior>) Class.forName(fullName, true,
context.getClassLoader());
c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
c.setAccessible(true);
constructors.put(fullName, c);
}
return c.newInstance(context, attrs);
} catch (Exception e) {
throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
}
}
layoutDependsOn方法
- 在CoordinatorLayout初始化时候,会在prepareChildren方法里面遍历子View,其中layoutDependsOn的返回值会作为是否添加依赖关系的判断。
private void prepareChildren() {
……
// Now iterate again over the other children, adding any dependencies to the graph
for (int j = 0; j < count; j++) {
if (j == i) {
//自己和自己不会有依赖关系
continue;
}
final View other = getChildAt(j);
//dependsOn方法里会有layoutDependsOn方法作为判断条件之一
if (lp.dependsOn(this, view, other)) {
if (!mChildDag.contains(other)) {
// Make sure that the other node is added
mChildDag.addNode(other);
}
// Now add the dependency to the graph
mChildDag.addEdge(other, view);
}
}
}
- 在每次子View发生变化时,CoordinatorLayout都会遍历子View,给其对应的Behaviour回调一次layoutDependsOn方法,确保有依赖关系的View能进行相应的变化。
final void onChildViewsChanged(@DispatchChangeEvent final int type) {
………………………………
for (int i = 0; i < childCount; i++) {
final View child = mDependencySortedChildren.get(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (type == EVENT_PRE_DRAW && child.getVisibility() == View.GONE) {
// Do not try to update GONE child views in pre draw updates.
continue;
}
………………………………
// Update any behavior-dependent views for the change
for (int j = i + 1; j < childCount; j++) {
final View checkChild = mDependencySortedChildren.get(j);
final LayoutParams checkLp = (LayoutParams) checkChild.getLayoutParams();
final Behavior b = checkLp.getBehavior();
if (b != null && b.layoutDependsOn(this, checkChild, child)) {
if (type == EVENT_PRE_DRAW && checkLp.getChangedAfterNestedScroll()) {
// If this is from a pre-draw and we have already been changed
// from a nested scroll, skip the dispatch and reset the flag
checkLp.resetChangedAfterNestedScroll();
continue;
}
final boolean handled;
switch (type) {
case EVENT_VIEW_REMOVED:
// EVENT_VIEW_REMOVED means that we need to dispatch
// onDependentViewRemoved() instead
b.onDependentViewRemoved(this, checkChild, child);
handled = true;
break;
default:
//告知依赖checkChild,被依赖的child有可能发生变化了
// Otherwise we dispatch onDependentViewChanged()
handled = b.onDependentViewChanged(this, checkChild, child);
break;
}
if (type == EVENT_NESTED_SCROLL) {
// If this is from a nested scroll, set the flag so that we may skip
// any resulting onPreDraw dispatch (if needed)
checkLp.setChangedAfterNestedScroll(handled);
}
}
}
}
……
}
2-1. onChildViewsChanged所有的回调地方如图所示
回调时机
onDependentViewChanged方法
-
CoordinatorLayout在onChildViewsChanged根据type的状态来决定有可能会调用到。
其中type的值有:EVENT_PRE_DRAW = 0 ; EVENT_NESTED_SCROLL = 1; EVENT_VIEW_REMOVED = 2;
由上面的代码可以看出,除了REMOVED状态的以外,其他的都会走onDependentViewChanged的回调。 -
调用者可以通过dispatchDependentViewsChanged手动触发View对应的Behaviour的onDependentViewChanged方法。
注:还有部分调用我没讲,大家有兴趣可以自己去源码看下。
onLayoutChild方法
- 这个方法明眼人一看就明白是摆放子View位置的,所以这个方法的回调也依赖于CoordinatorLayout的onLayout方法,当onLayout方法执行时候,会遍历自身所有的子View,获取他们对应的behaviour值,然后回调behaviour的onLayoutChild方法。
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int layoutDirection = ViewCompat.getLayoutDirection(this);
final int childCount = mDependencySortedChildren.size();
for (int i = 0; i < childCount; i++) {
final View child = mDependencySortedChildren.get(i);
if (child.getVisibility() == GONE) {
// If the child is GONE, skip...
continue;
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final Behavior behavior = lp.getBehavior();
if (behavior == null || !behavior.onLayoutChild(this, child, layoutDirection)) {
onLayoutChild(child, layoutDirection);
}
}
}
总结
- 关于Behaviour必须依赖在CoordinatorLayout布局里面才会生效,原因从源码分析可知:Behaviour是作为CoordinatorLayout的layoutParams一部分进行实例化的。
- 自定义Behaviour的demo教程自定义Behaviour