游戏开发工具

本节引言

告别了第一章迎来第二章——Android 中的 UIUser Interface 组件的详解,而本节我们要学习的是所有控件的父类 View 和 ViewGroup 类,突发奇想直接翻译官方文档对这两个东西的介绍吧,对了天朝原因 google 上不去 Android developer 上不去我们可以改 hosts 或者用 vpn 代理,当然也可以像笔者一样使用国内的API镜像,这里分享个吧 http://androiddoc.qiniudn.com/guide/topics/ui/overview.html 这个镜像是 5.0 的 API。


Android 的 UI 界面都是由 View 和 ViewGroup 及其派生类组合而成的。其中,View 是所有 UI 组件的基类,而 ViewGroup 是容纳 View 及其派生类的容器,ViewGroup 也是从 View 派生出来的。

一般来说,开发 UI 界面都不会直接使用 View 和 ViewGroup(自定义控件的时候使用),而是使用其派生类。

下图:UI布局的层次结构。

1.jpg

 

View 和 ViewGroup 的区别

可以从两方面来说:

一、事件分发方面的区别

二、UI 绘制方面的区别

事件分发方面的区别

事件分发机制主要有三个方法:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()

1、ViewGroup 包含这三个方法,而 View 则只包含 dispatchTouchEvent()、onTouchEvent() 两个方法,不包含 onInterceptTouchEvent()。


2、触摸事件由 Action_Down、Action_Move、Action_Up 组成,一次完整的触摸事件,包含一个 Down 和 Up,以及若干个 Move(可以为0);


3、在 Action_Down 的情况下,事件会先传递到最顶层的 ViewGroup,调用 ViewGroup 的 dispatchTouchEvent()

①如果 ViewGroup 的 onInterceptTouchEvent() 返回 false 不拦截该事件,则会分发给子 View,调用子 View 的 dispatchTouchEvent(),如果子 View 的 dispatchTouchEvent() 返回 true,则调用 View 的 onTouchEvent() 消费事件。

②如果 ViewGroup的onInterceptTouchEvent() 返回 true 拦截该事件,则调用 ViewGroup 的 onTouchEvent() 消费事件,接下来的 Move 和 Up 事件将由该 ViewGroup 直接进行处理。


4、当某个子 View 的 dispatchTouchEvent() 返回 true 时,会中止 Down 事件的分发,同时在 ViewGroup 中记录该子 View。接下来的 Move 和 Up 事件将由该子 View 直接进行处理。


5、当 ViewGroup 中所有子 View 都不捕获 Down 事件时,将触发 ViewGroup 自身的 onTouch()

触发的方式是调用 super.dispatchTouchEvent 函数,即父类 View 的 dispatchTouchEvent 方法。在所有子 View 都不处理的情况下,触发 Acitivity 的 onTouchEvent 方法。


6、由于子 View 是保存在 ViewGroup 中的,多层 ViewGroup 的节点结构时,上层 ViewGroup 保存的会是真实处理事件的 View 所在的 ViewGroup 对象。

如 ViewGroup0——ViewGroup1——TextView 的结构中,TextView 返回了 true,它将被保存在 ViewGroup1 中,而 ViewGroup1 也会返回 true,将被保存在 ViewGroup0 中;当 Move 和 Up 事件来时,会先从 ViewGroup0 传递到 ViewGroup1,再由 ViewGroup1 传递到 TextView,最后事件由 TextView 消费掉。


7、子View 可以调 getParent().requestDisallowInterceptTouchEvent(),请求父 ViewGroup 不拦截事件。

 

UI绘制方面的区别:

UI绘制主要有五个方法:onDraw(),onLayout(),onMeasure(),dispatchDraw(),drawChild()

1、ViewGroup 包含这五个方法,而 View 只包含 onDraw(),onLayout(),onMeasure() 三个方法,不包含 dispatchDraw(),drawChild()。

2、绘制流程:onMeasure(测量)——> onLayout(布局)——> onDraw(绘制)


3、绘制按照视图树的顺序执行,视图绘制时会先绘制子控件。

如果视图的背景可见,视图会在调用 onDraw() 之前调用 drawBackGround() 绘制背景。强制重绘,可以使用 invalidate();


4、如果发生视图的尺寸变化,则该视图会调用 requestLayou(),向父控件请求再次布局。

如果发生视图的外观变化,则该视图会调用 invalidate(),强制重绘。如果 requestLayout() 或  invalidate() 有一个被调用,框架会对视图树进行相关的测量、布局和绘制。

注意:视图树是单线程操作,直接调用其它视图的方法必须要在UI线程里。跨线程的操作必须使用Handler。


5、onLayout()

对于 View 来说,onLayout() 只是一个空实现;而对于 ViewGroup 来说,onLayout() 使用了关键字 abstract 的修饰,要求其子类必须重载该方法,目的就是安排其 children 在父视图的具体位置。


6、draw 过程

drawBackground() 绘制背景 ——> onDraw() 对 View 的内容进行绘制 ——> dispatchDraw() 对当前 View 的所有子 View 进行绘制 ——> onDrawScrollBars() 对 View 的滚动条进行绘制。


2.jpg

 

方法说明:

1、onDraw(Canvas canvas)

UI 绘制最重要的方法,用于 UI 重绘。这个方法是所有 View、ViewGroup 及其派生类都具有的方法。自定义控件时,可以重载该方法,并在内容基于 canvas 绘制自定义的图形、图像效果。


2、onLayout(boolean changed, int left, int top, int right, int bottom)

布局发生变化时调用此方法。这个方法是所有 View、ViewGroup 及其派生类都具有的方法。自定义控件时,可以重载该方法,在布局发生改变时实现特效等定制处理。


3、onMeasure(int widthMeasureSpec, int heightMeasureSpec)

用于计算自己及所有子对象的大小。这个方法是所有 View、ViewGroup 及其派生类都具有的方法。自定义控件时,可以重载该方法,重新计算所有对象的大小。 MeasureSpec 包含了测量的模式和测量的大小,通过 MeasureSpec.getMode() 获取测量模式,通过 MeasureSpec.getSize() 获取测量大小。mode 共有三种情况: 分别为MeasureSpec.UNSPECIFIED( View想多大就多大), MeasureSpec.EXACTLY(默认模式,精确值模式:将 layout_width 或 layout_height 属性指定为具体数值或者 match_parent。)MeasureSpec.AT_MOST( 最大值模式:将 layout_width 或 layout_height 指定为 wrap_content)。


4、dispatchDraw(Canvas canvas)

ViewGroup 及其派生类具有的方法,主要用于控制子 View 的绘制分发。自定义控件时,重载该方法可以改变子 View 的绘制,进而实现一些复杂的视效。


5、drawChild(Canvas canvas, View child, long drawingTime)

ViewGroup 及其派生类具有的方法,用于直接绘制具体的子 View。自定义控件时,重载该方法可以直接绘制具体的子 View。