Behaviour源码分析

Behaviour如何进行实例化

简单介绍下CoordinatorLayout的LayoutParams每次被子View触发后,就会走到Behaviour的实例化方法,具体看下图。
实例化过程如图所示:


Behaviour源码分析_第1张图片
实例化过程
  1. 关键代码
static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
       ……
        try {
            Map> constructors = sConstructors.get();
            if (constructors == null) {
                constructors = new HashMap<>();
                sConstructors.set(constructors);
            }
            Constructor c = constructors.get(fullName);
            if (c == null) {
                final Class clazz = (Class) 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方法

  1. 在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);
                }
            }
        }
  1. 在每次子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所有的回调地方如图所示


Behaviour源码分析_第2张图片
回调时机

onDependentViewChanged方法

  1. CoordinatorLayout在onChildViewsChanged根据type的状态来决定有可能会调用到。
    其中type的值有:EVENT_PRE_DRAW = 0 ; EVENT_NESTED_SCROLL = 1; EVENT_VIEW_REMOVED = 2;
    由上面的代码可以看出,除了REMOVED状态的以外,其他的都会走onDependentViewChanged的回调。

  2. 调用者可以通过dispatchDependentViewsChanged手动触发View对应的Behaviour的onDependentViewChanged方法。
    :还有部分调用我没讲,大家有兴趣可以自己去源码看下。

onLayoutChild方法

  1. 这个方法明眼人一看就明白是摆放子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);
            }
        }
    }

总结

  1. 关于Behaviour必须依赖在CoordinatorLayout布局里面才会生效,原因从源码分析可知:Behaviour是作为CoordinatorLayout的layoutParams一部分进行实例化的。
  2. 自定义Behaviour的demo教程自定义Behaviour

你可能感兴趣的:(Behaviour源码分析)