红橙Darren视频笔记 Behavior的工作原理源码分析

主要coordinatorlayout的代码来自coordinatorlayout-1.0.0-sources.jar
本文从源码介绍 CoordinatorLayout 的 behavior 怎么工作的

1 behavior的使用方法

不管是自定义behavior还是使用Android自带的behavior, 放的都是全类名,且外面包含CoordinatorLayout ,behavior的所在xml节点必须是CoordinatorLayout 的直接子布局(不能隔层嵌套) Behavior 是layout_behavior 只能由 CoordinatorLayout的LayoutParams去解析

2 behavior如何实例化

2.1调用 parseBehavior 解析我们设置的 layout_behavior
2.2获取设置好的全类名 xxx.xx.xx 还可以是 .xx
2.3通过类名获取 class 然后获取两个参数的构造方法
2.4通过反射创建 Behavior 对象 newInatence() , 把所有的 behavior 放入了集合 以备之后使用
源码证明1-2.4
利用
红橙Darren视频笔记 CoordinatorLayout:实现自定义behavior
一文的demo 在TranslationBehavior的构造函数打下断点 观察调用
在CoordinatorLayout generateLayoutParams中会调用parseBehavior
CoordinatorLayout.java

  		//CoordinatorLayout的LayoutParams继承自MarginLayoutParams 这里是它的构造方法
		//这里会解析CoordinatorLayout的直接子view 确认他们的属性 因此说只能用在直接子view上
		LayoutParams(@NonNull Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);

            final TypedArray a = context.obtainStyledAttributes(attrs,
                    R.styleable.CoordinatorLayout_Layout);

            this.gravity = a.getInteger(
                    R.styleable.CoordinatorLayout_Layout_android_layout_gravity,
                    Gravity.NO_GRAVITY);
            mAnchorId = a.getResourceId(R.styleable.CoordinatorLayout_Layout_layout_anchor,
                    View.NO_ID);
            this.anchorGravity = a.getInteger(
                    R.styleable.CoordinatorLayout_Layout_layout_anchorGravity,
                    Gravity.NO_GRAVITY);

            this.keyline = a.getInteger(R.styleable.CoordinatorLayout_Layout_layout_keyline,
                    -1);

            insetEdge = a.getInt(R.styleable.CoordinatorLayout_Layout_layout_insetEdge, 0);
            dodgeInsetEdges = a.getInt(
                    R.styleable.CoordinatorLayout_Layout_layout_dodgeInsetEdges, 0);
            mBehaviorResolved = a.hasValue(
                    R.styleable.CoordinatorLayout_Layout_layout_behavior);
            //重点 判断子view是否有behavior属性
            if (mBehaviorResolved) {
                //这里得到了实例 需要跟进去 确认它如何实例化的
                mBehavior = parseBehavior(context, attrs, a.getString(
                        R.styleable.CoordinatorLayout_Layout_layout_behavior));
            }
            a.recycle();

            if (mBehavior != null) {
                // If we have a Behavior, dispatch that it has been attached
                mBehavior.onAttachedToLayoutParams(this);
            }
        }


	//sConstructors起缓存作用
	static final ThreadLocal<Map<String, Constructor<Behavior>>> sConstructors =
            new ThreadLocal<>();

	static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
        if (TextUtils.isEmpty(name)) {
            return null;
        }

        final String fullName;
        if (name.startsWith(".")) {
            // Relative to the app package. Prepend the app package name.
            fullName = context.getPackageName() + name;//如果在xml的写法类似app:layout_behavior=".TranslationBehavior"
            //那么 加上包名补全类的全名
        } else if (name.indexOf('.') >= 0) {
            // Fully qualified package name.
            fullName = name;//写的就是类的全名
        } else {
            // Assume stock behavior in this package (if we have one)
            fullName = !TextUtils.isEmpty(WIDGET_PACKAGE_NAME)
                    ? (WIDGET_PACKAGE_NAME + '.' + name)
                    : name;
        }

        try {
            //尝试获取构造函数
            Map<String, Constructor<Behavior>> constructors = sConstructors.get();
            if (constructors == null) {
                //没有获取到就重新创建
                constructors = new HashMap<>();
                sConstructors.set(constructors);
            }
            //根据类的全名取出Behavior的构造函数
            Constructor<Behavior> c = constructors.get(fullName);
            if (c == null) {
                //利用反射 根据Behavior的全类名 得到class
                final Class<Behavior> clazz = (Class<Behavior>) context.getClassLoader()
                        .loadClass(fullName);
                //获取类的两个参数的构造函数
                c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
                c.setAccessible(true);
                //将构造函数存储起来 以备下次使用 key是全类名 value是构造函数
                constructors.put(fullName, c);
            }
            //利用反射创建behavior实例
            return c.newInstance(context, attrs);
        } catch (Exception e) {
            throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
        }
    }

    //两参构造函数的参数
	static final Class<?>[] CONSTRUCTOR_PARAMS = new Class<?>[] {
            Context.class,
            AttributeSet.class
    };

3 CoordinatorLayout 如何与RecyclerView 联动 即behavior如何监听到滚动

同样 在上面的demo中的TranslationBehavior.onStartNestedScroll中断点 观察调用堆栈
这一段用源码说明容易造成混乱 我直接写下我的调查结果:
CoordinatorLayout.dispatchTouchEvent
->CoordinatorLayout.dispatchTransformedTouchEvent
->child.dispatchTouchEvent(event);(这里的child是recyclerView 断点可以看出类型)
->RecyclerView.onInterceptTouchEvent(ev);
->RecyclerView.startNestedScroll
->RecyclerView.getScrollingChildHelper().startNestedScroll(axes, type);
->NestedScrollingChildHelper.startNestedScroll()
->ViewParentCompat.onStartNestedScroll(p, child, mView, axes, type)
->ViewParentCompat.onStartNestedScroll
->((NestedScrollingParent2) parent).onStartNestedScroll(child, target,
nestedScrollAxes, type);
->CoordinatorLayout.onStartNestedScroll
->viewBehavior.onStartNestedScroll(this, view, child,
target, axes, type);
总结下来顺序如下:
CoordinatorLayout处理事件分发 分发给子view RecyclerView,子view RecyclerView接受到手势调用startNestedScroll并利用NestedScrollingChildHelper告诉CoordinatorLayout子view RecyclerView开始scroll CoordinatorLayout则利用内部类告诉其他包含Behavior属性的布局RecyclerView开始Scroll了
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值