Android 自定义View绘制的基本开发流程 Android自定义View(二)
1 View绘制的过程
- View的测量——onMeasure()
- View的位置确定——onLayout()
- View的绘制——onDraw()
2 View的测量——onMeasure()
Android中View的绘制前,先要进行测量,会回调方法onMeasure()
@Override
public void oMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
- MeasureSpec类 它是Android系统为我们设计的一个短小而又强悍的一个类,通过它来帮助我们测量View。它是一个32位的int值,其中前两位代表测量的模式,后30位代表测量出的大小。
- 测量模式分为三种:
- EXACTLY 精确值模式,即当我们在布局文件中为View指定了具体的大小,例如:android:layout_width=“100dp”,或者当我们将View的大小指定为充满父布局,即为match_parent时,此时,该View的测量模式即为EXACTLY模式。(View的默认测量模式为EXACTLY模式)
- AT_MOST 最大值模式,此时View的尺寸不得大于父控件允许的最大尺寸即可。即对应我们给View的宽或高指定为wrap_content时。
- UPSPECIFIED不指定测量模式,即父视图没有限制其大小,子View可以是任何尺寸。该模式一般用于系统内部,平时的Android开发基本用不到。
2.1 简述 View onMeasure中的测量值的获取
如前面所说,View的测量是从measure方法开始的,其是一个用final修饰的方法,这就说明此方法其子类是不能重写的,而在这个方法中,会调用View的onMeaure方法,如下所示
protect void onMeasure(int widthMeaureSpec, int heightMeasureSpec) {setMeasureDismension(getDefaultSize(getSuggestedMinimumWith(), widthMeaureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
其中,setMeasureDismension方法会进行View的宽高的测量值的设置,而getDefaultSize方法如下:
public static int getDefaultSize(int size, int measureSpec) {int result = size;//获取当前View的测量模式int specMode = MeasureSpec.getMode(measureSpec);//精准模式获取当前Viwe测量后的值,如果是最大值模式,会获取父View的大小.int specSize = MeasureSpec.getSize(measureSpec);switch(specMode) {case MeasureSpec.UNSPECIFIED:result = size;break;//当测量模式为精准模式,返回设定的值case MeasureSpec.AT_MOST:case MeasureSpec.EXACTLY:result = specSize;break;}return result;
}
可以看到,该方法的内部实现很简单,其返回值即为View测量后的大小,即measureSpec中的specSize。(这里并不是View最终的大小,因为View最终的大小需要在layout过程中确定,但其实这里测量值的大小和最终的大小几乎所有的情况下都是一样的)。
2.2 简述 ViewGroup 中的测量值的获取
对于ViewGroup来说,它除了要完成自身的测量,还要遍历自己所有的子View,让它们去调用自身的measure方法完成测量。这里ViewGroup不同的一点是,ViewGroup是一个抽象类,并没有实现onMeasure方法,而它提供了一个measureChildren方法来遍历自身所有的子View去调用measureChild方法。方法如下
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { final int size = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < size; ++i) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { measureChild(child, widthMeasureSpec, heightMeasureSpec); } }
}
而measureChild方法中,会获得子元素的LayoutParams属性,再通过getChildMeasureSpec方法来创建子元素的MeasureSpec,最后再调用measure方法并将获得的MeasureSpec传递给measure方法中。接着就是执行View的measure方法了,之后的流程就和前面说到的一样了。其具体的方法体如下:
protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { final LayoutParams lp = child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
ViewGroup没有具体的实现onMeasure方法,是因为ViewGroup的子类有很多种,其对应的布局的特性也不尽相同,这就会导致他们的测量细节会有所不同,因此这里并没有对onMeaure方法做统一的实现。
3 View的位置确定——onLayout()
在测量好View的大小之后,我们就要确定一下View的最终位置了。那么就追溯到我们最开始说的,ViewRoot在执行过performMeasure方法后会执行performLayout方法,而再去调用layout方法来确定View的位置
public void layout(int l, int t, int r, int b) { int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight; boolean changed = setFrame(l, t, r, b); if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT); } onLayout(changed, l, t, r, b); mPrivateFlags &= ~LAYOUT_REQUIRED; if (mOnLayoutChangeListeners != null) { ArrayList<OnLayoutChangeListener> listenersCopy = (ArrayList<OnLayoutChangeListener>) mOnLayoutChangeListeners.clone(); int numListeners = listenersCopy.size(); for (int i = 0; i < numListeners; ++i) { listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB); } } } mPrivateFlags &= ~FORCE_LAYOUT;
}
在layout方法中,首先会通过setFrame方法对View的四个顶点的值进行赋值,即mLeft, mRight, mTop, mBottom。当各个顶点的坐标确定以后,View在ViewGroup中的位置也就确定了。接着就要调用onLayout方法用来确定子元素的位置了。而对于View的onLayout方法,这里要说的是,它是一个空方法,至于为什么,估计大家应该也能想的通,因为onLayouta方法就是为了确定子元素在ViewGroup中的位置,这个功能方然要有ViewGroup去实现了啊。而我们点击进入ViewGroup的onLayout方法,如下:
@Override
protected abstract void onLayout(boolean changed, int l, int t, int r, int b);
在onLayout方法中,我们就可以通过getWidth和getHeight方法来获取View的宽和高了。这时,估计又有人会产生一个疑问了,在之前说的在调用过setMeaureDismension后,可以通过getMeasureWidth和getMeasureHeight方法获取View的宽高。那么这两种方式又有什么区别呢。getMeasureWidth的值是在onMeasure之后能获取到的,而getWidth的值是在onLayout方法之后才能获取到。而getWidth的值其实就是View的两边坐标的差值。二者在数值上几乎所有的情况下都是相同的。而如果有时我们刻意去重写layout方法,并修改方法中的参数huozhe,那么就会造成二者的值不相同
4 View的绘制——onDraw()
完成了测量和位置确立,那就差把View绘制出来以让我们看到了。这是就要开始进入我们的绘制流程了。在调用了layout方法后,接着ViewRoot会常见一个Canvas对象,接着调用View的draw方法来执行具体的绘制流程。
Android 自定义View绘制的基本开发流程 Android自定义View(二)相关推荐
- 微信Android客户端架构演进及其对开发流程的影响
微信Android客户端架构演进及其对开发流程的影响 http://www.infoq.com/cn/presentations/android-client-architecture-evoluti ...
- Android事件分发机制在实战开发中的应用之二
学习的最终目标就是要学以致用,本文所分享的案例都是自己在公司实战开发过程中的真实案例,现在把它分享出来,希望对初学者有所帮助 版权声明:本文来自门心叼龙的博客,属于原创内容,转载请注明出处:https ...
- android view绘制中调用的函数,Android开发实践:自定义带动画的View
前面两篇文章介绍了自定义View的onMeasure和onLayout原理,本文准备介绍自定义View的第三个关键部分,即onDraw()函数的重载. 对于一个自定义View来说,onMeasure只 ...
- 微信公众号接入第三方服务器,设置自动回复、关键回复、自定义菜单,配置及开发流程
首先需要确认一点,一旦接入第三方服务器,微信就认为你已经具备了开发能力,像自动回复.关键词回复.自定义菜单这些功能,微信公众平台就不再提供了(需要开发者调用相关接口),停用服务器之后,这些功能也就恢复 ...
- 微信公众号接入第三方服务器,设置自动回复、关键回复、自定义菜单,配置及开发流程...
首先需要确认一点,一旦接入第三方服务器,微信就认为你已经具备了开发能力,像自动回复.关键词回复.自定义菜单这些功能,微信公众平台就不再提供了(需要开发者调用相关接口),停用服务器之后,这些功能也就恢复 ...
- Android 10.0 系统服务之ActivityMnagerService-AMS启动流程-[Android取经之路]
摘要:上一节我们讲完了SystemServer的启动过程,这一节接着上一节的步骤,来讲解ActivityManagerService的启动过程. ActivityManagerService简称AMS ...
- android odex版本调试_[ROM开发工具]Android 8.0 9.0合并ODEX工具
编译过程: Clone this repository Install Android NDK if you want to cross-compile for Android devices Inv ...
- android paint 圆角 绘制_[BOT] 一种android中实现“圆角矩形”的方法
内容简介 文章介绍ImageView(方法也可以应用到其它View)圆角矩形(包括圆形)的一种实现方式,四个角可以分别指定为圆角.思路是利用"Xfermode + Path"来进行 ...
- 用最新版的Android Studio和Gradle把自己开发的Android包发布到JitPack上
使用的Android Studio版本: Android Studio Chipmunk | 2021.2.1 Patch 1 Build #AI-212.5712.43.2112.8609683, ...
最新文章
- 区分docker stack/service/task
- python选课系统_【精选】在Monash读Data Science,人人都拥有这样一份选课指南。
- shopex还是ecshop
- 【Win32汇编】复制字符串
- 将权限授予文件夹和程序集
- android 文件上传类(可以直接被调用的)
- 大幅减少GPU显存占用:可逆残差网络(The Reversible Residual Network)
- rocketMq发送事务消息
- JSON与XML优缺点对比分析
- 5. JavaScript RegExp 类型
- 电脑手写输入法_5款好用的拼音输入法软件推荐
- 微波存在感应雷达,人体存在感应雷达模块,物联网智能赋能应用
- 首份2020信创报告出炉,四大巨头市场格局立现(附全文下载)
- Java输入/输出(I/O)流
- coreldraw2022(CDR 2022)中文新增更新内容介绍win/mac
- Matplotlib 绘图库从入门到精通
- AC-Campus准入控制--Mac
- 华为开发者大会2022,发布鸿蒙开发套件
- qt android刘海屏状态栏,安卓手机刘海屏算抄袭苹果iPhone X吗?真相了
- 【转】网站流量UV是什么意思?什么是流量UV?
热门文章
- 韩国ETRI提出实时Anchor-Free实例分割算法CenterMask,代码将开源
- Github 1300+ 星!旷视开源的深度强化学习绘画智能体论文解读
- 刷新记录! CVPR2021全新目标检测机制达到SOTA!
- php狼和兔子算法,PHP基于递归算法解决兔子生兔子问题php技巧
- java 反射 单例类_利用反射机制破坏单例模式
- 数据结构(十一)桶排序
- 如何识别新加的计算机硬盘,图文解说win10系统无法识别新加的机械硬盘的具体技巧...
- c语言中字符串数组应用,C语言中字符变量字符串和字符数组应用.doc
- cli版的php.ini路径,CLI 执行 PHP 时自订 php.ini 设定档
- 新电脑怎么分盘_新买的笔记本电脑收货后,该如何验机?