自定义view

自定义View

2019-04-29  本文已影响9人  总会颠沛流离
image

自己理解

通常自定义view分为

1.继承现有控件,对其控件的功能进行拓展。
2.将现有控件进行组合,实现功能更加强大控件。
3.重写View实现全新的控件
一般当我们遇到了原生控件无法满足我们现有的需求的时候,我们就可以创建一个全新的View来实现我们所需要的功能。创建一个全新View实现自定义控件,无非分成这么几步:

自定义View分为两大块。

自定义控件 和 自定义容器
自定义View必须重写两个构造方法
第一个是一个参数的上下文,用于在java代码中new对象使用
第二个是两个参数的一个上下文,一个AttributSet。 主要用于在xml中定义使用。
OnMesure 计算出控件的大小。
onLayout 计算出控件的位置。
onDraw 画出样式
ViewGroup\View的绘制流程:
第一步:调用ViewGroup中的onMeasure方法。
在方法中调用了measureChild方法,执行了所有子控件的onMesure方法测绘出所有的子控件的大小。
调用setMeasureDimension方法 设置测绘后的大小。
第二步:调用ViewGroup中的onLayout方法。
在方法调用getChildCount方法 获取到子条目数量。
用for循环遍历出每一个子条目的对象。 通过对象.layout方法 给子控件设置摆放位置。
第三步:首先调用ViewGroup的disPatchDraw方法绘制ViewGroup。然后调用View中的onDraw方 进行绘制。
方法详解:
onMeasure:用于设置自定义view的大小
setMeasuredDimension(); 设置测绘后的大小。
方法内部需要调用MeasureSpec类 可以获取到view的模式 和 大小;
MeasureSpec.getMode()获取模式
MeasureSpec.getSize()获取大小​
模式:
MeasureSpec.EXACTLY 精确值模式: match_parent 或者 固定一个值(写出具体的dp值,)时使用。
MeasureSpec.AT_MOST 最大值模式: warp_content 当不确定大小时使用。但是不超过父控件
MeasureSpec.UNSPECIFIED 一般在scrollView或者listview中,要多大就多大
onDraw方法:
用于绘制自定义View。
主要使用到了Canvas 画布对象。 和Paint 画笔对象 进行的绘制。

注意

MeasureSpec在很大程度上决定了一个View的尺寸规格,
makeMeasureSpec()方法的作用将size 和 mode 打包成一个32位的int值,之所以这样做就是为了减少内存的分配。返回值为打包成的int类型值measureSpec 。
1.getMode 和 getSize 则是根据传入的int 类型值,解包成为 mode 和 size。
2 只处理AT_MOST情况也就是wrap_content,其他情况则沿用系统的测量值即可。
setMeasuredDimension会设置View宽高的测量值,只有setMeasuredDimension调用之后,才能使用getMeasuredWidth()和getMeasuredHeight()来获取视图测量出的宽高,以此之前调用这两个方法得到的值都会是0。
如果我们不处理AT_MOST情况,那么即使设置了wrap_content,最终的效果也和match_parent一样,这是因为这种情况下,view的SpecSize就是父容器测量出来可用的大小。

一、view树的绘制流程

measure--->layout--->draw

measure

image

1、ViewGroup.LayoutParams 指定部件的长宽
2、MeasureSpec 32位的int值 前两位代表模式 后30位测量规格的大小

layout

image

draw

invalidate()请求android系统 如果大小没有发生变化 就不会调用layout放置这个过程

requestLayout() 当布局发生变化时 希望重新测量尺寸大小 就会触发 measure和layout 但不会调用draw方法

二、View的绘制流程:OnMeasure()——>OnLayout()——>OnDraw()

OnMeasure():测量视图大小。从顶层父View到子View递归调用measure方法,measure方法又回调OnMeasure。

OnLayout():确定View位置,进行页面布局。从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。

OnDraw():绘制视图(利用Canvas(画布)与Paint(画笔)来绘制要显示的内容)。ViewRoot创建一个Canvas对象,然后调用OnDraw()。

draw过程

六个步骤:①、绘制视图的背景;②、保存画布的图层(Layer);③、绘制View的内容;④、绘制View子视图,如果没有就不用;⑤、还原图层(Layer);⑥、绘制滚动条。

OnMeasure

View树的绘制是从ViewRoot的performTraversals()方法开始,这个方法的主要作用是判断是否重新measure、是否重新layout、是否重新draw。如果是ViewGroup,还应该进行嵌套测量。

MeasureSpec 的值由specSize和specMode共同组成的,高2位代表specMode,低30代表spceSize,其中specSize记录的是大小,specMode记录的是规格。

specMode一共有三种类型:

EXACTLY

表示父视图希望子视图的大小应该是由specSize的值来决定的,系统默认会按照这个规则来设置子视图的大小,开发人员当然也可以按照自己的意愿设置成任意的大小。

AT_MOST

表示子视图最多只能是specSize中指定的大小,开发人员应该尽可能小得去设置这个视图,并且保证不会超过specSize。系统默认会按照这个规则来设置子视图的大小,开发人员当然也可以按照自己的意愿设置成任意的大小。

UNSPECIFIED

表示开发人员可以将视图按照自己的意愿设置成任意的大小,没有任何限制。这种情况比较少见。

每个View控件的实际宽高都是由父视图和自身决定的。实际的测量是在onMeasure方法进行,所以在View的子类需要重写onMeasure方法,这是因为measure方法是final的,不允许重载,所以View子类只能通过重载onMeasure来实现自己的测量逻辑。

OnLayout

测量完各个组件的大小之后,就可以排列他们的位置了。
View中的onLayout()方法就是一个空方法,因为onLayout()过程是为了确定视图在布局中所在的位置,而这个操作应该是由布局来完成的,即父视图决定子视图的显示位置。
然而,ViewGroup中的onLayout()方法是一个抽象方法,这就意味着所有ViewGroup的子类都必须重写这个方法。自定义ViewGroup控件中,onLayout配合onMeasure方法一起使用可以实现自定义View的复杂布局。自定义View首先调用onMeasure进行测量,然后调用onLayout方法动态获取子View和子View的测量大小,然后进行layout布局。重载onLayout的目的就是安排其children在父View的具体位置,重载onLayout通常做法就是写一个for循环调用每一个子视图的layout(l, t, r, b)函数,传入不同的参数l, t, r, b来确定每个子视图在父视图中的显示位置。
到这里就不得不提getWidth()、getHeight()和getMeasuredWidth()、getMeasuredHeight()这两对方法之间的区别(上面分析measure过程已经说过getMeasuredWidth()、getMeasuredHeight()必须在onMeasure之后使用才有效)。可以看出来getWidth()与getHeight()方法必须在layout(int l, int t, int r, int b)执行之后才有效。那我们看下

上一篇下一篇

猜你喜欢

热点阅读