一、说明
笔记主要是《Android开发艺术探索》的阅读笔记和自己的理解,笔记中部分内容引自《Android开发艺术探索》。
因此需要着重感谢任玉刚先生和他所著的《Android开发艺术探索》;然后感谢任玉刚先生授权我使用小部分《Android开发艺术探索》原文。
推荐Android开发者购买正版《 Android开发艺术探索》,该书是初级Android开发者进阶的必备良品!
由于本人水平有限,其中出现的错误或者不合理的地方望各位读者多多包含,并指出其中不合理和错误的地方,以便我来修正。谢谢!
二、笔记时间
2018年10月11日
三、简述
本文主要记录《Android开发艺术探索》中第四章中的重要知识点,其中包含measure、layout、draw、自定义View等一些重要知识细节。
文中加入了一些个人理解,可能有一些说明不合理或者错误之处,望各位读者指出错误的地方,以便我来修改错误,避免误导读者。谢谢!
四、详情
ViewRoot是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完成的。
View的回执流程是从ViewRoot的performTraversals方法开始的,它经过measure、layout和draw三个过程才能最终将一个View绘制出来,其中measure用来测量View的宽高,layout用来确定View中父容器中的位置,而draw则负责将View绘制在屏幕上。
1、View的工作流程
1.1、measure过程
1)View的measure过程:View的measure过程由其measure方法来完成,measure方法是一个final类型的方法,也就是说子类不能被重写此方法,这View的measure方法中会去调用View的onMeasure方法,可以在onMeasure自测算宽高。
2)ViewGroup的measure过程:对于ViewGroup来说,除了自己的measure过程以外,还会遍历子元素的measure方法,各个子元素再递归去执行这个过程。和View不同的是,ViewGroup是一个抽象类,因此它没有重写View的onMeasure方法,但是它提供了measureChildren的方法。
1.2、layout过程
Layout的作用是ViewGroup用来确定子元素的位置,当ViewGroup的位置被确定后,它在onLayout中遍历所有的子元素并调用其他layout方法,在layout方法中onLayout方法又会被调用。layout方法确定View本身的位置,而onLayout方法则确定所有子元素的位置。
1.3、draw过程
Draw是将View绘制到屏幕上。View的绘制过程遵循以下几步:
1)绘制背景background.draw(canvas);
2)绘制自己(onDraw);
3)绘制children(dispatchDraw);
4)绘制装饰(onDrawScrollBars)。
2、自定义View
2.1、自定义View的分类
1)继承View重写onDraw方法:这种方法主要用于实现一些不规则的效果。这些效果需要通过重写onDraw方法来实现。采用这种方法需要自己支持warp_content、padding。
2)继承ViewGroup派生特殊的Layout:此方法主要用于实现自定义布局,适合几种View组合在一起的情况。这种方式需要合理的处理ViewGroup的测量、布局这两个过程,并处理子元素的测量和布局过程。
3)继承特定的View:这种适合扩展某种已有的View的功能,比如TextView。这种不需要自己支持warp_content和padding。
4)继承特定的ViewGroup(比如 LinearLayout):这种方法和第二种是一样的,只是第二种更加接近View的底层。这种方法不需要自己处理ViewGroup的测量和布局这两个过程。
2.2、自定义View须知
以下问题处理不好,有些会影响View的正常使用,有些则会导致内存泄露等。
1)让View支持warp_content:这是因为直接继承View或者ViewGroup的控件,如果不在onMeasure中对warp_content做特殊处理,那么当布局中使用warp_content时就无法达到预期的效果。
2)如果有必要,让View支持padding:这是因为直接继承View的控件,如果不在draw中处理padding,那么padding属性是无法起到作用的。另外,直接继承ViewGroup的控件需要在onMeasure和onLayout中考虑padding和子元素的margin对其造成的影响,不然将导致padding和子元素的margin失效。
3)尽量不要在View中使用Handler:因为View内部本身提供了post系列的方法,完全可以代替Handler的作用,除非非常明确的使用Handler来发送消息。
4)View中如果有线程或动画,需要及时停止:如果不及时处理这种问题,有可能会造成内存泄露。如果有线程或者动画需要停止时,可以在onDetachedFromWindow中处理,因为当包含此View的Activity退出或者View被remove时,都会调用onDetachedFromWindow方法;和此方法对应的是onAttachedToWindow,当包含此View的Activity启动时,onAttachedToWindow方法会被调用。
5)View带有滑动嵌套时,需要处理好滑动冲突:如果不处理好滑动冲突,会影响View的效果和体验。