Android View系统解析(下)

转载请注明出处:(来自singwhatiwanna的csdn博客)

Android View系统解析系列:

Android View系统解析(上)

介绍View的基础知识、View的滑动、弹性滑动、滑动冲突解决方式、事件分发等

Android View系统解析(下)

介绍View的Framework层原理、View的measure / layout / draw三大流程和一些高级技巧

本次主要介绍下半部分,提纲如下

View的绘制过程

measure/layout/draw 工作流程

识别 MeasureSpec 并能够 make 合适的 MeasureSpec

在渲染前获取 View 的宽高

构造特殊的 View

自定义View

自定义View分类

自定义 View 须知

一 View的绘制过程初识 ViewRoot

ViewRoot对应于 ViewRootImpl 类,是连接 WindowManager 和 DecorView 的纽带。ActivityThread 中当 activity 对象被创建好后,会将 DecorView 加入到 Window中同时完成 ViewRootImpl 的创建并建立和 DecorView 的联系。root = new ViewRootImpl(view.getContext(), display);root.setView(view, wparams, panelParentView);view 的绘制流程从 ViewRoot 的 performTraversals 开始,代码流程是这样的:performMeasure -> measure -> onMeasureperformLayout -> layout -> onLayoutperformDraw -> draw -> onDraw

activity 界面的组成

由下图可知,DecorView作为顶级View,一般情况下它有上下两部分组成(具体情况会和api版本以及Theme有关),上面是title,下面是content,在activity中我们调用setContentView所设置的view其实就是被加到content中,而如何得到content呢,可以这样:ViewGroup group = findViewById(R.android.id.content),如何得到我们所设置的view呢,可以这样:group.getChildAt(0)。同时,通过源码我们可以知道,DecorView其实是一个FrameLayout。这里要说明的一点是View层的大部分事件都是从DecorView传递到我们的view中的。

MeasureSpec

MeasureSpec封装了父容器对 view 的布局上的限制,内部提供了宽高的信息( SpecMode 、 SpecSize ),SpecSize是指在某种SpecMode下的参考尺寸,其中SpecMode 有如下三种:UNSPECIFIED父容器不对 view 有任何限制,要多大给多大EXACTLY父容器已经检测出 view 所需要的大小AT_MOST父容器指定了一个大小, view 的大小不能大于这个值MeasureSpecs 的意义通过将 SpecMode 和 SpecSize 打包成一个 int 值可以避免过多的对象内存分配,为了方便操作,其提供了打包 / 解包方法

MeasureSpec 的实现

MeasureSpec

代表一个 32 位 int 值高 2 位代表 SpecMode ,低 30 位代表 SpecSize

下面先看一下MeasureSpec 内部的一些常量的定义,通过下面的代码,应该不难理解MeasureSpec的工作原理

private static final int MODE_SHIFT = 30;private static final int MODE_MASK = 0x3 << MODE_SHIFT;public static final int UNSPECIFIED = 0 << MODE_SHIFT;public static final int EXACTLY = 1 << MODE_SHIFT;public static final int AT_MOST = 2 << MODE_SHIFT;public static int makeMeasureSpec(int size, int mode) {if (sUseBrokenMakeMeasureSpec) {return size + mode;} else {return (size & ~MODE_MASK) | (mode & MODE_MASK);}}public static int getMode(int measureSpec) {return (measureSpec & MODE_MASK);}public static int getSize(int measureSpec) {return (measureSpec & ~MODE_MASK);}MeasureSpec 与 LayoutParams对于 DecorView ,其 MeasureSpec 由窗口的尺寸和其自身的LayoutParams 来共同确定对于应用层 View ,其 MeasureSpec 由父容器的 MeasureSpec 和自身的 LayoutParams 来共同决定MeasureSpec 一旦确定后, onMeasure 中就可以确定自身的宽高MeasureSpec-DecorView

这里分析下顶级容器DecorView的MeasureSpec的产生过程

childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight,lp.height);performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

上述代码描述了DecorView的MeasureSpec的产生过程,为了更清晰地了解,我们继续看下去

private static int getRootMeasureSpec(int windowSize, int rootDimension) {int measureSpec;switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: // Window can’t resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break;case ViewGroup.LayoutParams.WRAP_CONTENT: // Window can resize. Set max size for root view. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break;default: // Window wants to be an exact size. Force root view to be that size. measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break;}return measureSpec;}通过上述代码,顶级容器DecorView的MeasureSpec的产生过程就很明确了,具体来说其遵守如下规则:

根据它的LayoutParams中的宽高的参数来分,

LayoutParams.MATCH_PARENT:其模式为精确模式,大小就是窗口的大小

LayoutParams.WRAP_CONTENT:其模式为最大模式,,大小不定,但是不能超过窗口的大小

固定大小(比如100dp):其模式为精确模式,大小为LayoutParams中指定的大小

MeasureSpec- 应用层 View关于应用层View,这里是指我们布局中的view,其MeasureSpec的创建遵循下表中的规则好好扮演自己的角色,做自己该做的事

Android View系统解析(下)

相关文章:

你感兴趣的文章:

标签云: