RelativeLayout
是 Android 中一个常用的布局容器,它允许子视图基于相对定位规则来定位,例如相对于父容器的边缘或其他子视图的位置。下面我们将结合源码分析 RelativeLayout
的实现原理,重点探讨其测量和布局过程。
1. 测量过程(onMeasure)
RelativeLayout
的测量过程涉及到了对每个子视图的测量,同时也需要考虑到它们之间的相对定位关系。RelativeLayout
的 onMeasure
方法首先会测量所有子视图,然后根据测量结果和布局规则来确定自身的大小。
1@Override
2protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
3 // ... (省略部分初始化代码)
4
5 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
6 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
7 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
8 final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
9
10 // Measure children.
11 measureChildren(widthMeasureSpec, heightMeasureSpec);
12
13 // ... (省略部分计算代码)
14
15 // Set our measurement to the maximum of the children or the specified size.
16 setMeasuredDimension(resolveSizeAndState(widthSize, widthMeasureSpec, 0),
17 resolveSizeAndState(heightSize, heightMeasureSpec, 0));
18}
在 onMeasure
方法中,RelativeLayout
会调用 measureChildren
方法来测量所有子视图。这个方法会考虑每个子视图的布局规则(例如 android:layout_alignParentLeft="true"
或 android:layout_below="@id/someView"
)来确定它们的大小。
2. 布局过程(onLayout)
RelativeLayout
的布局过程是在 onLayout
方法中完成的,它会根据每个子视图的测量结果和布局规则来确定它们的最终位置。
1@Override
2protected void onLayout(boolean changed, int l, int t, int r, int b) {
3 // ... (省略部分初始化代码)
4
5 final int width = r - l;
6 final int height = b - t;
7 final int paddingLeft = getPaddingLeft();
8 final int paddingTop = getPaddingTop();
9 final int paddingRight = getPaddingRight();
10 final int paddingBottom = getPaddingBottom();
11
12 // Position children.
13 layoutChildren(width, height, changed);
14}
15
16private void layoutChildren(int width, int height, boolean changed) {
17 // ... (省略部分初始化代码)
18
19 final int childCount = getChildCount();
20 for (int i = 0; i < childCount; i++) {
21 final View child = getChildAt(i);
22 if (child.getVisibility() != GONE) {
23 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
24 final int childLeft = calculatePositionX(lp);
25 final int childTop = calculatePositionY(lp);
26 final int childWidth = child.getMeasuredWidth();
27 final int childHeight = child.getMeasuredHeight();
28 child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
29 }
30 }
31}
在 onLayout
方法中,RelativeLayout
调用 layoutChildren
方法来布局所有子视图。对于每个子视图,它会调用 calculatePositionX
和 calculatePositionY
方法来计算子视图的最终位置。
3. 计算子视图位置
RelativeLayout
中计算子视图位置的过程是通过解析布局规则并应用它们来实现的。calculatePositionX
和 calculatePositionY
方法会遍历所有与位置相关的规则,然后根据这些规则来计算子视图的位置。
1private int calculatePositionX(LayoutParams lp) {
2 // ... (省略部分初始化代码)
3
4 if (lp.leftOf != null) {
5 final View leftOfView = (View) lp.leftOf;
6 final int leftOfLeft = leftOfView.getLeft();
7 final int leftOfMargin = ((LayoutParams) leftOfView.getLayoutParams()).leftMargin;
8 result = leftOfLeft - childWidth - leftOfMargin - lp.rightMargin;
9 }
10
11 // ... (处理其他规则)
12
13 return result + paddingLeft;
14}
15
16private int calculatePositionY(LayoutParams lp) {
17 // ... (类似 calculatePositionX 的逻辑)
18}
在这些方法中,RelativeLayout
会检查每个子视图的 LayoutParams
对象中的规则,并根据这些规则来计算子视图的最终位置。例如,如果一个子视图的规则是 android:layout_toLeftOf="@id/someView"
,那么它会被放置在 someView
的左侧,同时考虑到相应的 margins。
总结
RelativeLayout
的实现原理涉及到测量所有子视图,然后根据它们之间的相对位置规则来确定每个子视图的最终位置。这个过程需要仔细解析每个子视图的 LayoutParams
对象中的规则,并将这些规则应用于子视图的测量和布局过程。通过这种方式,RelativeLayout
能够灵活地组织子视图,并提供丰富的布局选项。