Behaviour源码分析

作者: Magic旭 | 来源:发表于2019-11-01 17:39 被阅读0次

    Behaviour如何进行实例化

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


    实例化过程
    1. 关键代码
    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方法

    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所有的回调地方如图所示


    回调时机

    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源码分析

        本文链接:https://www.haomeiwen.com/subject/pfzfbctx.html