View的工作原理(—)View相关基本概念

View可以说是Android开发中,我们接触的最多的东西了。但是View内部的工作原理,我还并不是很懂。所以在此作一个总结归纳。

我们先来了解一下View相关的一些基本概念:

1.ViewRootImpl

ViewRootImpl是GUI管理系统(WindowManager)和GUI呈现系统(DecorView)之间的桥梁,它既不是View的子类,也不是View的父类。我们理解ViewRootImpl时,可以将它理解为“View树的管理者”--它有一个mView的成员变量,指向的是它所管理的View树的根。其主要完成两件事情:

(1)向DecorView分发收到的用户发起的event事件,入按键,触屏,滑动等事件。

(2)与WindowManagerService进行交互,完成Activity的GUI绘制过程。

(3)完成View的三大流程:measure、layout、draw。

在ActivityThread中,当Activity对象被创建完毕之后,会将DecorView添加到Window中,同时也会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView对象简历关联。(在之前的Activity的启动流程源码解析中我们介绍到~Activity的启动过程最终是ApplicationThread类中的scheduleLaunchActivity()发消息给H的handle来创建Activityd的,而ApplicationThread有时在ActivityThread中创建的。)

核心代码如下:WindowManagerGlobal类中的261~272行

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        // do this last because it fires off messages to start doing things
        try {
            root.setView(view, wparams, panelParentView);

这里可以看出,ViewRootImpl类通过setView方法来来关联。


View的绘制流程是从ViewRootImpl的performTraversals()方法开始的,经过measure、layout、draw三个过程最终将一个View绘制出来。

measure:测量View的宽和高

layout:用来确定View相对于父容器中的位置

draw:负责将View绘制到屏幕上

下面展示下流程图(图是网上找的Android开发艺术中的图。。。自己懒得画)



如图所示,performTraversals()会依次调用performMeasure、performLayout和performDraw三个方法,而这三个方法则分别完成顶级View(DecorView)的measure、layout、deaw这三大流程。

首先在performMeasur中会调用View中的measure方法,在measure方法中又会调用onMeasure方法,而在onMeasure中则会对所有的子元素进行measure过程

,这时measure流程就从父容器传递到子元素中,这样就完成了一次Measure过程。接着子元素会重复父容器的measure过程,如此反复完成整个View树的遍历。

接下来的layout和draw的流程,也和measure同理。

2.DecorView

DecorView是应用窗口最顶层的View,DecorView本身其实是一个FrameLayout,其内部一般情况下会包含一个竖直方向的LinearLayout,而在这个LinearLayout中有上下两个部分:标题栏(上)和内容栏(下)。而在Activity中,我们是通过serContentView设置的布局文件,骑士就是加到内容栏中,而在源码中我们可以看到内容栏的id是content,这就是我们指定布局时为什么叫setContentView的原因。View层的事件必须都先经过DecorView,然后再传递给我们内容区的View。

DecorView的结构如图:


3.MeasureSpec

MeasureSpec参与了View的measure过程,他代表一个32位的int值,高两位代表SpecMode,低30位代表SpecSize。其中SpecMode是指测量模式,而SpecSize是指在某种测量模式下的规格大小。

代码如下:

    public static class 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);
        }

        static int adjust(int measureSpec, int delta) {
            final int mode = getMode(measureSpec);
            if (mode == UNSPECIFIED) {
                // No need to adjust size for UNSPECIFIED mode.
                return makeMeasureSpec(0, UNSPECIFIED);
            }
            int size = getSize(measureSpec) + delta;
            if (size < 0) {
                Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size +
                        ") spec: " + toString(measureSpec) + " delta: " + delta);
                size = 0;
            }
            return makeMeasureSpec(size, mode);
        }
从代码中我们可以看到,MeasureSpec是通过makeMeasureSpec(int size,int mode)方法将SpecSize和SpecMode打包成一个int值来避免过多的对象内存分配。

其中的SpecMode有三类:

UNSPECIFIED

父容器不对View有任何限制,这种情况一般用于系统内部,表示一种测量状态。

EXACTLY

父容器已经测量出View所需要的精确大小,这时,View的最终大小就是SpecSize所指定的值,即它对应的是LayoutParams中的Match_parent和设置的具体的数值这两种模式。

AT_MOOST

父容器指定了一个可用大小,而View的大小不能大于这个值,但是距离数值要看View的具体实现,即对应LayoutParams中的wrap_content。


当我们给View设置了LayoutParams,在View测量的时候,系统会将LayoutParams在父容器的约束下转换成对应的MeasureSpec,然后再根据这个MeasureSpec来确定View测量后的宽高。

只要提供父容器的MeasureSpec和子元素的LayoutPrarms,就可以快速地确定出子元素的MeasureSpec了,而有了这个MeasureSpec之后,就可以确定出子元素测量后的大小了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值