自定义ViewGroup实现要点
更多面试题请看这里:https://interview.raoyunsoft.com/
面试题专栏会持续更新欢迎关注订阅
1. 核心方法重写
必须重写以下方法:
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {return new MarginLayoutParams(p);
}@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {return new MarginLayoutParams(getContext(), attrs);
}
2. onMeasure测量流程
关键操作:
- 解析父容器测量规格
- 测量所有子View
- 计算自身最终尺寸
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 获取测量模式和尺寸int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);// 测量所有子ViewmeasureChildren(widthMeasureSpec, heightMeasureSpec);// 遍历子View计算尺寸int tWidth = 0, bWidth = 0;for (int i = 0; i < getChildCount(); i++) {View child = getChildAt(i);int cWidth = child.getMeasuredWidth();MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();// 根据子View位置分组计算if (i == 0 || i == 1) {tWidth += cWidth + params.leftMargin + params.rightMargin;} else if (i == 2 || i == 3) {bWidth += cWidth + params.leftMargin + params.rightMargin;}}// 确定最终宽度int width = Math.max(tWidth, bWidth);setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth : width,(heightMode == MeasureSpec.EXACTLY) ? sizeHeight : ...);
}
3. onLayout布局流程
关键步骤:
- 获取子View尺寸
- 计算每个子View坐标
- 调用子View的layout()
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {for (int i = 0; i < getChildCount(); i++) {View child = getChildAt(i);int cWidth = child.getMeasuredWidth();int cHeight = child.getMeasuredHeight();MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();// 计算子View的四个边界坐标(示例)int cl = ... // 根据布局逻辑计算leftint ct = ... // 根据布局逻辑计算topint cr = cl + cWidth;int cb = ct + cHeight;child.layout(cl, ct, cr, cb);}
}
4. 实战技巧
- 测量模式处理:
switch(widthMode) {case MeasureSpec.EXACTLY: // 精确值模式case MeasureSpec.AT_MOST: // 最大值模式case MeasureSpec.UNSPECIFIED: // 未指定模式 } - 边距处理:
使用MarginLayoutParams获取子View的leftMargin/topMargin等值 - 性能优化:
避免在onMeasure/onLayout中创建新对象,复用已有变量
5. 常见问题解决方案
子View重叠问题:
- 检查坐标计算逻辑是否考虑其他子View的位置
- 确保每次布局时重置定位坐标
测量不准确:
- 检查是否处理了
wrap_content模式 - 验证子View测量是否调用
measureChild()或measureChildren()
布局效率优化:
// 使用预计算减少循环内计算量
int currentX = 0, currentY = 0;
for (View child : children) {child.layout(currentX, currentY, currentX + childWidth, currentY + childHeight);currentX += childWidth + margin;
}
