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

 更新时间:2025年11月15日 14:18:33   作者:峰哥的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属性会失效。

进阶技巧与优化建议

  • 性能优化
    • 避免在onDraw中创建对象:例如Paint,应在初始化时(如构造方法)创建并复用。
    • 使用Canvas.clipRect():避免绘制View边界之外的内容,减少过度绘制。
    • 考虑使用硬件加速:在XML中设置android:layerType="hardware"可以提高某些绘制操作的性能。
  • 自定义属性:你可以为自定义View定义属性,使其更灵活。步骤包括:在res/values/下创建attrs.xml定义属性;在构造方法中使用TypedArray解析属性;在布局文件中使用自定义命名空间声明属性。
  • 处理触摸事件:通过重写onTouchEvent(MotionEvent event)方法,可以让你的View具有交互能力。

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

相关文章

  • 详解App相互唤醒的几种方式

    详解App相互唤醒的几种方式

    这篇文章主要介绍了Android App相互唤醒的几种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • 从Cocos2d-x2迁移到Cocos2d-x3的过程分享

    从Cocos2d-x2迁移到Cocos2d-x3的过程分享

    Cocos2d-x 3.0发布没多久,我就迫不及待地将手头的一个习作尝试从2.2.2版本迁移到3.0rc0引擎上,本问介绍了迁移流程和遇到的问题,以及发现了针对Android平台的变动,需要的朋友可以参考下
    2014-04-04
  • Android实现卡片翻转动画

    Android实现卡片翻转动画

    这篇文章主要为大家详细介绍了Android实现卡片翻转动画,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01
  • Android WindowManger实现桌面悬浮窗功能

    Android WindowManger实现桌面悬浮窗功能

    这篇文章主要介绍了Android WindowManger实现桌面悬浮窗功能,他们基本都是在Activity之上显示的,如果想实现在桌面显示的悬浮窗效果,需要用到WindowManager来实现了,需要的朋友可以参考下
    2023-04-04
  • Android实现按钮点击效果

    Android实现按钮点击效果

    本文主要介绍了Android实现按钮点击效果:第一次点击变色,第二次恢复。具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • Android编程实现隐藏状态栏及测试Activity是否活动的方法

    Android编程实现隐藏状态栏及测试Activity是否活动的方法

    这篇文章主要介绍了Android编程实现隐藏状态栏及测试Activity是否活动的方法,涉及Android界面布局设置及Activity状态操作的相关技巧,需要的朋友可以参考下
    2016-10-10
  • Android实现底部导航栏功能(选项卡)

    Android实现底部导航栏功能(选项卡)

    这篇文章主要介绍了Android实现底部导航栏功能,可以随意切换不同的页面,实现选项卡功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2015-12-12
  • Android自定义view系列之99.99%实现QQ侧滑删除效果实例代码详解

    Android自定义view系列之99.99%实现QQ侧滑删除效果实例代码详解

    这篇文章给大家介绍android自定义view系列之99.99%实现QQ侧滑删除效果,本文介绍的非常详细,具有参考借鉴价值,需要的朋友参考下吧
    2016-09-09
  • android studio实现简单的计算器(无bug)

    android studio实现简单的计算器(无bug)

    这篇文章主要为大家详细介绍了android studio实现简单计算器,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-08-08
  • 基于Android实现数独游戏

    基于Android实现数独游戏

    这篇文章主要为大家详细介绍了基于Android实现数独游戏,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12

最新评论