FrameLayout
是 Android 中最简单的布局容器之一,它将所有添加的子视图堆叠放置,后添加的子视图会覆盖在先添加的子视图之上。下面我们将从源码层面来分析 FrameLayout
的实现原理,重点关注其测量和布局过程。
1. 测量过程(onMeasure)
FrameLayout
在测量其子视图时,通常会将父容器的测量规格直接传递给子视图,除非有特定的布局参数指定了子视图的尺寸。这意味着 FrameLayout
不会改变其子视图的大小,除非它被要求去适应某种尺寸限制。
1@Override
2protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
3 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
4 int count = getChildCount();
5 for (int i = 0; i < count; i++) {
6 View child = getChildAt(i);
7 if (mMeasureAllChildren || child.getVisibility() != GONE) {
8 measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
9 }
10 }
11}
在这个方法中,FrameLayout
会遍历其所有的子视图并调用 measureChildWithMargins
方法来测量每个子视图。measureChildWithMargins
方法会考虑子视图的 LayoutParams
中定义的 margin,并使用这些值来创建一个新的测量规格。
2. 布局过程(onLayout)
在布局过程中,FrameLayout
将所有子视图定位在其左上角,然后根据子视图的大小和位置属性(如 margin)来确定最终位置。由于 FrameLayout
默认不调整子视图大小,所以子视图的定位只依赖于它们自己的测量结果和布局参数。
1@Override
2protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
3 final int count = getChildCount();
4 final int parentLeft = getPaddingLeft();
5 final int parentRight = right - left - getPaddingRight();
6 final int parentTop = getPaddingTop();
7 final int parentBottom = bottom - top - getPaddingBottom();
8 final int parentWidth = parentRight - parentLeft;
9 final int parentHeight = parentBottom - parentTop;
10
11 for (int i = 0; i < count; i++) {
12 final View child = getChildAt(i);
13 if (child.getVisibility() != GONE) {
14 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
15 final int width = child.getMeasuredWidth();
16 final int height = child.getMeasuredHeight();
17 final int childLeft = parentLeft + lp.leftMargin;
18 final int childTop = parentTop + lp.topMargin;
19 final int childRight = childLeft + width;
20 final int childBottom = childTop + height;
21 child.layout(childLeft, childTop, childRight, childBottom);
22 }
23 }
24}
在上述代码中,onLayout
方法首先获取 FrameLayout
的边界和 padding,然后遍历所有子视图并根据它们的 LayoutParams
来计算每个子视图的左上角坐标。最后,它调用 child.layout()
方法来设置子视图的最终位置。
3. 布局参数(LayoutParams)
FrameLayout
使用 LayoutParams
类来存储每个子视图的布局参数。这包括子视图的宽度、高度、margin 等属性。LayoutParams
对象允许子视图定制它们在父布局中的位置和大小。
1public static class LayoutParams extends MarginLayoutParams {
2 public LayoutParams(Context c, AttributeSet attrs) {
3 super(c, attrs);
4 }
5
6 public LayoutParams(int width, int height) {
7 super(width, height);
8 }
9
10 public LayoutParams(MarginLayoutParams source) {
11 super(source);
12 }
13
14 public LayoutParams(LayoutParams source) {
15 super(source);
16 }
17}
通过继承 MarginLayoutParams
,FrameLayout.LayoutParams
可以支持 margin 属性,这对于定位子视图非常有用。
总结
FrameLayout
的实现相对简单,它主要负责将子视图按照添加顺序堆叠在一起,后添加的子视图会覆盖先前的子视图。在测量和布局过程中,FrameLayout
依赖于子视图自身的测量逻辑和 LayoutParams
中定义的属性来确定子视图的最终位置和大小。