自定义视图

来源:互联网 发布:跨域上传图片 java 编辑:程序博客网 时间:2024/05/16 17:01

画布是无穷大的,即可以绘制任意多的内容或任意大的图形(只要内存足够大,然而实际应用中并不会绘制一个无穷大的界面,那么到底应该绘制一个多大的界面呢?对于不同类型的View,其绘制的大小有所不同,一般分为两种情况,一种是内容型视图,另一种是图形型视图 。所谓内容型视图一般是指,该视图将显示一段文本内容,比如TextView,其绘制指的就是显示一段文本内容,内容有多少,就应该绘制多少,所以视图的大小由内容的多少决定。所谓图形型视图一般是指,该视图显示的是一个图形,比如三角形、正方形、背景图等,此时该视图的大小往往会根据父视图为该View 开了一个多大的“ 窗口 ” 而动态调整。 “ 窗口 ” 是指,在父视图看 来 该 View 应该占多大的区域,即 布 局 (layout)大小。换句话说,该视图的大小是“ 被父窗体主宰” 的layoutxml文件中layout_height和 layout_width属性设置的宽和高不是指视图的大小,而是指父视图给该View设置的“窗

口” 大小,这就是为什么这个属性的名称是以“ layout—” 为前缀,而不是直接使用width和 height的原因。该属性值可以是一个“ 相对值”,比如 WRAPCONTENT、 MATCHPARENT等 ,也可以是一个具体值,比 如 lOOdipView类内部用两个变量measuredWidth和 measuredHeight保存 视 图 的 布 局 (layout)大小值。这两个更应

该被命名为layoutWidth和 layoutHeighView内部不是有mLeftmRigthmTopmBottom四个变量吗,它 与 measuredWidth又有什么关系呢?

实际上,这四个变量指的是该View在父视图中所占的区域, mRight-mLeft的大小一般就是measuredWidth的大小, mBottom-mTop就是measuredHeigth的大小。measure过程的本质是把视图布局时使用的“ 相对值” 转换为具体值的过程,即 把 WRAP_COTENT及 MATCH_PARENT转换为具体的值。如果 Framework中不使用相对值,那么也就完全不需要measure过程了。

View系统启动measure过程是从ViewRoot中调用 host.measure()开始

ViewGroup类 是 一 个abstract类 ,应用程序必须实现一个具体的ViewGroup实例。在该实例中,程序员应该调用 measureChildWithMarginO对 下 一 层 的 子 视 图 继 续 进 行 measure()操 作 ,但 这 不 是 必 需 的 , 因 为 measure()仅仅是把layout width和 layoutheight置的相对值转换为具体值将layout程中助父视图对视图进行布局操作。如 果 某 个ViewGroup实例对子视图的布局不依赖子视图的大小,那么就不需要对所包含的子视图进行measure操作。

View中 measure()数原型为void  measure(int widthMeasureSpec, int heightMeasureSpec)

参数 widthMeasureSpec 和 heightMeaureSpec 分别对应宽和高的 measureSpec., 该参数是父视图传递给子视图的,measureSpec是 一 个int型值,当父视图对子视图进行measure操作时,会调用子视图的measure()数,该参数的意思是父视图所提供的measure的 “ 规格”,因为父视图最终为子视图提供的“ 窗口”大小是由父视图和子视图共同决定的 该值由高32位 和 低16位组成,其中高 32位保存的是specMode,低 16位 为 specSizespecMode有三种,分别如下。MeasureSpec.EXACTLY AT_MOST UNSPECIFIED

MeasureSpec.EXACTLY: “ 确定的”,父元素确定子元素的确切大小,子元素将被限定在给定的边界而忽略自身的大小。

• MeasureSpec .AT_MOST: “ 最多”,意思是说,子视图的大小最多是specSize中指定的值。在一般情况下, View 的设计者应该尽可能小地设置视图的大小,并且不能超过specSize,当然,也可以超过specSize

• MeasureSpec .UNSPECIFIED未指定,父元素不对元素施加任何束缚,子元素可以得到任意的大小。

视图大小是由父视图和子视图共同决定的. LinearLayout中 的 layout—height扮演了两个角色,一个是 和 LinearLayout的父视图一起对LinearLayout本 身 进 行measure操作;另一个角色是作为 TextView的 measureSpec,并 和 TextView中 的 layout_heigtht —起 对 TextView进 行 measure操作.也就是说onMeasure中的两个参数就是父视图的layout_witdh等大小。

ViewGroup提供了三个类似的函数用于对子视图进行measure()操作。

• measureChildren():如其名称所示, children是 child 复数,该 函 数 内 部 使 用 for()环调用measureChild()对每一个子视图进行measure操作。
• measureChild()为指定的子视图进行measure操作。
• measureChildWithMarginsO该函数与 measureChild 的唯一区别在于, measure 时考虑把 margin及 padding也作为子视图大小的一部分。

static int MeasureSpec.getMode(int measureSpec) : 根据提供的测量值(格式),提取模式(上述三个模式之一

static int MeasureSpec.getSize(int measureSpec) : 根据提供的测量值(格式),提取大小值(这个大小也就是我们通常所说的大小

static int makeMeasureSpec(int size,int mode) : 根据提供的大小值和模式,创建一个测量值(格式)

LinearLayout 中的 onMeasure过程举例

View 中 的 measure()函数是不能被重载的,以保证View系 统 中 measure()的基本流程。ViewGroup的实例一般需要重载0nMeasure()数,并在该函数中调用 ViewGroup的 measureChild()相关函数对每一个子视图进行measure操作 。 LinearLayout中 的 onMeasure()数内部,首先判断该LinearLayout是水平的还是垂直的,并分别调用 measureHorizontal()和 measureVertical().

先计算所有子视图的高度由于ListView本身的高度仅share剩余部分,所 以 TextView将有足够的空间显示,其 height为 60,所以将首先获得60 高度,此时父窗口 LinearLayout将 剩 余40,再把这40 均匀分配给TextView和 ListView,因为它们的weight值 都 为 2。最终的结果是TextView将获得80高度,而 ListView获 得 20 高度

接下来需要判断父视图中是否还有剩余空间,并将剩余空间均匀分配给weight>0的子视图们.

layout过程的设计思路

在 layout()数中,首先调用 setFrame()数给当前视图设置参数中指定的位置,然后回调onLayoutO函数。 ViewGroup类中重载了 onLayout()数,并且将其函数类型设置成了一个abstract类型,因此,所 有 的 ViewGroup实例必须实现onLayout()View统希望程序员能够在onLayout()数中对该视图所包含的子视图进行layout操作,当该视图是ViewGroup时,程序员一般会在onLayout()函数中调用子视图 child的 layoutO方法,从而完成对子视图的位置分配。当然,如果子视图也是一个ViewGroup实例,就又会调用相应的onLayout()数。

View类 中 layout()数的:

1. 调用setFrame(1,t,r,b)将位置参数保存起来,这些参数将保存到View内 部 变 量(mLeft、 mTopmRight、 mBottom)o保存完变量前,会先对比这些参数是否和原来的相同,如果相同,则什么都不做,如果不同才进行重新赋值,并且在赋值前,会 给 mPrivateFlags中添加DRAWN标识,同时调用invalidate^告 诉 View系统原来占用的位置需要重绘。注意是先调用 invalidateO,而后赋值的,因为需要重绘的是老的区域。

2. 回 调 onLayout(), View中定义的onLayout()数默认什么都不做,View统提供onLayout()函数的目的是为了使系统包含有子视图的父视图能够在onLayoutO函数对子视图进行位置分配,正因为如此 ,如果是父视图,就必须重载onLayout(),也正因为如此, ViewGroup类 才 把 onLayout()载修改为了一个 abstract 类型。

LinearLayout 中 onLayout内部过程

onLayout()函 数 中 首 先 根 据 mOrientation变 量 判 断 是 水 平 还 是 垂 直 ,如 果 是 垂 直 则调用layoutVertical()开始进行 layout 操作。

1.获得子视图可用的宽度。读者可能觉得奇怪,这里是进行垂直方向上的布局,为什么却要可用的宽度,而不是可用的髙度呢?因为,就算是垂直方向,子视图本身也货以水平居中,而要居中就得知道的宽度是多少,从而计算出子视图左边沿的位置

2.根据父视图中的gravity属性,决定子视图的起始位置。在默认情况下, gravity是 TOP,应用程序可以设置为BOTTOM或 者 CENTER_VERTICAL

3. 此时已经确定了子视图的垂直方向上的位置,于是就开始for()环为每一个子视图分配位置

TextView  gravity  layout 的关系

子视图中 gravity的值并不是android:gravity值,而 是 android:layout__gravity?

在 LayoutInflater类中有一个inflater(resId,root)函数,在大多数情况下,大家调用该函数时,第二个参数root都为空。那么这个参数的意义又是什么?为什么大多数情况下都要为空呢?

该段代码包含了一个LinearLayout,其中又包含两个子视图,分别是一个TextView及 一 个ListView。从 X M L 文件中构造对应的View树 是 在 Layoutlnflater中 的 inflate()数中完成的,如果该段代码是作为 setContentView()参数,则会被当作Activity的界面,那么 LinearLayout中定义的layout_width及 height都是有意义的。但如果这段代码仅仅是应用程序调用inflate()数创建一个View对象,那 么 layoutwidth及 height就没有任何意义。

inflateO函数会首先判断参数root是否为空,如果为空,就 以 X M L 文件中的根视图构造一个临时的 View对象,此 处 是 LinearLayout对象,此 时 该 LinearLayout中声明的layout_width及 height都没有任何用处。接下来继续读取X M L 文 件 中 TextView读 取 TextView后,就 需 要 把 该 TextView添加到LinearLayout中。因为要添加,所以必须有一个LayoutParams对象,于是, inflate()数中就以TextView中定义的layout_width及 height为参数,回 调 TextView父视 图 的 generateLayoutParams()

<TextView>标签中包含的android:layoutwidth、 height并没有定义 LayoutParams对象,它只是提供了构造LayoutParams参数所需要的值,而真正构造LayoutParams对象的 是它的父视图,这 些 参 数 最 终 会 产 生 什 么 样 的LayoutParams取 决 于 该 TextView的父视图中generateLayoutParams(attrs)的具体实现。

这也就是上一节中所说的为什么要使用layout_gravity而 不 是 gravity的原因,因为在LinearLayout的 generateLayoutParams()数中,是把 android:layout_gravity 属性值赋值给了 LayoutParams.gravity,而不 是 android:gravity。事实上, android:gravity属 性 将 作 为 TextView内部的参数, TextView内部的onDraw()数中将根据android:gravity值决定把文字显示在什么地方

root参数的意义在于它将提供 generateLayoutParams()数,该函数产生的LayoutParams对象将作为 X M L 文件中包含的视图被添加 到 root时的布局属性。

如果你在一个XM L 文件中仅仅定义了一个TextView标签,然后调用 inflate(xml, null)从这个XML文 件 中 infalte出一个TextView对象,那 么 该 TextView标签中所包含的android:layout__width、 height将没有任何意义。当 你 想 把 infalte 出 来 的 TextView对 象 添 加 到 某 个ViewGroup时,还必须构造一个LayoutParams 对象

绘制draw )过程

如果该View是 一 个ViewGroup,则需要递归绘制 该 ViewGroup中所包含的所有子视图。

 

mView对象就是窗口的根视图,对 于 Activity而言,就是 PhoneWindow.DecorView 对象。

在一般情况下,View对象不应该重载dmw()数,因此,mView.draw()调用到了 View类 的 draw()函数。该函数的内部过程也就是View系统绘制过程的核心过程,该函数中会依次绘制上一节所讲的四种绘制兀素,其中绘制视图本身的具体实现就是回调onDraw()数,应用程序一^般 会 重 载 onDraw()函数以绘制所设计的View的真正界面内容。绘制完界面内容后,如果该视图内部还包含子视图,贝U调 用 dispatchDraw()数, ViewGroup载了该函数。因此,实际调用的是ViewGroup中 的 dispatchDraw()数,应用程序不应该再重载ViewGroup类 中 的 dispatchDmwO函数,因为该函数内部已经有了默认的实现,并且该实现代表了 View 系统的内.

View类中draw()函数内部流程

1. 绘制背景.首先根据滚动值对canvas的坐标进行调整,然后再恢复坐标.为什么需要先调用tmnslateO平 移 Canvas的坐标呢?因为对每一个视图而言, Canvas的 坐 标 原 点 (0 , 0 ) 对应的都是该视图的内部区域,如 图 13-35所示。

2. 显示视图的渐变框,daoying, setVerticalFadingEdgeO, setHorizontalFadingEdgeO

3. 绘制视图本身实 际 上 回 调 onDmw()数即可, View 设计者可以在onDraw()数中调用canvas的各种绘制函数进行绘制。

4. 调 用 dispatchDmwO绘制子视图。如果该视图内部不包含子视图,则不需要重载该函数,而对所有 的 ViewGroup实例而言,都必须重载该函数,否则它也就不是vfewGroup 了。

5. 回 调 onDrawScrollbars()绘制滚动条

动画的绘制

根据动画的参数对当前View做一定的图形变换,比如缩放、平移等,然后将变换后的图像绘制到屏幕上。绘制完后,再发起一个重绘的消息,就这样连续绘制,直到动画参数指示动画结束。

从效果的角度看, View系统中包含的动画可以分为三类,分别是窗口动画、视图动画、布局动画.

对 于 View系统而言,所能提供的动画更是有限, Android中 View系统仅支持基本的五种动画,分别为平移、缩放、旋转、扭曲及颜色阿尔法通道变化,这 称 之 为 “ 动画参数” 或 “ 动画类型”动画使用一个Animation类来表示,当 View要开始动画时,从 Animation类中获取动画的参数,并根据这些参数对View进行图形变换, 然后将变换后的图形绘制到屏幕上。

视图动画是在ViewGroup类 中 drawChild()数中完成的。 drawChild()数首先会判断该视图是否包 含 Animation对象,如果包含,则 利用 Animation中的动画参数对该视图进行变换,然后把变换后的图像绘制到canvas中。应用程序可以使用 res/anim/xxx.xml文件描述一个动画,对于视图动画而言,一般使用如下的xml标签描述一个动画。

<Set><scale/><alpha/>

<l a y o u t A n i m a t i o n>

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 一岁半宝宝光喝奶粉不吃饭怎么办 一岁半的宝宝光喝奶粉不吃饭怎么办 两岁半宝宝光喝奶粉不吃饭怎么办 宝宝9个月不好好吃饭奶粉怎么办 2岁半宝宝不愿意自己吃饭怎么办 一周岁多的宝宝不吃饭怎么办 一岁宝宝吃母乳不爱吃饭怎么办 三个月大的宝宝不肯喝奶粉怎么办 自己要上班婆婆带孩子看不惯怎么办 让农村婆婆来给我带孩子怎么办 吃的不好胃难受怎么办吃什么药 特别讨厌婆婆还需要她带小孩怎么办 一岁八个月宝宝不喜欢穿袜子怎么办 未来婆婆给我买衣服我不喜欢怎么办 孩子调皮被同学排斥不想上学怎么办 宝宝出生没人带怎么办自己要上班 孩子上幼儿园一年了还哭怎么办 6年级孩子会认字不会写怎么办 小孩写字没兴趣爱玩玩具怎么办 小学二年级语文记不住生字怎么办 2周半宝宝不肯马桶拉臭臭怎么办 娃晚上不肯睡早上不肯起怎么办 小学一年纪学生做作业粗心怎么办 考完试的题本偷撕了一页怎么办 犯错把父母惹的很生气该怎么办 一年级孩子做题粗心不认真怎么办 作弊被老师发现怎么办抄错了卷子 孩子最近老是被老师罚抄课文怎么办 孩子总是撒谎说作业作完了怎么办 孩子严重挑食怎么办一点菜也不吃 孩子在家很听话在学校很调皮怎么办 没通过交警车主全责不垫付怎么办 开车撞伤人交警说车主全责怎么办 面对喜欢上网又判逆的儿子怎么办 五年级了计算题老是出错怎么办 三年级的孩子老是计算题出错怎么办 写在表上的字写错了怎么办不能涂改 我的孩子做作业很马虎怎么办 幼儿园老师把学生名字写错怎么办 孩子的手写字磨了疙瘩怎么办 我家孩子上一年级算题特慢怎么办