RelativeLayout的onMeasure源码分析

都知道RelativeLayout的一次测量调用两次子视图测量循环

横向一次 纵向一次

带着目的, 我们来分析源码

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mDirtyHierarchy) {
            mDirtyHierarchy = false;
            sortChildren();
        }
一上来就是重要代码! 

如果布局层次是脏的(无效、或过时失效) 那么sortChildren!!!! 这个方法是简历Relative布局关系的核心

    private void sortChildren() {
        final int count = getChildCount();
		//懒加载初始化两个View数组 分别存放横向 纵向
        if (mSortedVerticalChildren == null || mSortedVerticalChildren.length != count) {
            mSortedVerticalChildren = new View[count];
        }

        if (mSortedHorizontalChildren == null || mSortedHorizontalChildren.length != count) {
            mSortedHorizontalChildren = new View[count];
        }

        final DependencyGraph graph = mGraph;  //描述关系的graph
		
        graph.clear();
		//初始 清空graph
        for (int i = 0; i < count; i++) {
            graph.add(getChildAt(i)); //先都add收集
        }

        graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL); //再把两个方向分别sort
        graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
    }
CoordinatorLayout里的DAG这里没有使用, 而是用了一个内部类DependencyGraph描述graph
    private static class DependencyGraph {
  

mNodes存放所有一级子View 

        /**
         * List of all views in the graph. 
         */
        private ArrayList<Node> mNodes = new ArrayList<Node>();
mKeyNodes存放有id的一级子View

        /**
         * List of nodes in the graph. Each node is identified by its
         * view id (see View#getId()).
         */
        private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
mRoots临时数据结构,用于有序的放置目标的View数组,注意:这里的数据结构是ArrayDeque 双端队列 为什么要用这个数据结构 在getSortedViews里会讲到
        /**
         * Temporary data structure used to build the list of roots
         * for this graph.
         */
        private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();

循环收集“”一级”子View的方法 add(注意是一级,二级里加dependency xml也会给你报错!)

        /**
         * Adds a view to the graph.  把view都收集在graph里
         *
         * @param view The view to be added as a node to the graph.
         */
        void add(View view) {
            final int id = view.getId();
            final Node node = Node.acquire(view); //内部类Node描述节点

            if (id != View.NO_ID) {
                mKeyNodes.put(id, node);  //所有有id的View都会被存放在mKeyNodes里,因为有可能被依赖
            }

            mNodes.add(node); //所有的子View都收集在mNodes里
        }
核心方法getSortedViews排序后放入View数组里
        /**
         * Builds a sorted list of views. The sorting order depends on the dependencies
         * between the view. For instance, if view C needs view A to be processed first
         * and view A needs view B to be processed first, the dependency graph
         * is: B -> A -> C. The sorted array will contain views B, A and C in this order.
         * 创建有序的view数组。“序”取决于View之间的dependencies关系。
         * 例如,如果viewC的布局需要先viewA先处理,而viewA的布局又需要先viewB先处理 
         * 那么依赖图就是:B -> A -> C 有序数组将这么排序:{viewB,viewA, viewC}。
         *
         * @param sorted The sorted list of views. The length of this array must
         *        be equal to getChildCount().
         * @param rules The list of rules to take into account.
         */
        void getSortedViews(View[] sorted, int... rules) {
            final ArrayDeque<Node> roots = findRoots(rules); //找出所有的没有依赖的node 就是root
            int index = 0;

            Node node;
			// 啥是pollLast: 获取并移除此列表尾部的最后一个元素
            while ((node = roots.pollLast()) != null) { //把roots根节点依次pollLast出来
                final View view = node.view;
                final int key = view.getId();

                sorted[index++] = view; //然后设置到数组的对应位置
                //是不是搞错了 为什么只加了根节点?别急
                // 在这个方法内部 所有的根节点rootA被设置完之后,
                //实际上会把(被rootA直接依赖的)rootB加在roots尾部,进行下一次while循环...
                //直到这个rootN没有被依赖的root了,才会while到下一个findRoots方法出来的无依赖root

                final ArrayMap<Node, DependencyGraph> dependents = node.dependents;
                final int count = dependents.size(); //找到这个node的被依赖树graph
                for (int i = 0; i < count; i++) {
                    final Node dependent = dependents.keyAt(i);
                    final SparseArray<Node> dependencies = dependent.dependencies;

                    dependencies.remove(key); //优化,移除掉处理过的root
                    if (dependencies.size() == 0) { //把被依赖树的顶层(也就是当前root的直接被依赖)
                    //作为下一次while循环的root对象 add到roots的尾部
                        roots.add(dependent);
                    }
                }
            }

            if (index < sorted.length) { //如果数组没设置满就跳出了while循环,
            //说明有循环依赖! 源码是会抛出异常的!!&
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Android View 是 Android 中最基本的 UI 构建块之一,负责在屏幕上绘制视图并响应用户的操作。下面是一个简单的 View 源码分析过程: 1. 首先,我们需要了解 View 的继承关系。View 是 Android 中所有 UI 组件的基类,它的直接子类包括 ViewGroup、TextView、ImageView 等。其中,ViewGroup 又是各种布局容器的基类,例如 LinearLayout、RelativeLayout 等。 2. 接着,我们可以查看 View 的基本属性。这些属性包括 layout_width、layout_height、padding、background 等。其中,layout_width 和 layout_height 决定了 View 在布局中的大小,padding 指定了 View 的内边距,background 则是 View 的背景。 3. View 的绘制过程可以分为两个阶段:测量和绘制。在测量阶段,View 会根据其 layout_width 和 layout_height 等属性计算出自身的尺寸。在绘制阶段,View 会将自身绘制到屏幕上。 4. View 的事件响应机制是 Android 中 UI 开发的重要部分。当用户触摸屏幕时,系统会将事件传递给 View,View 会根据自身的点击区域判断是否响应该事件,并将事件传递给其父容器或下一个 View 进行处理。 5. 最后,我们可以查看 View 的源码实现,深入了解 View 的内部实现逻辑。例如,View 的测量和绘制过程是通过 onMeasure 和 onDraw 方法实现的,事件响应机制是通过 onTouchEvent 和 dispatchTouchEvent 方法实现的。 总的来说,理解 Android View 的源码实现可以帮助我们更好地理解 Android UI 开发的工作原理,从而编写出更高效、更灵活、更具交互性的应用程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值