Android

[Android] 谈一下自定义View的流程

2019-08-01  本文已影响1人  Merbng

抛出

可以从:

  1. 自定义View的步骤;
    2.自定义View的注意事项;
    3.自定义ViewGroup的步骤以及注意事项;
    4.一些特殊需要注意的地方;
    以上几方面进行。

Answer1:

要想充分理解自定义View的流程,就必须对View的绘制流程有深刻理解,下面说几点:

DecorView被加载到Window中

了解绘制的整体流程

绘制会从根视图ViewRootperformTraversals()方法开始,从上到下遍历整个视图树,每个View控件负责绘制自己,而ViewGroup还需要负责通知自己的子View进行绘制操作。

理解MeasureSpec

MeasureSpec表示的是一个32位的整形值,它的高2位表示测量模式SpecMode,低30位表示某种测量模式下的规格大小SpecSizeMeasureSpec是View类的一个静态内部类,它来说明应该如何测量这个View。它有三种测量模式,如下
1.EXACTLY:精确测量模式,视图宽高指定为match_parent或具体数值时生效,表示父视图已经决定了子视图的精确大小,这种模式下View的测量值就是SpecSize的值。
2.AT_MOST:最大值测量模式,当视图的宽高指定为wrap_parent时生效, 此时子视图的尺寸可以是不超过父视图允许的最大尺寸的任何尺寸。
3.UNSPECIFIED:不指定测量模式,父视图没有限制子视图的大小,子视图可以是想要的任何尺寸,通常用于系统内部,应用开发中很少用到。
MeasureSpec通过将SpecModeSpecSize打包成一个int值来避免过多的对象内存分配,为了方便操作,其提供了打包和解包的方法, 打包方法为makeMeasureSpec,解包方法为getMode和getSize
普通ViewmeasureSpce的创建规则如下:

image
对于DecorView而言,它的measureSpec由窗口尺寸和其自身的LayoutParams共同决定;对于普通的View,它的MeasureSpec由父视图的MeasureSpec和其自身的LayoutParams共同决定。

View绘制流程之Measure

getSuggestMinimumWidth分析

如果View没有设置背景,那么返回android:minWidth这个属性所指定的值,这个值可以为0;如果View设置了背景,则返回android:minWidth和背景的最小宽度这两者中的最大值。

自定义View时手动处理wrap_content时的情形

直接继承View的控件需要重写onMeasure方法并设置wrap_content时的自身大小,否则在布局中使用wrap_content就相当于使用match_parent,此时,可以在wrap_content的情况下(对应MeasureSpec.AT_MOST)指定内部宽/高(mWidth和mHeight)。

LinearLayout的onMeasure方法实现解析(这里仅分析measureVertical核心源码)

系统会遍历子元素并对每个子元素执行measureChildBeforeLayout方法,这个方法内部会调用子元素的measure方法,这样各个子元素就开始依次进入measure过程,并且系统会通过mTotalLength这个变量来存储LinearLayout在竖直方向的初步高度。每测量一个子元素,mTotalLength就会增加,增加的部分主要包括了子元素的高度以及子元素在竖直方向上的margin等。

在Activity中获取某个View的宽高

由于View的measure过程和Activity的生命周期方法不是同步执行的,如果View还没有测量完毕,那么获得的宽/高就是0,所以在onCreateonStartonResume中均无法正确得到某个View的宽高信息,解决方式如下:

View的绘制流程之Layout

首先,会通过setFrame方法来设定View的四个顶点的位置,即View在父容器中的位置,然后回执行onLayout空方法,子类如果是ViewGroup类型,则重写这个方法,实现VIewGroup中所有View控件布局流程。

LinearLayout的onLayout方法实现解析(layoutVertical核心代码)

其中会遍历调用每个子View的setChildFrame方法为子元素确定对应的位置。其中的childTop会逐渐增大,意味着后面的子元素会被放置在靠下的位置。
注意:在View的默认实现中,View的测量宽/高和最终宽/高是相等的,只不过测量宽/高形成于View的measure过程,而最终宽/高形成于View的layout过程,即两者的赋值时机不同,测量宽/高的赋值时机稍早一些,在一些特殊情况下则两者不相等:

View的绘制流程之Draw

Draw的基本流程

绘制基本上可以分为六个步骤:

setWillNotDraw的作用

如果一个View不需要绘制任何内容,那么设置这个标记位位true以后,系统会进行相应的优化。

RequestLayout、onLayout、onDraw、onDrawChild区别与联系?

invalidate()和postInvalidate()的区别?

invalidate()postInvalidate()都用于刷新View,主要区别是invalidate()在主线程中调用,若在子线程中使用需要配合handler;而postInvalidate()可在子线程中直接调用。
更详细内容查看这里

Answer2:

大多数自定义View要么在onDraw方法中画点东西,和在onTOuchEvent中处理触摸事件。

自定义View的步骤:
自定义View注意事项:

如果现有ViewGroup的排版或者行为不满足当前需求,就可以自定义ViewGroup

自定义ViewGroup的步骤:

自定义ViewGroup注意事项:

参考链接:
每日一问 | 谈一下自定义 View 的流程

上一篇 下一篇

猜你喜欢

热点阅读