Android 自定义view onMeasure() 方法剖析

Eplan2022 复制已有的宏文件生成新的原理图宏文件 Eplan2022 新建原理图宏文件 阅读详情

 

接着上一篇自定义view 相关的,揭秘 Android Graphics2D 实现动态效果之——invalidate()   内容的介绍,这一篇主要介绍下自定义view 中的 onMeasure()方法的使用。

在介绍前,先简单回顾下自定义view 中的 onDraw()方法,该方法主要是将图形通过Paint画在Canvas上,View 上的所有内容都最终显示在Canvas 对象上,但这仅仅是绘制出来图形,但是想要设置View 的大小要怎么设置呢?是在xml 里给自定义view设置wrap_content或者match_parent?还是给自定义view限制一个精确的宽高值呢?带着这几个问题,不妨和我一起看一看:

一、自定义View宽高如何设置

1、设置wrap_content或者match_parent,view都会占满全屏(很简单,就是直接在onDraw()中绘制图形);

占满全屏

2、布局修改,给view限制一个宽高;

限制高度
限制宽高

3、动态添加view修改宽高

 /*方式一*/
CoordinateView coordinateView = new CoordinateView(this);
coordinateView.setBackgroundColor(getResources().getColor(R.color.albumColorPrimary));
linRoot.addView(coordinateView);

 /*方式二*/
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(500,1500);
CoordinateView customView = new CoordinateView(this);
customView.setBackgroundColor(getResources().getColor(R.color.albumColorPrimary));
customView.setLayoutParams(layoutParams);
linRoot.addView(customView);

两个方式区别是,单位不一样,new LinearLayout.LayoutParams(500,1500);一个是像素单位,一个是dp单位

二、认识onMeasure

(1)一般情况重写onMeasure()方法作用是为了自定义View尺寸的规则,如果你的自定义View的尺寸是根据父控件行为一致,就不需要重写onMeasure()方法 ;
(2)如果不重写onMeasure方法,那么自定义view的尺寸默认就和父控件一样大小,当然也可以在布局文件里面写死宽高,而重写该方法可以根据自己的需求设置自定义view大小;

1、onMeasure (int widthMeasureSpec, int heightMeasureSpec)是view自己的方法

2、onMeasure 方法简单的理解就是是用于测量视图的大小,主要是用来测量自己和内容的来确定宽度和高度

3、onMeasure有两个参数 ( int widthMeasureSpec, int heightMeasureSpec),该参数表示控件可获得的空间以及关于这个空间描述的元数据.

4、widthMeasureSpec和heightMeasureSpec这两个值通常情况下都是由父视图经过计算后传递给子视图的,说明父视图会在一定程度上决定子视图的大小。

/**
 * Created by ${lxb} on 2019/5/16.
 * 邮箱:207***1410@qq.com
 * TIP:坐标转换 学习
 */

public class CoordinateView extends View {

    public CoordinateView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public CoordinateView(Context context) {
        super(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //自定义view 如何设置宽高?
        int width = measureWidth(widthMeasureSpec);
        int height = measureHeight(heightMeasureSpec);
        //设置默认宽高
        setMeasuredDimension(width, height);
    }

    private int measureWidth(int measureSpec) {
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        //wrap_content
        if (mode == MeasureSpec.AT_MOST) {  // wrap_content

        } else if (mode == MeasureSpec.EXACTLY) {  // fill_parent 或者精确值
//            size = 500;
        }
        return size;//默认
    }

    private int measureHeight(int measureSpec) {
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        if (mode == MeasureSpec.AT_MOST) {  // wrap_content

        } else if (mode == MeasureSpec.EXACTLY) { // fill_parent 或者精确值
//            size = 3000;
        }
        return size;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

    }
}

三、MeasureSpec 作用

在测量自定义view的大小之前,我们需要认识一个类MeasureSpec,它封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求  MeasureSpec由size和mode组成。

specMode一共有三种类型,如下所示: 
1. EXACTLY 
表示父视图希望子视图的大小应该是由specSize的值来决定的,系统默认会按照这个规则来设置子视图的大小,简单的说(当设置width或height为match_parent时,模式为EXACTLY,因为子view会占据剩余容器的空间,所以它大小是确定的) 
2. AT_MOST 
表示子视图最多只能是specSize中指定的大小。(当设置为wrap_content时,模式为AT_MOST, 表示子view的大小最多是多少,这样子view会根据这个上限来设置自己的尺寸)

3. UNSPECIFIED 
表示开发人员可以将视图按照自己的意愿设置成任意的大小,没有任何限制。这种情况比较少见,不太会用到。 

四、案例分析

/**
 * Created by ${lxb} on 2019/5/17.
 * 邮箱:207***1410@qq.com
 * TIP: 自定义view onMeasure() 理解
 */

public class OnMeasureView extends View {
    private static final String TAG = OnMeasureView.class.getSimpleName();
    private Context mContext;
    /*定义一个画笔*/
    private Paint mPaint;
    private final String mText = "自定义view,测试文字内容";
    //    private final String mText = "韬睿科技,移动互联网卓越品牌!我爱 Android!";
    /*绘制时控制文本绘制的范围*/
    private Rect mBound;
    private int defaultWidth;
    private int defaultHeight;

    public OnMeasureView(Context context) {
        this(context, null);
    }

    public OnMeasureView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public OnMeasureView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        mBound = new Rect();
        mPaint = new Paint();
        mPaint.setTextSize(40);
        mPaint.getTextBounds(mText, 0, mText.length(), mBound);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.GRAY);
        mPaint.setColor(Color.RED);
        //绘制文字
        canvas.drawText(
                mText,
                getWidth() / 2 - mBound.width() / 2,
                getHeight() / 2 + mBound.height() / 2,
                mPaint);
    }


    /**
     * 比onDraw() 先执行
     * 1.基准点是baseline
     * 2.ascent:是baseline之上至字符最高处的距离
     * 3.descent:是baseline之下至字符最低处的距离
     * 4.leading:是上一行字符的descent到下一行的ascent之间的距离,也就是相邻行间的空白距离
     * 5.top:是指的是最高字符到baseline的值,即ascent的最大值
     * 6.bottom:是指最低字符到baseline的值,即descent的最大值
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int suggestedMinimumWidth = getSuggestedMinimumWidth();//最低限度宽度
        int suggestedMinimumHeight = getSuggestedMinimumHeight();//最低限度高度
        Log.d(TAG, "suggestedMinimumWidth:" + suggestedMinimumWidth);
        Log.d(TAG, "suggestedMinimumHeight:" + suggestedMinimumHeight);
        //设置测量宽高
        int width = measureWith(suggestedMinimumWidth, widthMeasureSpec);
        int height = measureHeight(suggestedMinimumHeight, heightMeasureSpec);
        setMeasuredDimension(width, height);

    }

    private int measureWith(int suggestedMinimumWidth, int measureSpec) {
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        Log.d(TAG, "size:" + size);
        switch (mode) {
            case MeasureSpec.AT_MOST:
                defaultWidth = (int) mPaint.measureText(mText) + getPaddingLeft() + getPaddingRight();
                break;
            case MeasureSpec.EXACTLY:
                defaultWidth = size;
                break;
            case MeasureSpec.UNSPECIFIED:
                defaultWidth = Math.max(defaultWidth, size);
                break;
        }
        return defaultWidth;

    }

    private int measureHeight(int suggestedMinimumHeight, int measureSpec) {
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        switch (mode) {
            case MeasureSpec.AT_MOST:
                defaultHeight = (int) (-mPaint.ascent() + mPaint.descent()) + getPaddingTop() + getPaddingBottom();
                break;
            case MeasureSpec.EXACTLY:
                defaultHeight = size;
                break;
            case MeasureSpec.UNSPECIFIED:
                defaultHeight = Math.max(defaultHeight, size);
                break;
        }
        return defaultHeight;
    }
}

 改变 OnMeasureView 的宽高对比就很清晰了:

                                                                        图一

                                                                        图二 

                                                                        图三 

知识拓展 ——baseLine 基线

上面的案例中,在onMeasure 注释中提到了,绘制文字的基准线 baseLine,那什么是基线呢?说白了就是一条直线,我们这里理解的是确定它的位置。我们先来看一下基线: 

9

 

 

从上图看出:基线等同四线格的第三条线,在Android中基线的位置定了,那么文字的位置也就定了。

更多自定义view学习,可阅读以下链接:

Android自定义View教程  https://www.gcssloop.com/category/customview

自定义View之绘图篇(三):文字(Text) https://blog.youkuaiyun.com/u012551350/article/details/51330308

Vue2 - 实现全景图Vr 720°全景效果查看预览功能,3D全景图流畅不卡顿渲染+手势拖拽+悬浮工具按钮,vue实现vr看720度全景效果方案讲解及示例代码(兼容电脑PC网页+手机H5移动端页面) vue全景VR图,vue h5实现全景720预览,vue加载720全景图_vue怎么加载vr图片,vue实现vr全景看房,vue2实现全景效果,加载很慢卡怎么办,实现全景预览的插件,vue显示720度全景图,720°全景图显示,在h5中展示720°全景图,浏览全景图,基于vue实现vr全景预览,加载全景图,vue全景预览pannellum插件,vue实现3D全景图,实现720°全景图展示,在VUE 中使用three.js实现全景视频播放,vue项目中使用photo-sphere-viewer实现vr720度 阅读详情

相关推荐

MID360+fastlio功能笔记

前言。

qq_35598561的博客 5298

Android onMeasure详解

理解Android onMeasure方法什么是父控件和子控件何时测量测量流程MeasureSpec 静态内部类ViewGroup中三个测量子控件的方法onMeasure()方法 什么是父控件和子控件         父控件就是容纳子控件的控件(也就是我们常说的布局)也称作容器,常见的父控件有LinearLayout,RelativeLayout,FrameLayout,TableLayout,GridLayout; &nbs

weixin_45558166的博客 3915

引入 CloudWeGo 后飞书管理后台平台化改造的演进史

飞书管理后台是飞书套件专为企业管理员提供的信息管理平台,它通过引入 Kitex 泛化调用对飞书管理后台进行平台化改造,成为了企业统一数字化管理平台。本文将从三个方面为大家讲解 Kitex 泛化调用在飞书管理后台平台化改造过程中的落地实践。...

weixin_44186218的博客 1762

Android之onMeasure的三种模式

在 Android 中,onMeasure() 方法是 View 或 ViewGroup 中的一个重要方法,用于测量视图的大小。子视图可以根据自身的需要设置任意的大小。在这种模式下,子视图的大小将由 MeasureSpec 的 getSize() 方法给出的确切值决定。在 onMeasure() 方法中,需要根据父容器传递给你的 MeasureSpec 来处理这三种模式,确保子视图被正确地测量和布局。在这种模式下,子视图的大小将由 MeasureSpec 的 getSize() 方法给出的最大值决定。

追梦的鱼儿 1188

SAP PP工艺路线Routing保姆级配置指南:从CA01创建到替代/并行顺序实战

本文详细解析SAP PP模块中工艺路线(Routing)的配置与应用,从基础创建(CA01)到高级功能如替代顺序和并行顺序的实战操作。通过真实案例演示,帮助用户掌握SAP PP工艺路线的精细化管理,提升生产流程效率。

weixin_30898555的博客 388

用OpenPose进行单个或多个人体姿态估计

在计算机视觉中,人体姿态估计(关键点检测)是一个很常见的问题,在体育健身、动作采集、3D试衣、舆情监测等领域具有广阔的应用前景,本文要使用的检测方法就是基于OpenPose的人体姿态估计方法。 一、OpenPose简介 OpenPose人体姿态识别项目是美国卡耐基梅隆大学(CMU)基于卷积神经网络(CNN)和监督学习(SL)并以Caffe为框架开发的开源库。可以实现人体动作、面部表情、手指运动等姿...

weixin_42709563的博客 8404

Android中onMesure研究

当一个Activity取得焦点后就会向Android系统请求绘制它的布局。Android框架会处理这个绘制的过程,一个View的显示,需要先后调用onMeasure,onLayout和onDraw方法。从字面意思理解onMeasure,为计算,测量。上图所示,A,B,C分别表示为三个View,其中,A View包含B View,B View 又包含C View。这三个View在屏幕上显示出来, 会

xuepeng0728119的专栏 560

opencv案例实战——银行卡模式匹配识别

在之前的几篇文章中我们已经介绍了opencv的一些基础知识,本篇文章我们将结合一个案例运用之前的知识并且学习opencv中模式匹配的应用,用到的知识点如果有不清楚的可以查看上面列出来的系列文章。具体代码会放在最后。

艾醒的博客 1339

android 自定义View学习一之onMeasure和onLayout

本文主要作为一个回忆笔记存在,不是从一开始的源码研究,所以是一个结论笔记。 通常来说我们创建一个activity的布局层次:

qq_34606099的博客 2089

在实车上进行lio-sam测试

PS:将雷达和imu相对固定后,拆下来,原地晃啊晃,直到xyz进度达到百分百,参考gif。标定完成后,会在result文件夹下生成result.txt。

如果想成为中心,那么就到中心去吧。 997

手把手教你学simulink(8.39)--基于Simulink的步进电机(Stepper Motor)仿真实例

通过这个仿真实例,你可以学习如何使用Simulink构建和仿真步进电机(Stepper Motor)系统。你可以根据实际需求调整模型和参数,进行更复杂的仿真和分析。希望这个例子对你有所帮助,祝你学习愉快!

MHD0815的博客 1052

ANDROID自定义视图——onMeasure流程,MeasureSpec详解

简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量——onMeasure():决定View的大小 2.布局——onLayout():决定View在ViewGroup中的位置 3.绘制——onDraw():如何绘制这个View。 而第3步的onDraw系统已经封装的很好了,基本不用我们来操心,只需要专注到1,2两个步骤就中好了。 而这篇文章就来谈谈第一步,也是十

easyer2012的专栏 1万+

Unreal5从入门到精通之初学者碰到解决不了的问题怎么办?

在学习Unreal的过程中,你曾陷入困境了吗?对于我来说,这种情况发生得比我愿意承认的要多得多。幸运的是,除了一般地在网上搜索之外,还有很多地方可以获得虚幻引擎的帮助。

TxNet 979

Android 自定义 View 之 onMeasure() 源码分析及重写

我们都知道,自定义View的三个重要过程分别是measure、layout、draw,而measure处于这条处理链的首端,自然是非常重要的。所以接下来的这一篇Android自定义View之onMeasure()源码分析及重写

薛建新的博客 4661

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

View的onMeasure()、onLayout()、onDraw()总结

自定义View是android开发中常有的一项工作,要想自定义View,你就必须熟练掌握View的测量、布局及绘制,了解其原理并会使用。 View视图绘制需要搞清楚两个问题,一个是从哪里开始绘制,一个是怎么绘制? 先说从哪里开始绘制的问题:我们平常在使用Activity的时候,都会调用setContentView来设置布局文件,没错,视图绘制就是从这个方法开始的; 再来说说怎么绘制的: 在...

流月的博客 5850

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

一起Talk Android吧(第一百三十三回:Android自定义View十)

各位看官们,大家好,上一回中咱们说的是Android中自定义View的例子,这一回咱们继续说该例子。闲话休提,言归正转。让我们一起Talk Android吧! 看官们,在上一章回中,我们完成了自定义View基本的框架,这一回中,我们给框架中添加具体的内容,这一回主要是给onMeasure()方法添加内容。代码如下: @Override protected void onMeasur...

talk_8的专栏 1589

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

自定义View(一) measure,layout,draw()

///final 本方法不能重写,只可以继承。viewGroup与View的 measure()方法一样 public final void measure(int widthMeasureSpec, int heightMeasureSpec) { boolean optical = isLayoutModeOptical(this); if (optical != isLayoutModeOptical(mParent)) { Insets insets = getOp.

weixin_39113436的博客 209

android view系统及其绘制流程,Android View 的绘制流程分析及其源码调用追踪

一、概述接着前面的一篇 Android Application 启动流程分析及其源码调用探究,从 Activity 启动,到正式开始绘制流程,整个过程如下图所示:上图中我列出了重要的几个方法步骤,可以看到,里面包含了 PhoneWindow 的创建时机和 DecorView 的初始化时机。因为 Android 视图层次结构中,PhoneWindow 和 DecorView 扮演着很重要的角色,请看...

weixin_34303763的博客 278

01-02-03 View测量体系完全指南:MeasureSpec与onMeasure实战

本文深入解析Android View测量体系的核心机制,重点介绍了MeasureSpec和onMeasure的实现原理。主要内容包括: MeasureSpec的结构与作用:通过32位int编码尺寸约束,包含EXACTLY/AT_MOST/UNSPECIFIED三种模式 测量流程架构:从DecorView开始,通过measure()触发onMeasure()完成测量 MeasureSpec生成规则:父容器根据自身约束和子View的LayoutParams生成子View的MeasureSpec 自定义View测

时代我西的博客 172

android 代码自定义checkbox,Android开发之自定义CheckBox

要实现的效果如下考虑到关键是动画效果,所以直接继承View。不过CheckBox的超类CompoundButton实现了Checkable接口,这一点值得借鉴。下面记录一下遇到的问题,并从源码的角度解决。问题一: 支持 wrap_content由于是直接继承自View,wrap_content需要进行特殊处理。View measure流程的MeasureSpec:/*** A MeasureSpe...

weixin_31966453的博客 868

Android onMeasure()测量流程解析

Android onMeasure()测量流程解析 文章目录Android onMeasure()测量流程解析前言组件测量的那些结论一、MeasureSpec:测量规则二、查看测量流程源码2.1 查看ViewRootImpl的PerformTraveals()方法2.2 View类的默认onMeasure()方法2.3 从FrameLayout的onMeasure()方法了解自定义ViewGroup的测量行为 前言 众所周知,Android 中组件显示到屏幕上需要经历测量、布局、绘制三个阶段,我们

weixin_43864176的博客 1890

GraphQL Java 后端接入 MongoDB:Connectors 连接器实战与 N+1 查询优化

> 本篇指南基于 HowToGraphQL 开源仓库中的 [graphql-java 教程 · 连接器章节](https://link.gitcode.com/i/fc606862a59a4ed7bd4e5a9d3b257583) 展开,讲解如何为基于 `graphql-java` + `graphql-java-tools` + Jetty 的 Java GraphQL 服务器接入 MongoD

gitblog_09772的博客 326

View绘制2-onMeasure

在自定义View的绘制过程中,重写onMeasure,onLayout,onDraw三个函数实现了View的外观形象,加上onTouchEvent等等函数实现的重载视图行为,构建出一个完整的自定义View体系。 在Android体系中,以on来头的onXXX函数,多以在Activity,Service,View中出现,一般都是使用了设计模式里面的模板设计模式。定义好一套模板流程,然后通过重写模板方

我早就想,要这样载着你,翻山越岭 741

View体系(八)深入剖析View的onMeasure方法

之前的文章《View体系(六)View工作流程入口》介绍了View是从什么地方开始它的工作流程的,《View体系(七)理解 MeasureSpec》介绍了View在measure流程时的一个重要参数。有了上两篇的铺垫,我们就来看一下View的onMeasure方法到底做了什么。 (注:文中源码基于 Android 12) 在View做测量时,会调用View的onMeasure方法: protected void onMeasure(int widthMeasureSpec, int height

droidYu 402

Android View 绘制流程与自定义 View 全解

Android View 绘制流程与自定义 View 全解

OuO-1 的博客 494

自定义控件View之onMeasure调用时机源码分析

hty1053240123的专栏 3731
上一篇: 了解 Android 双缓冲技术绘图机制
下一篇: 解读Android 消息队列和Looper
LXB-89
博客等级 码龄11年 34粉丝 81原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值