• 背景
  • 理解MeasureSpec
  • MeasureSpec
  • 情况分析
  • 结合图例分析
  • 总结
    • A little bit of progress every day!Come on!

背景

首先关于自定义view的实现过程我这里就不去实现了,因为这部分总体来说是不难的,自己写个类去继承View,然后实现构造方法,重写onMeasure()方法、onLayout()方法(继承ViewGroup的时候需要重写)和onDraw()方法来实现自定义view。
进入正题:很多时候我们需要自己来确定自定义view的大小,比如某种情况下你需要改变自定义view的大小,这时候你就需要重新测量自定义view的宽/高了,而这就与重写的onMeasure()方法有关系了,所以很关键就是onMeasure()方法里的实现方式了。那么今天给大家写一个该方法的通用写法,可以让自定义view根据设置的宽/高类型(wrap_content或者match_parent),来控制显示的大小。下面跟我来一起实现。


理解MeasureSpec

通过源码可以发现,MeasureSpec参与了View的measure过程。MeasureSpec是干什么的呢?确切来说,MeasureSpec在很大程度上决定了一个View的尺寸规格,之所以说是很大程度上是因为这个过程还受父容器的影响,因为父容器影响View的MeasureSpec的创建过程。在测量过程中,系统会将View的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec,然后再根据这个measureSpec来测量出View的宽/高。


MeasureSpec

MeasureSpec代表一个32位int值,高两位代表SpecMode,低30位代表SpecSize,SpecMode是指测量模式,而SpecSize是指在某种测量模式下的规格大小。

SpecMode有三类,每一类都表示特殊的含义,如下表所示

SpecMode 代表的含义
UNSPECFIED 父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量状态。
EXACTLY 父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的值。它对应于LayoutParams中的match_parent和具体的数值这两种模式。
AT_MOST 父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,具体是什么值要看不同View的具体实现。它对应于LayoutParams中的warp_content。

情况分析

实际情况中,你在布局文件中引用这个自定义View的时候设置宽和高的时候也就三种情况:

  • 宽/高都设置为了match_parent或者是固定大小
  • 宽/高其中一个设置为了wrap_content,另外一个为match_parent或者是固定大小
  • 宽/高都设置为了wrap_content

结合图例分析

  • 首先不在onMeasure()方法里写任何代码,就用系统默认的测量来试试看当给自定义view设置不同的宽/高类型后,在布局中是怎么显示的。

代码如下(onMeasure()方法里不写代码):

package com.example.demo.myapplication;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;/*** Created by Administrator on 2018/3/16.*/public class CustomView extends View{Paint paint = null;public CustomView(Context context) {super(context);init();}public CustomView(Context context, AttributeSet attrs) {super(context, attrs);init();}public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}/*** 控件初始化,一些初始化设置*/private void init() {paint = new Paint();paint.setStyle(Paint.Style.FILL_AND_STROKE);paint.setColor(Color.BLUE);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
//        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
//
//        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
//        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
//
//        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST){//            setMeasuredDimension(400, 400);
//        }else if (widthSpecMode == MeasureSpec.AT_MOST){//            setMeasuredDimension(400, heightSpecSize);
//        }else if (heightSpecMode == MeasureSpec.AT_MOST){//            setMeasuredDimension(widthSpecSize, 400);
//        }}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawColor(Color.RED);// 给自定义View的背景设置成红色,直观的显示view的在不同模式下的大小变化情况}
}

这里大家可以看到,我把相关代码注释了。这时候View的大小就是系统自己控制的, 我们不做任何的操作了。

在分析各种情况的时候,我这里发现了一个小技巧,不用运行项目就可以直接的将布局中自定义部分的可视界面根据代码变化也相应的动态变化。通俗点说就是代码变化后,不需要运行项目在移动设备上查看界面,只需要在布局中就能看到变化后的界面效果,这个如何做到的呢?如下:

简单来说,当你的自定义view中onMeasure方法的代码变化后,在布局中可以看到有提示说你的布局构建过时了,这时候就需要重新构建build,点击build就会根据代码的变动做出相对应的修改。而不需要再运行项目重新查看新的界面了,这样的话就方便多了。

  • 当宽/高都是wrap_content的时候,显示如下:


这时候自定义View充满了父布局所在空间。

  • 当宽/高都是match_parent的时候,显示如下:


这时候自定义View充满了父布局所在的空间。

  • 当宽/高都是固定大小的时候,显示如下:


这时候自定义View的大小就是固定的大小了。

很显然上面采用系统默认的测量方式不是我所期望的,我希望的是当自定义View的宽/高为wrap_content的时候可以自己动态的控制view的大小,但是上面的情况下,这时候view是充满父布局的空间的,这显然做不到动态的控制view的显示大小。现在来看第二种情况。

在onMeasure()方法中根据不同的宽/高设置类型来执行对应的操作达到控制view的大小。代码如下:

package com.example.demo.myapplication;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;/*** Created by Administrator on 2018/3/16.*/public class CustomView extends View{Paint paint = null;public CustomView(Context context) {super(context);init();}public CustomView(Context context, AttributeSet attrs) {super(context, attrs);init();}public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}/*** 控件初始化,一些初始化设置*/private void init() {paint = new Paint();paint.setStyle(Paint.Style.FILL_AND_STROKE);paint.setColor(Color.BLUE);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);// 获取view宽的SpecSize和SpecModeint widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);// 获取view高的SpecSize和SpecModeint heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST){// 当view的宽和高都设置为wrap_content时,调用setMeasuredDimension(measuredWidth,measureHeight)方法设置view的宽/高为400pxsetMeasuredDimension(400, 400);}else if (widthSpecMode == MeasureSpec.AT_MOST){// 当view的宽设置为wrap_content时,设置View的宽为你想要设置的大小(这里我设置400px),高就采用系统获取的heightSpecSizesetMeasuredDimension(400, heightSpecSize);}else if (heightSpecMode == MeasureSpec.AT_MOST){// 当view的高设置为wrap_content时,设置View的高为你想要设置的大小(这里我设置400px),宽就采用系统获取的widthSpecSizesetMeasuredDimension(widthSpecSize, 400);}}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawColor(Color.RED);// 给自定义View的背景设置成红色,直观的显示view的在不同模式下的大小变化情况}
}
  • 当宽/高都是wrap_content的时候,显示如下:


看这里就是我们自己在代码中设置的400px大小了。

  • 当宽/高都是match_parent的时候,显示如下:


这时候也是充满父布局的空间的。

  • 当宽/高都是固定大小的时候,显示如下:


这时候就是固定的大小显示,也没问题。

  • 当宽/高有一个是wrap_content的时候,显示如下:

可以看到设置的wrap_content是根据代码中设置的大小来进行显示,符合我们的要求。


总结

这里大家应该就差不多清楚了不同模式下的view的显示大小是怎么变化的了。onMeasure()方法中的通用写法如下:

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);// 获取view宽的SpecSize和SpecModeint widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);// 获取view高的SpecSize和SpecModeint heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST){// 当view的宽和高都设置为wrap_content时,调用setMeasuredDimension(measuredWidth,measureHeight)方法设置view的宽/高为400pxsetMeasuredDimension(400, 400);}else if (widthSpecMode == MeasureSpec.AT_MOST){// 当view的宽设置为wrap_content时,设置View的宽为你想要设置的大小(这里我设置400px),高就采用系统获取的heightSpecSizesetMeasuredDimension(400, heightSpecSize);}else if (heightSpecMode == MeasureSpec.AT_MOST){// 当view的高设置为wrap_content时,设置View的高为你想要设置的大小(这里我设置400px),宽就采用系统获取的widthSpecSizesetMeasuredDimension(widthSpecSize, 400);}}

这里大家想要设置多大的宽和高,自己可以动态的设置,但是需要设置其类型为wrap_content哦

===========================================================================

A little bit of progress every day!Come on!

Android中自定义view的onMeasure()方法详谈相关推荐

  1. android中对View的onMeasure()方法的理解

    在android开发中,很多人对自定义View是望而生畏,我也一样,但这又是向高级进阶的必经之路,主要是对View里面的很多方法不知道怎么理解,其中一个就是onMeasure()方法,网上有很多这样解 ...

  2. Android 中自定义View 裁剪扇形图片

    Android 中自定义View 裁剪扇形图片 当需要裁剪图片为扇形区域时,使用Canvas.clipPath(path)方法可以裁剪为扇形区域 ps:此方法会导致绘制图片边缘有锯齿,暂无解决方法(知 ...

  3. 【Android】自定义view之onMeasure

    1 onMeasure什么时候会被调用 onMeasure方法的作用是测量控件的大小,当我们创建一个View(执行构造方法)的时候不需要测量控件的大小,只有将这个view放入一个容器(父控件)中的时候 ...

  4. 【eoe教程】Android中自定义视图的绘制方法

    原文链接 :http://android.eoe.cn/topic/ui 自定义视图最重要的部分是它的外观.你可以根据应用的需求简单或复杂的实现它. 这个教程包含了最常见的操作. 重写onDraw() ...

  5. 自定义View之onMeasure()方法

    前言 一个View从创建到被绘制到屏幕上,需要完成measure(测量).layout(布置).draw(绘制)三个步骤,分别对应View中的measure().layout().draw()三个方法 ...

  6. Android中自定义View的研究 -- 在XML中引用自定义View

    如果在一直使用SetContentView(new HellwView(this)觉得总是少了一点东西,少了什么了,失去了Android中使用XML定义组件的便携性,这种感觉让人很不爽,呵呵,在这节里 ...

  7. Android 自定义View大全,Android中自定义View的实现方式总结大全

    Android自定义view是什么 在我们的日常开发中,很多时候系统提供的view是无法满足我们的需求的,例如,我们想给一个edittext加上清除按钮,等等. 这时候我们就需要对系统的view进行扩 ...

  8. Android中移动View(ViewGroup)方法探究

    最新研究Veiw的移动方法,参考网上和自己的总结.如下: 1.setX(),setY() 自己测试好像只对自定义ViewGroup起作用,自定义View不行,不知道为什么 ,欢迎解惑. private ...

  9. android sqlite自定义函数,Android中自定义一个View的方法详解

    本文实例讲述了Android中自定义一个View的方法.分享给大家供大家参考,具体如下: Android中自定义View的实现比较简单,无非就是继承父类,然后重载方法,即便如此,在实际编码中难免会遇到 ...

最新文章

  1. 2021固原一中高考成绩查询,【固原一中高考成绩实现新突破】
  2. mysqldump 忽略视图_mysqldump的使用--解决不能导出视图的问题
  3. Codeforces Round #496 (Div. 3)【未完结】
  4. 《密码与安全新技术专题》第11周作业
  5. 2019蓝桥杯省赛---java---C---1(求和)
  6. 大学刚毕业,零基础大数据如何入门?
  7. 004:神秘的数组初始化_使容器神秘化101:面向初学者的深入研究容器技术
  8. nginx+memcache实现页面缓存应用
  9. 简约风车壁纸自动采集小程序源码
  10. 分区函数Partition By的与row_number()的用法以及与排序rank()的用法详解(获取分组(分区)中前几条记录)...
  11. 中国紧凑型玻璃垫圈行业市场供需与战略研究报告
  12. c语言编程蛇形,蛇形矩阵c语言实现
  13. 1.业务层 、服务层、数据层、表现层
  14. Opencv 移植 (亲测有效)
  15. 利用宝塔面板部署SSM项目
  16. 道士后期时的技能运用有哪些
  17. 基于 STM32F412RE 的 Flappy bird 游戏机实现
  18. buflab-计算机系统实验
  19. 原生JS制作最简单轮播图(超清晰思路)
  20. SHP(shapefile)文件详细格式介绍

热门文章

  1. acw_sc__v2的算法破解之路
  2. vue跳转下个页面关闭当前页面_vue页面跳转 vue生命周期
  3. 重做mac系统的一堆坑
  4. 狂神说docker笔记(一)
  5. Homomorphism
  6. DOM标准与IE的html事件模型区别
  7. Google Earth Engine 学习7---ASTER GED 地表发射率数据下载
  8. 【微信小程序AR】基于Kivicube零代码实现微信小程序AR
  9. 不是mysql常用的数据类型_6.下列( )不是MySQL常用的数据类型。
  10. 聚苏丹红Ⅲ膜/磺化聚醚醚酮膜/ SiO2/Ag纤维复合材料修饰多巴胺的研究