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