首页 > 编程知识 正文

android自定义view面试,android自定义组件

时间:2023-05-06 07:06:41 阅读:60879 作者:4969

首先,在了解定制View三个流程的Measure流程之前,必须了解重要的基础。 今天,我将全面解析MeasureSpec类的相关知识。 希望Carson会喜欢带你学习安卓定制View文章系列。

Carson带你去安卓:学习定制视图的基础

Carson带你去安卓:用一句话组织自定义视图工作流

Carson教你安卓:自定义视图测量流程

Carson教你安卓:自定义视图布局流程

Carson教你安卓:自定义视图绘制流程

Carson带你去安卓,手把手地教你,写完整的自定义视图

Carson教你安卓: canvas类的全面解析

Carson教你安卓:路径类的全面解析

目录

1 .前言

2 .组成测量标准(MeasureSpec )由测量模式(mode )和测量尺寸(size )构成,共计32位(int型)。

占测量模式(mode )测量标准(MeasureSpec )的前2位; 测量尺寸(size )占测量标准(MeasureSpec )的后30位。

其中,测量模式(Mode )的类型有3种

3 .具体使用测量标准(MeasureSpec )的包类,MeasureSpec类MeasureSpec类用一个变量封装测量模式(mode )和测量大小(size )。 使用二进制文件将测量模式(mode )和测量大小(size )封装在一起,具体为//1 .获取测量模式(mode ) intspecmode=measurespec.getmode ) mmd 2 .使用“获取测量大小”(Size ) intspecSize=measurespec )3.使用mode和size创建新的specmodeintmeasurespec=measurespec.makemeasurespec (measurespec 4 .源代码分析public class MeasureSpec { //进位大小=2的30次方//int的大小为32位,因此将进位30位int的32位和31位用作标志位//运算掩码:0x3为十六进制,十进制为3,二进制为11 //3,向左舍入为30=1100000000(11后接30个0 )//作用:所需值用1表示,不需要的值用0表示。 1和任意数计算,任意数,0和任意数计算,0 privatestaticfinalintmode _ mask=0x3mode _ shift; //UNSPECIFIED模式设置: 0向左舍入30=00,至30个0,即00 00000000000 //前2位publicstaticfinalintunspecified=0mode _ shift //EXACTLY模式设置: 1向左舍入30=01,然后舍入30个0,即0100000000 publicstaticfinalintexactly=1mode _ shift; //AT_MOST模式设置: 2向左递增30=10,后跟30个0。 即,100000000 publicstaticfinalintat _ most=2mode _ shift; /** * makeMeasureSpec ()方法)作用:是否根据提供的size和mode获得详细的测量结果? 即,measurespec (*/publicstaticintmakemeasurespec (int mode )、intmode //measureSpec=size mode; 这是二进制加法,而不是十进制//设计目的。 使用32位二进制数。 其中,32位和31位表示测量模式(mode ),后30位表示测量尺寸(size )//例如size=100(4)4)、mode=AT_MOST。 measurespec=10010000 . 00=10000 . 00100 }/* * * getmode ()方法*作用:在measurespec上测量模式(mode ) */publicstaticintgetmetme

ODE_MASK; // MODE_MASK = 运算遮罩 = 11 00000000000(11后跟30个0) //原理:保留measureSpec的高2位(即测量模式)、使用0替换后30位 // 例如10 00..00100 & 11 00..00(11后跟30个0) = 10 00..00(AT_MOST),这样就得到了mode的值 } /** * getSize方法 * 作用:通过measureSpec获得测量大小size **/ public static int getSize(int measureSpec) { return (measureSpec & ~MODE_MASK); // size = measureSpec & ~MODE_MASK; // 原理类似上面,即 将MODE_MASK取反,也就是变成了00 111111(00后跟30个1),将32,31替换成0也就是去掉mode,保留后30位的size } } 5. 计算逻辑

View的MeasureSpec值计算取决于两个因素:

View自身的布局参数(LayoutParams)父容器的测量规格(MeasureSpec)

即View的大小是由自身布局参数(LayoutParams)和父容器的测量规格(MeasureSpec)共同决定的。

MeasureSpec值的具体计算逻辑封装在getChildMeasureSpec()里,具体计算逻辑如下源码所示。

/** * 源码分析:getChildMeasureSpec() * 作用:根据父视图的MeasureSpec & 布局参数LayoutParams,计算单个子View的MeasureSpec * 注:子view的大小由父view的MeasureSpec值 和 子view的LayoutParams属性 共同决定 **/public static int getChildMeasureSpec(int spec, int padding, int childDimension) { // 参数说明// * @param spec 父view的详细测量值(MeasureSpec) // * @param padding view当前尺寸的的内边距和外边距(padding,margin) // * @param childDimension 子视图的布局参数(宽/高) //父view的测量模式 int specMode = MeasureSpec.getMode(spec); //父view的大小 int specSize = MeasureSpec.getSize(spec); //通过父view计算出的子view = 父大小-边距(父要求的大小,但子view不一定用这个值) int size = Math.max(0, specSize - padding); //子view想要的实际大小和模式(需要计算) int resultSize = 0; int resultMode = 0; //通过父view的MeasureSpec和子view的LayoutParams确定子view的大小 // 当父view的模式为EXACITY时,父view强加给子view确切的值 //一般是父view设置为match_parent或者固定值的ViewGroup switch (specMode) { case MeasureSpec.EXACTLY: // 当子view的LayoutParams>0,即有确切的值 if (childDimension >= 0) { //子view大小为子自身所赋的值,模式大小为EXACTLY resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; // 当子view的LayoutParams为MATCH_PARENT时(-1) } else if (childDimension == LayoutParams.MATCH_PARENT) { //子view大小为父view大小,模式为EXACTLY resultSize = size; resultMode = MeasureSpec.EXACTLY; // 当子view的LayoutParams为WRAP_CONTENT时(-2) } else if (childDimension == LayoutParams.WRAP_CONTENT) { //子view决定自己的大小,但最大不能超过父view,模式为AT_MOST resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // 当父view的模式为AT_MOST时,父view强加给子view一个最大的值。(一般是父view设置为wrap_content) case MeasureSpec.AT_MOST: // 道理同上 if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { resultSize = size; resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // 当父view的模式为UNSPECIFIED时,父容器不对view有任何限制,要多大给多大 // 多见于ListView、GridView case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // 子view大小为子自身所赋的值 resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // 因为父view为UNSPECIFIED,所以MATCH_PARENT的话子类大小为0 resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // 因为父view为UNSPECIFIED,所以WRAP_CONTENT的话子类大小为0 resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }

总结如下:

其中的规律总结:(以子View为标准,横向观察)

由于UNSPECIFIED模式适用于系统内部多次measure情况,很少用到,故此处不讨论


区别于顶级View(即DecorView)的测量规格MeasureSpec计算逻辑:取决于 自身布局参数 & 窗口尺寸

6. 总结 本文对自定义View绘制流程中Measure过程的基础MeasureSpec类进行了全面介绍。Carson带你学Android自定义View文章系列:
Carson带你学Android:自定义View基础
Carson带你学Android:一文梳理自定义View工作流程
Carson带你学Android:自定义View Measure过程
Carson带你学Android:自定义View Layout过程
Carson带你学Android:自定义View Draw过程
Carson带你学Android:手把手教你写一个完整的自定义View
Carson带你学Android:Canvas类全面解析
Carson带你学Android:Path类全面解析 欢迎关注Carson_Ho的CSDN博客 与 公众号!

博客链接:https://carsonho.blog.csdn.net/

请帮顶 / 评论点赞!因为你的鼓励是我写作的最大动力!

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。