33、Android--自定义控件流程
2021/6/30 6:20:38
本文主要是介绍33、Android--自定义控件流程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
自定义控件流程
View 和 ViewGroup
View的工作流程主要是指measure、layout和draw三大流程,即测量、布局和绘制。
View的位置参数
View的位置参数如下图所示:
通过上图,我们可以很方便的了解View的位置参数,View和MotionEvent提供的获取坐标的方法如下表所示:
方法 | 描述 |
---|---|
View的获取坐标的方法: | |
getTop() | 获取View自身的定边到其父布局顶边的距离。 |
getLeft() | 获取View自身的左边到父布局左边的距离。 |
getRight() | 获取View自身的右边到父布局左边的距离。 |
getBottom() | 获取View自身的底边到父布局顶边的距离。 |
MotionEvent获取坐标的方法: | |
getX() | 获取点击事件距离控件左边的距离,即视图坐标。 |
getY() | 获取点击事件距离控件顶边的距离,即视图坐标。 |
getRawX() | 获取点击事件距离整个屏幕左边的距离,即绝对坐标。 |
getRawY() | 获取点击事件距离屏幕顶边的距离,即绝对坐标。 |
View的测量
当我们对View和ViewGroup进行测量时,首先是获取它的宽高信息,获取的方式有如下三种:
方法 | 描述 |
---|---|
getMeasuredWidth() | 对View上的内容进行测量后得到的View内容占据的宽度。 |
getWidth() | View在设定好布局后整个View的宽度,也就是在onLayout之后。 |
getLayoutParams().width | 测量后就确定值,getLayoutParams.width比getMeasureWidth多了margin和padding。 |
所以,在自定义控件时,有时候会获取到宽高信息为0的情况,就要对照上面的表格进行排查。
(1)View的测量
在Measure过程中,系统会将View的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec,然后再根据这个MeasureSpec来测量View的宽高。
MeasureSpec代表一个32位的int值,高2位代表SpecMode(测量模式),低30位代表SpecSize(规格大小),可以通过getSize()和getMode()来获取相应的值。
测量的模式可以分为以下三种:
模式 | 描述 |
---|---|
EXACTLY | 即精确模式,当控件的layout_width或layout_height为具体数值时,系统使用的是EXACTLY模式。 |
AT_MOST | 最大值模式,当控件的layout_width或layout_height为wrap_content或match_parent时,控件的尺寸不能超过父控件允许的最大尺寸即可。 |
UNSPECIFIED | 未指定模式,它不指定其大小测量模式,View想多大就多大。 |
系统最终会调用setMeasuredDimension(int measuredWidth,int measuredHeight)方法将测量后的宽高传递进去,以完成测量操作。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } /**测量宽度的模板代码*/ private int measureWidth(int measureSpec){ int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if(specMode == MeasureSpec.EXACTLY){ // 精确数值 result = specSize; }else{ // 非精确数值 result = 200; if(specMode == MeasureSpec.AT_MOST){// 自动包含 result = Math.min(result, specSize);// 取出指定大小与specSize中最小一个作为最后测量值。 } } return result; } /**测量高度的模板代码*/ private int measureHeight(int measureSpec){ int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if(specMode == MeasureSpec.EXACTLY){ result = specSize; }else{ result = 200; if(specMode == MeasureSpec.AT_MOST){ result = Math.min(result, specSize); } } return result; }
a) 布局文件中指定精确的宽高值是400px时,View会根据指定的宽高进行设定。
b) 当指定宽高属性为match_parent时,View会填充整个父布局。
c) 当指定宽高属性为wrap_content时,如果不重写onMeasure()方法则会填充整个父布局,重写的话则会根据内容自动包含。
(2)ViewGroup的测量
当ViewGroup大小为wrap_content时,需要对子View进行遍历,以便根据所有子View的大小,来确定自身的大小。
ViewGroup调用子View的measure()方法遍历测量后,获取到子View的测量结果,然后打包成MeasureSpec传递给子View。
// measureChild(View, int, int)为子组件添加Padding measureChild(child, parentWidthMeasureSpec, parentHeightMeasureSpec); // measureChildren(int, int)根据指定的高和宽来测量所有子View中显示参数非GONE的组件。 measureChildren(widthMeasureSpec, heightMeasureSpec); // measureChildWithMargins(View, int, int, int, int)测量指定的子组件,为子组件添加Padding和Margin。 measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
View的绘制
装载画布
Android中,创建画布有两种方式:
Canvas canvas = new Canvas(); 或 Canvas canvas = new Canvas(bitmap);
当在创建画布传入bitmap对象时,bitmap和画布是紧紧相连的,这个过程我们称之为装载画布。
这个bitmap用来存储所有绘制在Canvas上的像素信息。且Canvas调用所有的Canvas.drawXXX方法都发生在该bitmap上。
装载画布时,当Canvas将绘制效果作用在bitmap时,刷新view就会改变bitmap,如果非装载画布模式下,改变的是bitmap对象,并让view重绘。
绘制解析
Android系统中要自定义view,首先需要了解Android的view加载机制。主要有三个方法:
1、onMeasure() //计算出view自身大小
2、onLayout() //仅在ViewGroup中,用来为子view指定位置(left,top)
3、onDraw() //view绘制内容
下面根据源码中的相关说明,进一步分析控件的绘制操作及顺序:
/* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: * 1. Draw the background 绘制控件设置的背景 * 2. If necessary, save the canvas' layers to prepare for fading 保存画布的图层和阴影信息 * 3. Draw view's content 重写onDraw(canvas)进行绘制 * 4. Draw children 绘制子控件,对应方法dispatchDraw(canvas) * 5. If necessary, draw the fading edges and restore layers 绘制控件阴影渐变效果 * 6. Draw decorations (scrollbars for instance) 绘制滚动条,对应方法onDrawScrollBars(canvas) */
在第四步时,如果当前需要绘制的控件是ViewGroup,则需要通过dispatchDraw()方法绘制子控件,如果是View则不需要。
通常情况下ViewGroup不需要进行绘制,因为其本身没有需要绘制的东西,如果不是指定背景色,那么ViewGroup的onDraw方法不会被调用。
但是,ViewGroup会通过dispatchDraw()方法来绘制其子View。
下面我们看看onDraw()和dispatchDraw()的区别:
- 绘制View本身内容时,可以调用View.onDraw(Canvas canvas)方法。
- 绘制View的子View的内容时,可以调用diapatchDraw方法。
这篇关于33、Android--自定义控件流程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-01-18android.permission.read_media_video
- 2024-01-18android_getaddrinfo failed eai_nodata
- 2024-01-18androidmo
- 2024-01-15Android下三种离屏渲染技术
- 2024-01-09Android 蓝牙使用
- 2024-01-06Android对接华为AI - 文本识别
- 2023-11-15代码安全之代码混淆及加固(Android)
- 2023-11-10简述Android语音播报TTS
- 2023-11-06Android WiFi工具类
- 2023-07-22Android开发未来的出路