Android View深入浅出

自己最近搞自定义View,觉得对于View这块学习的不够深入。遂打算跟着源码注释1深入了解下View。

View 的知识点主要集中于 View的绘制(mearsure、layout、draw) 以及 事件的点击 上。

一.setContentView流程

很多提及Activity的onCreate方法的时候都会说OnCreate方法是Activity创建的过程。如果不调用setConentView的话,Activity界面是没有显示的。那具体setContentView的实现是什么样子的呢?

1.我们跟踪源码看下:
A. Activity#setContentView`

    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

接着看getWindow()返回的是mWindow,这是一个Window类型的;

B.Window#setContentView

public abstract void setContentView(@LayoutRes int layoutResID);

一个抽象方法,需要看下mWindow的赋值是在哪里?

    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window);
        ......

OK!找到实现类是PhoneWindow类;

C. PhoneWindow#setContentView

    @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.
------------------------------A--------------------------------------------------
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
------------------------------B--------------------------------------------------
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
------------------------------C--------------------------------------------------
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

2.PhoneWindow类中setContentView到底做了些什么呢?来一一解读。

在上面的代码中我分割了三部分来看

A.mContentParent对象为空,则会调用installDecor()方法;其他情况则会清除所有子View。mContentParent是ViewGroup类型的。

我们看下installDecor()做了什么,上代码段:

    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
   ......

mDecor对象为空则调用generateDecor()方法。此方法是为了实例化一个DecorView。注释2

mContentParent为空则调用generateLayout()方法。这个方法中首先会加载设置Window中的样式,比如说:窗口是否为NO_TITLE/ACTION_BAR/SWIPE_TO_DISMISS等、是否需要menu键、设置statusBar/NavigationBar的颜色等;然后会去加载窗口。

B.判断窗口是否含有FEATURE_CONTENT_TRANSITIONS 的flag标记,若有则动画显示调用transitionTo方法;没有的话,则调用inflate加载布局resouceId;

C.回调实现监听view内容的变化;

注释1:这里的源代码都是基于Nougat - 7.1.1_r6的源码。
注释2:DecorView,继承FrameLayout。其实是对FrameLayout进行功能的装饰拓展。是整个View树的最顶层View。

补充:
Window.java // 抽象类,提供了绘制窗口的一组通用API;

PhoneWindow.java //Window类的具体实现,是把一个DecorView对象进行一定封装,将其作为窗口的根View,并且提供一组通用的窗口操作借口;

猜你喜欢

转载自blog.csdn.net/zplxl99/article/details/73012248