美文网首页Android自定义View
Android View 相关源码分析之五 RelativeLa

Android View 相关源码分析之五 RelativeLa

作者: 又似在水一方 | 来源:发表于2016-12-12 22:54 被阅读464次

    Android View 相关源码分析之一 从setContentView说起

    Android View 相关源码分析之二 继LayoutInflater来说

    Android View 相关源码分析之三 View的绘制过程

    Android View 相关源码分析之四 LinearLayout源码分析

    RelativeLayout 源码分析

    继承自ViewGroup 没有重载onDraw方法 内部子View又是相对 只要计算出View的坐标 layout过程同样简单

    measure过程

    主要过程

    1. 将内部View根据纵向关系和横向关系排序
    2. 初始化相关变量
    3. 遍历水平关系的View
    4. 遍历竖直关系的View
    5. baseline计算
    6. 宽度和高度修正

    1 将内部View根据纵向关系和横向关系排序

    layout_toRightOf 为横向关系 layout_below为纵向关系

        //首先会根据mDirtyHierarchy的值判断是否需要将子View重新排序
        if (mDirtyHierarchy) {
            mDirtyHierarchy = false;
            sortChildren();
        }
    

    相关调用方法

        //mDirtyHierarchy的值只有在requestLayout方法下被更新
        public void requestLayout() {
            super.requestLayout();
            mDirtyHierarchy = true;
        }
    
        //sortChildren()方法对横向纵向关系的view的数组进行非空判断 用DependencyGraph进行判断
        private void sortChildren() {
            final int count = getChildCount();
            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.clear();
    
            for (int i = 0; i < count; i++) {
                graph.add(getChildAt(i));
            }
    
            graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL);
            graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
        }
    

    DependencyGraph的相关方法

        private static class DependencyGraph {
            ...
            /**
             * Adds a view to the graph.
             *
             * @param view The view to be added as a node to the graph.
             */
            void add(View view) {
                //因为是图 根据view生成一个节点
                final int id = view.getId();
                final Node node = Node.acquire(view);
                //如果是有效的id 则将该节点添加到List中
                if (id != View.NO_ID) {
                    mKeyNodes.put(id, node);
                }
    
                mNodes.add(node);
            }     
    
              /**
               * 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.
               *
               * @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) {
                //当前View找不到其它的可依赖的View时  作为root节点
                  final ArrayDeque<Node> roots = findRoots(rules);
                  int index = 0;
    
                  Node node;
                  //读取root的下一个node
                  while ((node = roots.pollLast()) != null) {
                      final View view = node.view;
                      final int key = view.getId();
    
                  //将符合规则的View加到 sorted中
    
                      sorted[index++] = view;
    
                      final ArrayMap<Node, DependencyGraph> dependents = node.dependents;
                      //dependents 依赖该node的node  (A C依赖B 则B的dependents中存A C)
                      final int count = dependents.size();
                      //遍历所有依赖自己的node
                      for (int i = 0; i < count; i++) {
                          final Node dependent = dependents.keyAt(i);
                          //dependencies 是被依赖的的node的规则和node(A 依赖 B D 则dependencies存有B D )
                          final SparseArray<Node> dependencies = dependent.dependencies;
    
                          //移除当前node和dependencies的依赖关系
                          dependencies.remove(key);
                          //如果解除依赖后没有其它依赖 则将该node也视为rootNode
                          if (dependencies.size() == 0) {
                              roots.add(dependent);
                          }
                      }
                  }
    
                  if (index < sorted.length) {
                      throw new IllegalStateException("Circular dependencies cannot exist in RelativeLayout");
                  }
              }
      ...
    }
    

    eg: A依赖B B依赖C 首先存入C 因为不依赖任何其它的

    
            /**
             * Finds the roots of the graph. A root is a node with no dependency and
             * with [0..n] dependents.
             *
             * @param rulesFilter The list of rules to consider when building the
             *        dependencies
             *
             * @return A list of node, each being a root of the graph
             */
            private ArrayDeque<Node> findRoots(int[] rulesFilter) {
              //keyNodes为nodelist
                final SparseArray<Node> keyNodes = mKeyNodes;
                final ArrayList<Node> nodes = mNodes;
                final int count = nodes.size();
    
              //初始化依赖该node的node和该node依赖的node相关参数
                for (int i = 0; i < count; i++) {
                    final Node node = nodes.get(i);
                    node.dependents.clear();
                    node.dependencies.clear();
                }
    
    
                //遍历所有node  存入当前view和他所依赖的关系
                for (int i = 0; i < count; i++) {
                    final Node node = nodes.get(i);
    
                    final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
                    //取出当前View所有的依赖关系
                    final int[] rules = layoutParams.mRules;
                    final int rulesCount = rulesFilter.length;
    
                    //遍历当前View所有的
                    for (int j = 0; j < rulesCount; j++) {
                      //rule对应被依赖view的id
                        final int rule = rules[rulesFilter[j]];
                        if (rule > 0) {
                            //找到被依赖的node
                            final Node dependency = keyNodes.get(rule);
                            //跳过空view和本身
                            if (dependency == null || dependency == node) {
                                continue;
                            }
                            //添加依赖被依赖的node
                            dependency.dependents.put(node, this);
                            node.dependencies.put(rule, dependency);
                        }
                    }
                }
    
                final ArrayDeque<Node> roots = mRoots;
                roots.clear();
    
                // 再次遍历  如果该node的依赖关系为0 即该view不依赖任何view 则视为rootView
                for (int i = 0; i < count; i++) {
                    final Node node = nodes.get(i);
                    if (node.dependencies.size() == 0) roots.addLast(node);
                }
    
                return roots;
            }
    

    2 初始化相关变量

      int myWidth = -1;
      int myHeight = -1;
    
      int width = 0;
      int height = 0;
    
      final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
      final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
      final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
      final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    
      // 如果不是UNSPECIFIED模式 则将widthSize赋值于myWidth
      if (widthMode != MeasureSpec.UNSPECIFIED) {
          myWidth = widthSize;
      }
      // 如果不是UNSPECIFIED模式 则将heightSize赋值于myHeight
      if (heightMode != MeasureSpec.UNSPECIFIED) {
          myHeight = heightSize;
      }
      //如果是EXACTLY模式 则将myWidth和myHeight记录
      if (widthMode == MeasureSpec.EXACTLY) {
          width = myWidth;
      }
    
      if (heightMode == MeasureSpec.EXACTLY) {
          height = myHeight;
      }
    
      View ignore = null;
      //判断是否为Start 和  top 确定左上角坐标
      int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
      final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;
      gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
      final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
    
      int left = Integer.MAX_VALUE;
      int top = Integer.MAX_VALUE;
      int right = Integer.MIN_VALUE;
      int bottom = Integer.MIN_VALUE;
    
      boolean offsetHorizontalAxis = false;
      boolean offsetVerticalAxis = false;
      // 记录ignore的view
      if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
          ignore = findViewById(mIgnoreGravity);
      }
      //宽度个高度是否为warp模式
      final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
      final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
    
      //在计算和分配的子View的坐标的时候 需要用到父VIew的尺寸 但是暂时无法拿到准确值(待完成下面操作)
      //先使用默认值代替 在计算后 用偏移量更新真是坐标
      final int layoutDirection = getLayoutDirection();
      if (isLayoutRtl() && myWidth == -1) {
          myWidth = DEFAULT_WIDTH;
      }
    

    3 遍历水平关系的View

        View[] views = mSortedHorizontalChildren;
        int count = views.length;
    
        for (int i = 0; i < count; i++) {
            View child = views[i];
            if (child.getVisibility() != GONE) {
                LayoutParams params = (LayoutParams) child.getLayoutParams();
                //根据方向获得子View中设置的规则
                int[] rules = params.getRules(layoutDirection);
                //将左右方向规则转换为左右的坐标
                applyHorizontalSizeRules(params, myWidth, rules);
                //测算水平方向的子View的尺寸
                measureChildHorizontal(child, params, myWidth, myHeight);
                //确定水平方向子View的位置
                if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
                    offsetHorizontalAxis = true;
                }
            }
        }
    
    

    相关方法

        private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
            RelativeLayout.LayoutParams anchorParams;
            childParams.mLeft = VALUE_NOT_SET;
            childParams.mRight = VALUE_NOT_SET;
            //得到当前子View的layout_toLeftOf属性对应的View
            anchorParams = getRelatedViewParams(rules, LEFT_OF);
            if (anchorParams != null) {
              //如果这个属性存在 则当前子View的右坐标是layout_toLeftOf对应的view的左坐标减去对应view的marginLeft的值和自身marginRight的值
                childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
                        childParams.rightMargin);
            //如果alignWithParent为true alignWithParent取alignWithParentIfMissing
            //如果layout_toLeftOf的view为空 或者gone 则将RelativeLayout当做被依赖的对象
            } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
                //如果父容器RelativeLayout的宽度大于0
                //则子View的右坐标为 父RelativeLayout的宽度减去 mPaddingRight 和自身的marginRight
                if (myWidth >= 0) {
                    childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
                }
            }
    
            //类似的方法 得到左坐标(通过参数RIGHT_OF)
            anchorParams = getRelatedViewParams(rules, RIGHT_OF);
            if (anchorParams != null) {
                childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
                        childParams.leftMargin);
            } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
                childParams.mLeft = mPaddingLeft + childParams.leftMargin;
            }
            //类似的方法 得到左坐标 (通过参数ALIGN_LEFT)
            anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
            if (anchorParams != null) {
                childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
            } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
                childParams.mLeft = mPaddingLeft + childParams.leftMargin;
            }
            //类似的方法 得到右坐标 (通过参数ALIGN_RIGHT)
            anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
            if (anchorParams != null) {
                childParams.mRight = anchorParams.mRight - childParams.rightMargin;
            } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
                if (myWidth >= 0) {
                    childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
                }
            }
            //根据ALIGN_PARENT_LEFT 将自己放到父RelativeLayout的左边
            if (0 != rules[ALIGN_PARENT_LEFT]) {
                childParams.mLeft = mPaddingLeft + childParams.leftMargin;
            }
            //根据ALIGN_PARENT_RIGHT 将自己放到父RelativeLayout的右边
            if (0 != rules[ALIGN_PARENT_RIGHT]) {
                if (myWidth >= 0) {
                    childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
                }
            }
        }
    
    private void measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight) {
      //获得child的宽度MeasureSpec
        final int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight,
                params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight,
                myWidth);
    
        final int childHeightMeasureSpec;
        //在低于4.2的时候 mAllowBrokenMeasureSpecs为true
        //当myHeight < 0 时 则根据父RelativeLayout设置其MeasureSpec模式
        if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
            //如果父RelativeLayout的height大于0  则 设置子view的MeasureSpec模式为EXACTLY
            if (params.height >= 0) {
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                        params.height, MeasureSpec.EXACTLY);
            } else {
                //反之 如果其小于0  则设置子View的MeasureSpec为UNSPECIFIED
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
            }
        } else {
          //当当前myHeight >= 0
          //判断当前高度是否与父RelativeLayout高度相同 设置heightMode
          //根据maxHeight 和heightMode设置子View的MeasureSpec模式
            final int maxHeight;
            if (mMeasureVerticalWithPaddingMargin) {
                maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom
                        - params.topMargin - params.bottomMargin);
            } else {
                maxHeight = Math.max(0, myHeight);
            }
            final int heightMode;
            if (params.height == LayoutParams.MATCH_PARENT) {
                heightMode = MeasureSpec.EXACTLY;
            } else {
                heightMode = MeasureSpec.AT_MOST;
            }
            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, heightMode);
        }
        //获得了子View的WidthMeasureSpec和HeightMeasureSpec
        //子View可以通过measure方法获取自身的size
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
    
    /**
        * Get a measure spec that accounts for all of the constraints on this view.
        * This includes size constraints imposed by the RelativeLayout as well as
        * the View's desired dimension.
        *
        * @param childStart The left or top field of the child's layout params
        * @param childEnd The right or bottom field of the child's layout params
        * @param childSize The child's desired size (the width or height field of
        *        the child's layout params)
        * @param startMargin The left or top margin
        * @param endMargin The right or bottom margin
        * @param startPadding mPaddingLeft or mPaddingTop
        * @param endPadding mPaddingRight or mPaddingBottom
        * @param mySize The width or height of this view (the RelativeLayout)
        * @return MeasureSpec for the child
        */
       private int getChildMeasureSpec(int childStart, int childEnd,
               int childSize, int startMargin, int endMargin, int startPadding,
               int endPadding, int mySize) {
           int childSpecMode = 0;
           int childSpecSize = 0;
    
           final boolean isUnspecified = mySize < 0;
           //如果父RelativeLayout宽度小于0 版本号不小于4.2
           if (isUnspecified && !mAllowBrokenMeasureSpecs) {
                //如果子View的左边距和右边距都不为VALUE_NOT_SET
                //且右边距坐标大于左边距坐标 则将其差当做宽度赋予View 设置模式为EXACTLY
                //VALUE_NOT_SET = Integer.MIN_VALUE
                /**
                 * Constant for the minimum {@code int} value, -2<sup>31</sup>.
                 */
                //public static final int MIN_VALUE = 0x80000000;
               if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
                   childSpecSize = Math.max(0, childEnd - childStart);
                   childSpecMode = MeasureSpec.EXACTLY;
               } else if (childSize >= 0) {
                   // 如果childSpecSize >= 0 则赋值于childSpecSize
                   //同样设置模式为EXACTLY
                   childSpecSize = childSize;
                   childSpecMode = MeasureSpec.EXACTLY;
               } else {
                   // 都不满足则设置模式为UNSPECIFIED
                   childSpecSize = 0;
                   childSpecMode = MeasureSpec.UNSPECIFIED;
               }
    
               return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
           }
    
           // 计算 开始和结束相关
           int tempStart = childStart;
           int tempEnd = childEnd;
    
           //如果没有指定start值 则默认赋予 padding和merage的值
           if (tempStart == VALUE_NOT_SET) {
               tempStart = startPadding + startMargin;
           }
           //同上
           if (tempEnd == VALUE_NOT_SET) {
               tempEnd = mySize - endPadding - endMargin;
           }
    
           //指定最大可提供的大小
           final int maxAvailable = tempEnd - tempStart;
    
           if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
               //如果Start和End都是有效值 根据isUnspecified设置specMode为UNSPECIFIED或EXACTLY
               //并将设置对应的size
               childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
               childSpecSize = Math.max(0, maxAvailable);
           } else {
              //反之 判断childSize的相关值
               if (childSize >= 0) {
                   //设置模式为EXACTLY
                   //判断maxAvailable和childSize情况 取较大值设置为childSpecSize
                   childSpecMode = MeasureSpec.EXACTLY;
    
                   if (maxAvailable >= 0) {
                       // We have a maximum size in this dimension.
                       childSpecSize = Math.min(maxAvailable, childSize);
                   } else {
                       // We can grow in this dimension.
                       childSpecSize = childSize;
                   }
               } else if (childSize == LayoutParams.MATCH_PARENT) {
                   //如果子View是match模式 参照isUnspecified设置相关
                   childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
                   childSpecSize = Math.max(0, maxAvailable);
               } else if (childSize == LayoutParams.WRAP_CONTENT) {
                   //在wrap进行设置
                   if (maxAvailable >= 0) {
                       // We have a maximum size in this dimension.
                       childSpecMode = MeasureSpec.AT_MOST;
                       childSpecSize = maxAvailable;
                   } else {
                       // We can grow in this dimension. Child can be as big as it
                       // wants.
                       childSpecMode = MeasureSpec.UNSPECIFIED;
                       childSpecSize = 0;
                   }
               }
           }
    
           return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
       }
    

    以上 完成了View的第一次测量 确定了View的大小 然后根据大小觉得把子view放在父RelativeLayout中的位置

    private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
            boolean wrapContent) {
        //获取RelativeLayout的布局方向
        final int layoutDirection = getLayoutDirection();
        int[] rules = params.getRules(layoutDirection);
    
        if (params.mLeft == VALUE_NOT_SET && params.mRight != VALUE_NOT_SET) {
            // 如果右边界有效 左边界无效 根据右边界计算出左边界
            params.mLeft = params.mRight - child.getMeasuredWidth();
        } else if (params.mLeft != VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
            // 同上反之
            params.mRight = params.mLeft + child.getMeasuredWidth();
        } else if (params.mLeft == VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
            //都无效的时候
    
            if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
              //设置了CENTER_IN_PARENT或者 CENTER_HORIZONTAL的情况下
                if (!wrapContent) {
                  //非wrap情况下
                  //把子View水平中心固定在RelativeLayout的中心
                    centerHorizontal(child, params, myWidth);
                } else {
                   //左边距为padding+margin
                   //右边距为左边距加上测量宽度
                    params.mLeft = mPaddingLeft + params.leftMargin;
                    params.mRight = params.mLeft + child.getMeasuredWidth();
                }
                return true;
            } else {
                //RTL右到左 布局方向
                //LTR�左到右 布局方向
                if (isLayoutRtl()) {
                    params.mRight = myWidth - mPaddingRight- params.rightMargin;
                    params.mLeft = params.mRight - child.getMeasuredWidth();
                } else {
                    params.mLeft = mPaddingLeft + params.leftMargin;
                    params.mRight = params.mLeft + child.getMeasuredWidth();
                }
            }
        }
        return rules[ALIGN_PARENT_END] != 0;
        //当为CENTER_IN_PARENT  CENTER_HORIZONTAL ALIGN_PARENT_END三种情况之一时返回True
    }
    

    4 遍历竖直关系的View

    ...
      for (int i = 0; i < count; i++) {
               final View child = views[i];
               if (child.getVisibility() != GONE) {
                   final LayoutParams params = (LayoutParams) child.getLayoutParams();
                  //将竖直方向规则转换为坐标
                   applyVerticalSizeRules(params, myHeight, child.getBaseline());
                   //测量子View
                   measureChild(child, params, myWidth, myHeight);
                   //确定竖直方向子View的位置
                   if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
                       offsetVerticalAxis = true;
                   }
                  //首先判断是否为wrap模式
                   if (isWrapContentWidth) {
                     //根据RTL或者LTR和版本进行区分
                     //Build.VERSION_CODES.KITKAT = 19
                     //主要对margin进行处理
                       if (isLayoutRtl()) {
                           if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                               width = Math.max(width, myWidth - params.mLeft);
                           } else {
                               width = Math.max(width, myWidth - params.mLeft - params.leftMargin);
                           }
                       } else {
                           if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                               width = Math.max(width, params.mRight);
                           } else {
                               width = Math.max(width, params.mRight + params.rightMargin);
                           }
                       }
                   }
                   if (isWrapContentHeight) {
                       if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                           height = Math.max(height, params.mBottom);
                       } else {
                           height = Math.max(height, params.mBottom + params.bottomMargin);
                       }
                   }
    
                   if (child != ignore || verticalGravity) {
                       left = Math.min(left, params.mLeft - params.leftMargin);
                       top = Math.min(top, params.mTop - params.topMargin);
                   }
    
                   if (child != ignore || horizontalGravity) {
                       right = Math.max(right, params.mRight + params.rightMargin);
                       bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
                   }
               }
           }
    ...
    

    5 baseline计算

    // Use the top-start-most laid out view as the baseline. RTL offsets are
    // applied later, so we can use the left-most edge as the starting edge.
        View baselineView = null;
        LayoutParams baselineParams = null;
        for (int i = 0; i < count; i++) {
            final View child = views[i];
            if (child.getVisibility() != GONE) {
                final LayoutParams childParams = (LayoutParams) child.getLayoutParams();
                if (baselineView == null || baselineParams == null
                        || compareLayoutPosition(childParams, baselineParams) < 0) {
                    baselineView = child;
                    baselineParams = childParams;
                }
            }
        }
        mBaselineView = baselineView;
    

    6 宽度和高度修正

        //如何是wrap模式
        if (isWrapContentWidth) {
                width += mPaddingRight;
    
                if (mLayoutParams != null && mLayoutParams.width >= 0) {
                    width = Math.max(width, mLayoutParams.width);
                }
    
                width = Math.max(width, getSuggestedMinimumWidth());
                width = resolveSize(width, widthMeasureSpec);
    
                //在得到最后的width之后 对依赖RelativeLayout的子View添上偏移量
                if (offsetHorizontalAxis) {
                    for (int i = 0; i < count; i++) {
                        final View child = views[i];
                        if (child.getVisibility() != GONE) {
                            final LayoutParams params = (LayoutParams) child.getLayoutParams();
                            final int[] rules = params.getRules(layoutDirection);
                            //对CENTER_IN_PARENT或者CENTER_HORIZONTAL的子View重测
                            if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
                                centerHorizontal(child, params, width);
                            //对ALIGN_PARENT_RIGHT重测
                            } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
                                final int childWidth = child.getMeasuredWidth();
                                params.mLeft = width - mPaddingRight - childWidth;
                                params.mRight = params.mLeft + childWidth;
                            }
                        }
                    }
                }
            }
            //同上
            if (isWrapContentHeight) {
                height += mPaddingBottom;
    
                if (mLayoutParams != null && mLayoutParams.height >= 0) {
                    height = Math.max(height, mLayoutParams.height);
                }
    
                height = Math.max(height, getSuggestedMinimumHeight());
                height = resolveSize(height, heightMeasureSpec);
    
                if (offsetVerticalAxis) {
                    for (int i = 0; i < count; i++) {
                        final View child = views[i];
                        if (child.getVisibility() != GONE) {
                            final LayoutParams params = (LayoutParams) child.getLayoutParams();
                            final int[] rules = params.getRules(layoutDirection);
                            if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
                                centerVertical(child, params, height);
                            } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
                                final int childHeight = child.getMeasuredHeight();
                                params.mTop = height - mPaddingBottom - childHeight;
                                params.mBottom = params.mTop + childHeight;
                            }
                        }
                    }
                }
            }
    
    
            //根据gravity再次修正
            if (horizontalGravity || verticalGravity) {
                final Rect selfBounds = mSelfBounds;
                selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
                        height - mPaddingBottom);
    
                final Rect contentBounds = mContentBounds;
                Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
                        layoutDirection);
    
                final int horizontalOffset = contentBounds.left - left;
                final int verticalOffset = contentBounds.top - top;
                if (horizontalOffset != 0 || verticalOffset != 0) {
                    for (int i = 0; i < count; i++) {
                        final View child = views[i];
                        if (child.getVisibility() != GONE && child != ignore) {
                            final LayoutParams params = (LayoutParams) child.getLayoutParams();
                            if (horizontalGravity) {
                                params.mLeft += horizontalOffset;
                                params.mRight += horizontalOffset;
                            }
                            if (verticalGravity) {
                                params.mTop += verticalOffset;
                                params.mBottom += verticalOffset;
                            }
                        }
                    }
                }
            }
    
            //如果是RTL(右到左显示)则再次修改
            if (isLayoutRtl()) {
                final int offsetWidth = myWidth - width;
                for (int i = 0; i < count; i++) {
                    final View child = views[i];
                    if (child.getVisibility() != GONE) {
                        final LayoutParams params = (LayoutParams) child.getLayoutParams();
                        params.mLeft -= offsetWidth;
                        params.mRight -= offsetWidth;
                    }
                }
            }
    

    简单总结

    RelativeLayout更加关注子View的left right top bottom值 并且优先级高于width和height

    RelativeLayout的layout过程

    对于RelativeLayout来的 layout过程更多的根据子View的left right top bottom值来设定位置

        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            //  The layout has actually already been performed and the positions
            //  cached.  Apply the cached values to the children.
            final int count = getChildCount();
    
            for (int i = 0; i < count; i++) {
                View child = getChildAt(i);
                if (child.getVisibility() != GONE) {
                    RelativeLayout.LayoutParams st =
                            (RelativeLayout.LayoutParams) child.getLayoutParams();
                    child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
                }
            }
        }
    

    RelativeLayout的draw过程

    RelativeLayout作为ViewGroup的子类 因为其性质原因 没有对draw过程进行修改

    以上

    相关文章

      网友评论

        本文标题:Android View 相关源码分析之五 RelativeLa

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