一 概述
在上篇文章中我们分析了 View 的 Measure 过程,本篇文章我们分析 Layout 过程,通过本篇文章,你将了解到:
- 关于 Layout 简单类比
- 一个简单 Demo
- View Layout 过程
- ViewGroup Layout 过程
- View/ViewGroup 常用方法分析
- 为什么说 Layout 是承上启下的作用
二 关于Layout简单类比
在上篇文章的比喻里,我们说过:
老王给三个儿子,大王(大王儿子:小小王)、二王、三王分配了具体的良田面积,三个儿子(小小王)也都确认了自己的需要的良田面积。这就是:Measure 过程
既然知道了分配给各个儿孙的良田大小,那他们到底分到哪一块呢,是靠边、还是中间、还是其它位置呢?先分给谁呢?
老王想按到这个家的时间先后顺序来分 (对应 addView 顺序),大王是自己的长子,先分配给他,于是从最左侧开始,划出3亩田给大王。现在轮到二王了,由于大王已经分配了左侧的3亩,那么给二王的5亩地只能从大王右侧开始划分,最后剩下的就分给三王。这就是:ViewGroup onLayout 过程。
大王拿到老王给自己指定的良田的边界,将这个边界 (左、上、右、下) 坐标记录下来。这就是:View Layout 过程。
接着大王告诉自己的儿子小小王:你爹有点私心啊,从爷爷那继承的5亩田地不能全分给你,我留一些养老。这就是设置:padding 过程。
如果二王在最开始测量的时候就想:我不想和大王、三王的田离得太近,那么老王就会给大王、三王与二王的土地之间留点缝隙。这就是设置:margin 过程
三 一个简单Demo
自定义 ViewGroup
public class MyViewGroup extends ViewGroup {
public MyViewGroup(Context context) {
super(context);
}
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int usedWidth = 0;
int maxHeight = 0;
int childState = 0;
//测量子布局
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
MarginLayoutParams layoutParams =
(MarginLayoutParams) childView.getLayoutParams();
measureChildWithMargins(childView, widthMeasureSpec, usedWidth,
heightMeasureSpec, 0);
usedWidth += layoutParams.leftMargin + layoutParams.rightMargin +
childView.getMeasuredWidth();
maxHeight = Math.max(maxHeight, layoutParams.topMargin +
layoutParams.bottomMargin + childView.getMeasuredHeight());
childState = combineMeasuredStates(childState, childView.getMeasuredState());
}
//统计子布局水平,记录尺寸值
usedWidth += getPaddingLeft() + getPaddingRight();
maxHeight += getPaddingTop() + getPaddingBottom();
setMeasuredDimension(
resolveSizeAndState(usedWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//父布局传递进来的位置信息
int parentLeft = getPaddingLeft();
int left = 0;
int top = 0;
int right = 0;
int bottom = 0;
//遍历子布局
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
MarginLayoutParams layoutParams =
(MarginLayoutParams) childView.getLayoutParams();
left = parentLeft + layoutParams.leftMargin;
right = left + childView.getMeasuredWidth();
top = getPaddingTop() + layoutParams.topMargin;
bottom = top + childView.getMeasuredHeight();
//子布局摆放
childView.layout(left, top, right, bottom);
//横向摆放
parentLeft += right;
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {