Android源码解析UI绘制流程

2018-02-11  本文已影响0人  滗仙

说到android的UI绘制流程,咱们首先介绍一下setContentView这个大家经常用到的方法。当创建一个Activity时,实现了onCreate方法,然后把创建好的布局Layout或者View通过调用setContentView(xxx)就显示到窗口中了。那么接下来就带着下面的疑问,了解View的加载过程。

为什么调用了setContentView后就可以显示出我们的局部页面?

看看源码中setContentView究竟做了什么

    private Window mWindow;
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
    public Window getWindow() {
        return mWindow;
    }

从源码中可以看到setContentView中调用了getWindow方法获取了Window对象,看看源码中对Window对象的定义。

/**
 * Abstract base class for a top-level window look and behavior policy.  An
 * instance of this class should be used as the top-level view added to the
 * window manager. It provides standard UI policies such as a background, title
 * area, default key processing, etc.
 *
 * <p>The only existing implementation of this abstract class is
 * android.view.PhoneWindow, which you should instantiate when needing a
 * Window.
 */
public abstract class Window {
        public View findViewById(@IdRes int id) {}
        public void setBackgroundDrawableResource(@DrawableRes int resId) {}
        ...

}

从注释中我们看到Window对象是一个顶层窗口的抽象类,里面封装了一些跟窗口UI相关的方法。既然Window是一个抽象类,那么我们就去看一下它的唯一实现类PhoneWindow。

public class PhoneWindow extends Window{
    // This is the view in which the window contents are placed. It is either
    // mDecor itself, or a child of mDecor where the contents go.
    private ViewGroup mContentParent;
    // This is the top-level view of the window, containing the window decor.
    private DecorView mDecor;
    @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        ...
    }
}

PhoneWindow类中的setContentView方法里面用到了mContentParent,它的类型是ViewGroup,从注释中看到我们的Activity中的View就会放到这个ViewGroup中,换而言之就是Window内容要摆放的一个地方,要么是mDecor自己要么就是mDecor的子类。而这个DecorView就是Window最顶层的View。从代码中看到当mContentParent为空时,调用了installDecor方法,进去看一下。

private void installDecor() {
        if (mDecor == null) {
            //generateDecor方法中new了一个DecorView
            mDecor = generateDecor();
            ...
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
             ...
           }
    }

protected ViewGroup generateLayout(DecorView decor) {
        ...
        // Inflate the window decor.
        int layoutResource;
        //根据不同的features加载不同的layout布局文件
        int features = getLocalFeatures();
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            ...
        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            ...
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            ...
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            ...
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            ...
        } else {
            layoutResource = R.layout.screen_simple;
        }

        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        mContentRoot = (ViewGroup) in;
        //代码看到这就可以看出上文提到的mContentParent就是布局
        //layoutResource中id为content的FrameLayout,下文中会接着提到。
        //public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
       
        return contentParent;
    }

DecorView的结构拿最简单的R.layout.screen_simple为例,如下图:

image.png

本文原创,转载请注明出处。

上一篇下一篇

猜你喜欢

热点阅读