Android View与ViewGroup的概念
本节引言
告别了第一章迎来第二章——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布局的层次结构。
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 的滚动条进行绘制。
方法说明:
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。
更多建议: