Android

关注公众号 jb51net

关闭
首页 > 软件编程 > Android > android的自定义view

Android自定义view详解及Measurepec深入解析

作者:峰哥的Android进阶之路

这篇文章详细介绍了自定义View的三大流程:测量、布局和绘制,并深入解析了MeasureSpec的含义和确定规则,文章还提供了实现自定义View的关键步骤,包括继承View类、正确处理测量和绘制,以及如何进行性能优化和自定义属性处理,感兴趣的朋友跟随小编一起看看吧

理解自定义View的三大流程

自定义View的绘制主要围绕三个核心过程展开,它们依次执行,共同决定了View的最终呈现:

流程阶段

核心方法

主要职责

测量 (Measure)

onMeasure(int widthMeasureSpec, int heightMeasureSpec)

确定View的测量宽度和高度。必须在此方法末尾调用setMeasuredDimension()来保存结果。

布局 (Layout)

onLayout(boolean changed, int l, int t, int r, int b)

确定View在父容器中的位置(四个顶点的坐标),对于ViewGroup,还需负责遍历和确定所有子View的位置。

绘制 (Draw)

onDraw(Canvas canvas)

将View的视觉内容绘制到屏幕上。通过Canvas(画布)和Paint(画笔)对象完成具体绘制工作。

整个流程的发起者是ViewRootImplperformTraversals()方法,它会根据情况决定是否执行完整的测量、布局和绘制。

深入解析MeasureSpec

MeasureSpec(测量规格)是理解测量流程的钥匙。它是一个32位的int值,高2位代表测量模式(SpecMode),低30位代表在该模式下的规格大小(SpecSize)。其设计目的是为了高效地用一个变量同时携带模式和尺寸信息。

1. 三种测量模式的含义

模式

含义

常见对应场景

EXACTLY

精确模式:父容器已经为子View确定了一个精确的尺寸。此时子View的尺寸应直接设为SpecSize

在布局中设置了具体数值(如100dp)或match_parent

AT_MOST

最大模式:父容器为子View指定了一个最大可用尺寸。子View的尺寸不能超过这个SpecSize,应根据自身内容需求决定大小。

在布局中设置了wrap_content

UNSPECIFIED

未指定模式:父容器对子View没有任何限制,子View可以取任意它需要的大小。这种模式不常用,通常出现在ScrollViewListView等可滚动的容器中。

2. MeasureSpec的确定规则

一个子View的MeasureSpec并非凭空产生,而是由父容器的MeasureSpec和子View自身的LayoutParams(布局参数,如match_parentwrap_content或固定尺寸)共同决定的。这个规则封装在ViewGroupgetChildMeasureSpec()方法中。

其决策规律可以总结为下表:

子View的LayoutParams

父容器的SpecMode

子View的SpecMode

固定值(如100dp)

任意模式

EXACTLY

match_parent

EXACTLY

EXACTLY

AT_MOST

AT_MOST

wrap_content

EXACTLY/ AT_MOST

AT_MOST

实现自定义View的关键步骤

1. 继承View类并重写构造方法

通常需要实现三个构造函数,以正确处理在代码中创建和在XML布局中声明的情况。

public class MyCustomView extends View {
    public MyCustomView(Context context) {
        super(context);
        init();
    }
    public MyCustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    private void init() {
        // 初始化画笔、属性等
    }
}

2. 正确处理测量(重写onMeasure)

这是自定义View,特别是处理wrap_content时最容易出错的地方。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int defaultWidth = 200; // 自定义View的默认宽度
    int defaultHeight = 200; // 自定义View的默认高度
    int width = resolveSize(defaultWidth, widthMeasureSpec);
    int height = resolveSize(defaultHeight, heightMeasureSpec);
    setMeasuredDimension(width, height);
}

关键点:如果不重写onMeasure,或者处理不当,当在布局中使用wrap_content时,其效果会与match_parent相同。因为系统的默认实现(getDefaultSize())在AT_MOSTEXACTLY模式下都返回相同的SpecSize。因此,你需要为wrap_content(即AT_MOST模式)指定一个默认大小。

3. 实现绘制(重写onDraw)

在此方法中,使用CanvasPaint进行绘制。

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    // 示例:绘制一个圆形
    int centerX = getWidth() / 2;
    int centerY = getHeight() / 2;
    int radius = Math.min(centerX, centerY) - 10;
    Paint paint = new Paint();
    paint.setColor(Color.BLUE);
    paint.setStyle(Paint.Style.FILL);
    canvas.drawCircle(centerX, centerY, radius, paint);
}

注意:应考虑padding的影响,绘制内容时应减去相应的padding值,否则padding属性会失效。

进阶技巧与优化建议

到此这篇关于Android的自定义view详解及Measurepec详解的文章就介绍到这了,更多相关android的自定义view内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文