codecamp

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。        


Android 反编译APK获取代码&资源
Android LinearLayout(线性布局)
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

第二章——Android中的UI组件的详解

第十一章——由来、答疑和资源

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }