自定义View

作者: 总会颠沛流离 | 来源:发表于2019-04-29 19:34 被阅读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)执行之后才有效。那我们看下

    相关文章

      网友评论

        本文标题:自定义View

        本文链接:https://www.haomeiwen.com/subject/szyynqtx.html