设为首页 加入收藏

TOP

从Android源码分析View绘制(一)
2015-08-31 21:23:15 来源: 作者: 【 】 浏览:52
Tags:Android 源码 分析 View 绘制

在开发过程中,我们常常会来自定义View。它是用户交互组件的基本组成部分,负责展示图像和处理事件,通常被当做自定义组件的基类继承。那么今天就通过源码来仔细分析一下View是如何被创建以及在绘制过程中发生了什么。


创建


首先,View公有的构造函数的重载形式就有四种:


View(Context context)? ? 通过代码创建view时使用此构造函数,通过context参数,可以获取到需要的主题,资源等等。


View(Context context, AttributeSet attrs)? ? 当通过xml布局文件创建view时会使用此构造函数,调用了3个参数的构造方法。


View(Context context, AttributeSet attrs, int defStyleAttr)? ? 通过xml布局文件创建view,并采用在属性中指定的style。这个view的构造函数允许其子类在创建时使用自己的style。调用了下面四参的构造方法。


View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)? ? 该构造函数可以通过xml布局文件创建view,可以采用theme属性或者style资源文件指定的style。


参数:


Context : view运行的上下文信息,从中可以获取到当前theme,资源文件等信息。


AttributeSet: xml布局文件中view标签下指定的属性集合。


defStyleAttr: 当前theme中的一条属性,它包含一条指向theme资源文件中style的引用。默认值为0。


defStyleRes: 一个style资源文件的标示,表示style的ID,当值为0或者找不到对应的theme资源时候采用默认值。


综上所述,单参的构造函数从代码创建view,其余都调用四参的构造函数根据xml布局文件创建view。我们可以在不同的地方指定属性值,例如:


直接在xml标签中中指定的attrs值,可以从AttributeSet中获取。


通过在标签属性“style”中指定的资源文件。


默认的defStyleAttr。


默认的defStyleRes。


当前theme中的默认值。


构造函数的代码过长,就不在这里贴了,主要进行的工作是:获取各项系统定义的属性,然后根据属性值初始化view的各项成员变量和事件。


一般情况下,我们自定义view的时候,根据实际情况重写构造函数时,如果只从code创建,则只用实现单参数的即可。如果需要从xml布局文件中创建,则需要实现单参数和一个多参数的就好了,因为多参数的默认调用了四参数的构造函数;然后再获取到自定义的属性进行处理就OK了。


绘制


在activity获取到焦点后,会请求Android Framework根据它的布局文件进行绘制,activity需要提供所绘布局文件的根节点,然后对布局的树结构一边遍历一边进行绘制。我们都知道,ViewGroup是View的子类,它可以拥有若干子view,它的很多操作和view相同,不同的是ViewGroup负责绘制其子节点,而view则负责绘制其自身。整个遍历过程从上到下,在整个过程中,需要进行大小测量(measure函数)和定位(layout函数),然后再进行绘制。下面我们来看这些工作是如何进行的:


测定尺寸


在Android中,所有view被组织成树状结构,最顶层measure的主要工作就是负责递归测量出整个view树结构的尺寸大小,每个View的控件的实际宽高都是由父视图和本身视图决定的。


在研究源码之前,我先从整体上概况一下整个递归调用过程。从根view开始,使用measure方法中计算整个view树的大小,在该方法中调用子view的onMeasure方法。在onMeasure中主要进行两个工作:


调用setMeasuredDimension设置view自身的尺寸(mMeasureWidth和mMeasuredHeight),具体会在下面看到。


如果该view是ViewGroup,则需要继续递归调用其onMeasure方法来计算ViewGroup的子view大小。


根view通常就是一个ViewGroup,需要计算子view尺寸。首先获取到所有子view,然后调用measureChildWithMargins方法来计算子view的尺寸。在这个方法中调用了子view的measure方法。下面我们来看具体源码。


首先在measure方法中确定view的大小。这个方法被定义为final类型,不可被子类重写。在View中有一个静态内部类MeasureSpec封装了父view要传递给子View的布局参数,由size 和 mode共同组成。size即是大小,mode表示模式。(其实就是一个int值高2位表示mode,低30位表示size). mode总共有三种模式:


UNSPECIFIED:父view并未指定子view的大小,可随意根据开发人员需求指定view大小。


EXACTLY: 父view严格指定了子view的大小


AT_MOST: 子view的大小不超过该值


public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
? ? ? ? boolean optical = isLayoutModeOptical(this);//是否使用视觉边界布局
? ? ? ? if (optical != isLayoutModeOptical(mParent)) {// 当view和它的父viewGroup就是否采用视觉边界布局不一致时
? ? ? ? ? ? Insets insets = getOpticalInsets();
? ? ? ? ? ? int oWidth? = insets.left + insets.right;
? ? ? ? ? ? int oHeight = insets.top? + insets.bottom;
? ? ? ? ? ? widthMeasureSpec? = MeasureSpec.adjust(widthMeasureSpec,? optical ? -oWidth? : oWidth);
? ? ? ? ? ? heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
? ? ? ? }


? ? ? ?
? ? ? ? long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
? ? ? ? if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);


? ? ? ? if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
? ? ? ? ? ? ? ? widthMeasureSpec != mOldWidthMeasureSpec ||
? ? ? ? ? ? ? ? heightMeasureSpec != mOldHeightMeasureSpec) {


? ? ? ? ? ? // first clears the measured dimension flag
? ? ? ? ? ? mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;


? ? ? ? ? ? resolveRtlPropertiesIfNeeded()

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇Android LayoutInflater源码解析 下一篇Android View事件传递

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: