- UID
- 666760
- 积分
- 0
- 金币
- 0
- 精华
- 0
- 威望
- 0
- 贡献
- 0
- 阅读权限
- 220
- 注册时间
- 2009-10-4
- 最后登录
- 2026-5-6
- 在线时间
- 0 小时
热心网友
- 金币
- 0
- 阅读权限
- 220
- 精华
- 0
- 威望
- 0
- 贡献
- 0
- 在线时间
- 0 小时
- 注册时间
- 2009-10-4
|
onLayout、onMeasure和onDraw方法介绍
onMeasure(int widthMeasureSpec, int heightMeasureSpec)
onLayout(boolean changed, int left, int top, int right, int bottom)
- onLayout方法用于确定View位置的属性。在自定义ViewGroup(视图容器基类:可以理解为它可以调用所有的View)中,我们需要重写这个方法,根据子View的测量宽高来确定它们的位置
onDraw(Canvas canvas)
- onDraw方法用于绘制View的内容。在自定义View中,我们需要重写这个方法,利用Canvas进行绘制操作,如绘制形状、文本、图片等
自定义View案例
- 下面我们将通过一个简单的自定义View案例来演示使用这三个方法绘制一个带有边框的圆形
创建CircleView类
-
首先,创建一个名为CircleView的类,继承自View,并实现构造方法
public class CircleView extends View {
public CircleView(Context context) {
super(context);
}
public CircleView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
重写onMeasure方法
-
在CircleView类中,重写onMeasure方法,根据MeasureSpec来计算并设置View的宽高。这里我们假设圆形的半径为100dp
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int desiredSize = (int) (100 * getResources().getDisplayMetrics().density);
int width = measureDimension(desiredSize, widthMode, widthSize);
int height = measureDimension(desiredSize, heightMode, heightSize);
setMeasuredDimension(width, height);
}
private int measureDimension(int desiredSize, int mode, int size) {
int result;
if (mode == MeasureSpec.EXACTLY) {
result = size;
} else if (mode == MeasureSpec.AT_MOST) {
result = Math.min(desiredSize, size);
} else {
result = desiredSize;
}
return result;
}
重写onDraw方法
-
在CircleView类中,重写onDraw方法,使用Canvas绘制圆形和边框
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
int radius = Math.min(width, height) / 2;
Paint paint = new Paint();
paint.setAntiAlias(true);
// 绘制圆形
paint.setColor(Color.BLUE);
canvas.drawCircle(width / 2, height / 2, radius, paint);
// 绘制边框
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
canvas.drawCircle(width / 2, height / 2, radius - 2.5f, paint);
}
小结
- 我们已经完成了一个简单的自定义View - CircleView。在布局文件xml中使用这个自定义View,就可以看到一个带有边框的蓝色圆形。通过这个案例可以看到onMeasure和onDraw这两个方法在自定义View中的重要作用。onMeasure方法用于测量View的大小,onDraw方法用于绘制View的内容,而onLayout方法在此例中并未涉及,因为我们的CircleView直接继承自View,没有子View的布局需求。但如果我们需要自定义一个ViewGroup,那么onLayout方法将会用于确定子View的位置
自定义ViewGroup案例
- 演示onLayout方法的使用中将创建一个名为CustomLayout的自定义ViewGroup,它将简单地将子View按照从左到右、从上到下的顺序排列
创建CustomLayout类
-
创建一个名为CustomLayout的类,继承自ViewGroup,并实现构造方法
public class CustomLayout extends ViewGroup {
public CustomLayout(Context context) {
super(context);
}
public CustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
重写onMeasure方法
-
在CustomLayout类中,重写onMeasure方法,根据MeasureSpec来计算并设置ViewGroup的宽高。这里我们假设每个子View的宽高为100dp,水平间距和垂直间距均为20dp
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 获取宽度和高度的测量模式和尺寸
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// 定义子View的宽高和水平、垂直间距
int childWidth = (int) (100 * getResources().getDisplayMetrics().density);
int childHeight = (int) (100 * getResources().getDisplayMetrics().density);
int horizontalSpacing = (int) (20 * getResources().getDisplayMetrics().density);
int verticalSpacing = (int) (20 * getResources().getDisplayMetrics().density);
// 初始化ViewGroup的宽高和当前行的宽高
int width = 0; // ViewGroup的宽
int height = 0; // ViewGroup的高
int rowWidth = 0; //当前行的宽
int rowHeight = childHeight; //当前行的高
// 遍历所有子View
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
// 测量子View的大小
measureChild(child, MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
// 更新当前行的宽度
rowWidth += childWidth + horizontalSpacing; // rowWidth = childWidth + horizontalSpacing + rowWidth
// 检查当前行宽度是否超过ViewGroup的宽度
if (rowWidth > widthSize) {
// 更新ViewGroup的宽度
width = Math.max(width, rowWidth - horizontalSpacing);
// 累加高度
height += rowHeight + verticalSpacing; // height = rowHeight + verticalSpacing + height
// 重置当前行的宽度
rowWidth = childWidth + horizontalSpacing;
}
}
// 更新ViewGroup的宽度和高度
width = Math.max(width, rowWidth - horizontalSpacing);
height += rowHeight; // height = rowHeight + height
// 设置ViewGroup的测量宽高
setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : width, heightMode == MeasureSpec.EXACTLY ? heightSize : height);
}
-
在这段代码中,先获取宽度和高度的测量模式和尺寸。然后定义子View的宽高和水平、垂直间距,并初始化ViewGroup的宽高和当前行的宽高。接着遍历所有的子View,测量子View的大小,并更新当前行的宽度。检查当前行宽度是否超过ViewGroup的宽度,如果超过就更新ViewGroup的宽度并累加高度,并重置当前行的宽度。最后更新ViewGroup的宽度和高度,并设置ViewGroup的测量宽高
重写onLayout方法
-
在CustomLayout类中,重写onLayout方法,根据子View的测量宽高来确定它们的位置
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int width = getWidth();
int childWidth = (int) (100 * getResources().getDisplayMetrics().density);
int childHeight = (int) (100 * getResources().getDisplayMetrics().density);
int horizontalSpacing = (int) (20 * getResources().getDisplayMetrics().density);
int verticalSpacing = (int) (20 * getResources().getDisplayMetrics().density);
int x = 0;
int y = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (x + childWidth > width) {
x = 0;
y += childHeight + verticalSpacing;
}
child.layout(x, y, x + childWidth, y + childHeight);
x += childWidth + horizontalSpacing; // x = childWidth + horizontalSpacing + x
}
}
小结
- 以上完成了一个简单的自定义ViewGroup - CustomLayout。在布局文件中使用这个自定义ViewGroup,然后添加多个子View,就可以看到它们按照从左到右、从上到下的顺序排列。通过这个案例我们可以看到onLayout方法在自定义ViewGroup中的重要作用。它用于确定子View的位置,根据子View的测量宽高来进行布局。在实际开发中,我们可以根据需求自定义不同的布局方式,实现各种复杂的界面效果
总结
- 通过本文了解了onLayout、onMeasure和onDraw这三个方法在自定义View和自定义ViewGroup中的作用和用法。onMeasure方法用于测量View的大小,onDraw方法用于绘制View的内容,onLayout方法用于确定子View的位置。这三个方法对于实现自定义View和自定义ViewGroup至关重要,有助于我们在实际开发中更好地满足设计需求,提高界面的交互性和美观性
来源:https://www.cnblogs.com/ajunjava/p/18318483 |
|