通过自定义View了解MeasureSpecMode
一、自定义View的基本步骤
本篇文章的核心知识点,并不是自定义View的全部讲解,主要是通过一个简单的demo了解自定义View的MeasureSpecMode模式。
自定义View基本分为自定义View和自定义ViewGroup。
自定义View的步骤:
- 继承View重写构造方法(有四个构造方法,不同的使用场景可以了解下)
- 自定义属性,在构造方法中初始化属性
- 重写onMeasure方法测量宽高
- 重写onDraw方法绘制控件
关于View的绘制流程中,三个核心的回调方法onMeasure、onLayout、onDraw,在自定义View的时候,onLayout方法基本不用,onLayout方法是在ViewGroup自定的时候需要重写的方法,这个也比较好解释,就不多做介绍。
下面看一个简单的自定义TextView的代码编写:
CTextView1.java
public class CTextView1 extends View {private Paint mPaint;//计算字体所需要的范围private Rect mTextBounds;private int color;private String text;//一般纯代码创建View的时候,可以使用这个构造函数public CTextView1(Context context) {this(context,null);}//加载布局文件的时候会调用public CTextView1(Context context, @Nullable AttributeSet attrs) {this(context, attrs,0);}public CTextView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, 0);//自定义属性TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.MNView);color = typedArray.getInteger(R.styleable.MNView_mn_color,R.color.colorAccent);text = typedArray.getString(R.styleable.MNView_mn_text);typedArray.recycle();//初始化画笔mPaint= new Paint();mTextBounds = new Rect();mPaint.setColor(color);mPaint.setTextSize(50);//确定paint的绘制范围mPaint.getTextBounds(text,0,text.length(),mTextBounds);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//这个函数简单理解下:文字实际内容的绘制源点;//text:文本内容,x、y是源点坐标,x:绘制文本的起始x坐标;y:实际上是展示文本的基线上起点的y坐标canvas.drawText(text,0,getPaddingTop()+mTextBounds.height(),mPaint);}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#AB7CEF"tools:context=".MainActivity"><com.hym.view.CTextView1android:layout_width="wrap_content"android:layout_height="wrap_content"app:mn_text="自定义View"android:padding="10dp"android:background="@color/colorAccent"/></LinearLayout>
运行看一下效果:
这里是截图,运行出来的效果也是一致的。这里面很显然wrap_content是失效的。那么这里面为什么wrap_content会失效呢,就是接下来我们今天核心要回顾和学习的知识点。
二、View的MeasureSpec模式
View的MeasureSpec是由父容器的MeasureSpec以及自身的layoutParams决定的。
parentSpecMode\childLayoutParams | Exactly | AT_MOST | UNSPECIFIED |
---|---|---|---|
固定值:dp/px | Exactly:固定大小,即子View实际定义大小 | Exactly:固定大小,即子View实际定义大小 | Exactly:固定大小,即子View实际定义大小 |
match_parent | Exactly:父View的大小 | AT_MOST:父View的大小 | UNSPECIFIED:0 |
wrap_content | AT_MOST:父View的大小 | AT_MOST:父View的大小 | UNSPECIFIED:0 |
1:父容器为Exactly模式:(设定了具体数值宽高)
如果子View是Exactly模式(当用户指定了具体的数值(宽/高)),那么这个子View的resultSize就是你赋值的具体值。
如果子View指定的是match_parent,那么子View的resultSize依然是父容器给予的最大值,模式还是Exactly。
如果子View指定的是wrap_content,那么子View的resultSize也是父容器给予的最大值,但是模式变成了At_most。(这也是经常被提问到在自定义View的过程中,wrap_content失效的问题)
2:父容器为At_most模式:(对应父容器设置了wrap_content)
如果子View是Exactly模式(当用户指定了具体的数值(宽/高)),那么子View的resultSize就是具体制定的大小。
如果子View指定的是match_parent,那么子View的resultSize是父容器给予的最大值,模式还是At_most。
如果子View指定的wrap_content,子view的resultSize就是父控件的size,模式是at_most。
3:父容器为Unspecified模式:(这个模式基本用不到,大部分都是在系统内部使用)
如果子View是Exactly模式(当用户指定了具体的数值(宽/高)),那么子View的resultSize就是赋值的具体的值。
如果子View指定的match_parent,子View的大小为0,模式也为Unspecified。
如果子View指定的wrap_content,子View的大小为0,模式为Unspecified。
Unspecified模式,就总结一句话,父View对子View不做限制,子View有大小,就是子View设置的大小,其它均为0,不多研究。
上面的内容,可能有些拗口,对1、2种情况再做一下解释,来帮助理解:
如何确定一个View的MeasureSpecMode?
layoutParams设置的三种情况+父View的MeasureSpecMode来确定一个View当前的MeasureSpecMode。
View为固定大小,父View无论为什么模式,均为Exactly模式;
View为match_parent,父View为固定大小,子View就能确定为固定大小(即父View的大小),故为Exactly模式;
View为match_parent,父View为非固定大小,子View的大小跟随父View(但也不确定具体大小),故为At_most模式;
View为wrap_content,父View是固定大小或是非固定大小(只要非UnSpecified模式),子View都是父亲View的大小,故为At_most;
从View的MeasureSpec的模式确定角度,归纳上述描述为如下表格
View的MeasureSpec 模式 | 条件 |
---|---|
Exactly | View为设置的固定宽高||View为match_parent,父View为确定宽高(Exactly模式) |
At_most | View为wrap_content||View为match_parent,父View为非确定宽高(Exactly模式) |
通过上面的多方解释,应该算是到理解状态,具体MeasureSpec的精确确认过程,需要通过源码的解析,深度确认,但是上述结论是没有问题的。
通过上面的内容,我们也能看到,如果我们没有做任何特殊处理的情况,即使我的自定义View在布局中使用,使用了wrap_content,其实也不能达到我们所理解的当前View的高度即是子View的高度和,而是父View的大小。
于是,我们重新改造上面的onMeasure方法:
public class CTextView extends View {private Paint mPaint;//计算字体所需要的范围private Rect mTextBounds;private int color;private String text;private int mWidth;private int mHeight;//一般纯代码创建View的时候,可以使用这个构造函数public CTextView(Context context) {this(context,null);}//加载布局文件的时候会调用public CTextView(Context context, @Nullable AttributeSet attrs) {this(context, attrs,0);}public CTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, 0);//自定义属性TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.MNView);color = typedArray.getInteger(R.styleable.MNView_mn_color,R.color.colorAccent);text = typedArray.getString(R.styleable.MNView_mn_text);typedArray.recycle();//初始化画笔mPaint= new Paint();mTextBounds = new Rect();mPaint.setColor(color);mPaint.setTextSize(50);//确定paint的绘制范围mPaint.getTextBounds(text,0,text.length(),mTextBounds);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取宽度的测量规范模式int specMode = MeasureSpec.getMode(widthMeasureSpec);int specWidth = MeasureSpec.getSize(widthMeasureSpec);Log.i("onMeasure 模式和宽度","specMode:"+specMode+"|specWidth:"+specWidth);if(specMode == MeasureSpec.EXACTLY){//当前view指定的match_parent或者指定固定值大小mWidth=specWidth;}else{//At_most 模式,当前View的layoutParams设置的是wrap_content//At_most 模式,子View的宽高是父View的宽高(无论父View是Exactly模式还是At_most模式)mWidth=getPaddingLeft()+mTextBounds.width()+getPaddingRight();}specMode = MeasureSpec.getMode(widthMeasureSpec);int specHeight = MeasureSpec.getSize(widthMeasureSpec);Log.i("onMeasure 模式和宽度","specMode:"+specMode+"|specHeight:"+specHeight);if(specMode == MeasureSpec.EXACTLY){//当前view指定的match_parent或者指定固定值大小mHeight=specHeight;}else{//At_most 模式,当前View的layoutParams设置的是wrap_content//At_most 模式,子View的宽高是父View的宽高(无论父View是Exactly模式还是At_most模式)mHeight=getPaddingTop()+mTextBounds.height()+getPaddingBottom();}//要不测量出来的宽高,进行设置Log.i("测量后的结果","mWidth:"+mWidth+"|mHeight:"+mHeight);setMeasuredDimension(mWidth,mHeight);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//这个函数简单理解下:文字实际内容的绘制源点;//text:文本内容,x、y是源点坐标,x:绘制文本的起始x坐标;y:实际上是展示文本的基线上起点的y坐标canvas.drawText(text,0,getPaddingTop()+mTextBounds.height(),mPaint);}
}
再看下图,我们的效果就正常了。
关于自定义ViewGroup,针对于我们自己设计的ViewGroup的特性,正常情况下,我们重写onMeasure方法的时候,也是结合measureSpec测量模式,对我们的实际子View进行测量,同时要处理onLayout方法。
及时获得更多更新,关注gongzhonghao:Hym4Android
通过自定义View了解MeasureSpecMode相关推荐
- android 自定义取色器,【Android自定义View】仿Photoshop取色器ColorPicker(二)
ColorPicker 一款仿Photoshop取色器的Android版取色器. 前言 上一篇已经简单介绍了ColorPicker的项目结构以及两种颜色空间,接下来我们详细解析一下ColorPicke ...
- Android自定义view之measure、layout、draw三大流程
自定义view之measure.layout.draw三大流程 一个view要显示出来,需要经过测量.布局和绘制这三个过程,本章就这三个流程详细探讨一下.View的三大流程具体分析起来比较复杂,本文不 ...
- android自定义起止时间的时间刻度尺,Android 自定义View篇(六)实现时钟表盘效果...
前言 Android 自定义 View 是高级进阶不可或缺的内容,日常工作中,经常会遇到产品.UI 设计出花里胡哨的界面.当系统自带的控件不能满足开发需求时,就只能自己动手撸一个效果. 本文就带自定义 ...
- 【朝花夕拾】Android自定义View篇之(一)View绘制流程
前言 转载请申明转自[https://www.cnblogs.com/andy-songwei/p/10955062.html]谢谢! 自定义View.多线程.网络,被认为是Android开发者必须牢 ...
- 【朝花夕拾】Android自定义View之(一)手把手教你看懂View绘制流程——向源码要答案
前言 原文:Android自定义View之(一)手把手教你看懂View绘制流程--向源码要答案 View作为整个app的颜值担当,在Android体系中占有重要的地位.深入理解Android View ...
- Android自定义View(二)
文章目录 1.构造函数 2.onMeasure() 2.1.MeasureSpec 3.onSizeChanged() 4.onLayout() 5.onDraw() 上一篇: Android自定义V ...
- 自定义View 未完待续
自定义View 文章目录 自定义View 什么是自定义View 自定义View和自定义ViewGroup的区别? 自定义View的步骤 布局 widthMeasureSpec和heightMeasur ...
- Android自定义view之View的测量过程全解析
Android 应用层开发中绕不开自定义 View 这个话题,虽然现在 Github 上有形形色色的开源库供大家使用, 但是作为一名开发者而言,虽然不提倡重复造轮子,但是轮子都是造出来的.碰到一些新鲜 ...
- Android自定义View之(一)View绘制流程详解——向源码要答案
前言 View作为整个app的颜值担当,在Android体系中占有重要的地位.深入理解Android View的绘制流程,对正确使用View来构建赏心悦目的外观,以及用自定义View来设计理想中的酷炫 ...
- Android自定义View基本步骤
一.自定义属性 1.在res下的values下面新建attrs.xml 2.在布局中使用,声明命名空间 3.在自定义View构造方法中通过TypedArray获取属性 4.必须回收 array.rec ...
最新文章
- django redirect传递参数_Django中间件
- Nginx伪静态配置和常用Rewrite伪静态规则集锦
- git的简易上手笔记,及常用命令集合
- [转]Linux下用gcc/g++生成静态库和动态库(Z)
- 秒赞机器人好友_qq空间秒赞机器人好友 qq空间说说秒赞神器
- DTU网关连接MQTT服务器、MQTT.fx工具测试
- 滴滴在HBase性能与可用性上的探索与实践
- 干货 | 关于离岸银行开户(境外开户),看这一篇就够了
- 【实战】物联网安防监控项目【4】———从网页上控制A9的LED灯
- 在线客服系统源码软件代码+自动回复+可生成接入+手机版管理后台
- 华为运营商级路由器配置示例 | 静态BFD for IPv4静态路由
- 中国将购买43架A320和27架A330 总价百亿美元
- 网站是怎么了,一直http/1.1 302 found 浏览器假死了一样
- STM32F0系列FOC 源代码, 有单电阻采样和三电阻采样两种代码
- Fe3O4@mSiO2/DTX靶向载药纳米粒/FITC-SiO2@Fe3O4 荧光标记介孔二氧化硅磁颗粒
- openlayers6【七】地图控件controls详解
- 未来计算机将何去何从?
- centos根分区扩容,扩展磁盘
- 【目标检测】交通标识数据集CCTSDB标注处理(转YOLO)
- 大流量和高并发应对手段总结