美文网首页
setContentView源码分析

setContentView源码分析

作者: 码字农民工 | 来源:发表于2018-06-04 18:33 被阅读60次

setContentView源码分析

基于AppCompatDelegateImplV9

找到切入点

public class MainActivity extends AppCompatActivity {
    private GLPanorama mGLPanorama;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //分析这里的源码
        setContentView(R.layout.activity_main);
    }
}

点击setContentView来到AppCompatActivity类

@Override
    public void setContentView(@LayoutRes int layoutResID) {
         //获取代理实现类
        getDelegate().setContentView(layoutResID);
    }
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
        return create(activity, activity.getWindow(), callback);
    }

    public static AppCompatDelegate create(Dialog dialog, AppCompatCallback callback) {
        return create(dialog.getContext(), dialog.getWindow(), callback);
    }

    /*
     * 这里是重点,通过判断sdk版本来加载不同的实现类
     * 我们会点击每个实现类会发现它们最终都继承了AppCompatDelegateImplV9类
     */
    private static AppCompatDelegate create(Context context, Window window,
            AppCompatCallback callback) {
        if (Build.VERSION.SDK_INT >= 24) {
            return new AppCompatDelegateImplN(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 23) {
            return new AppCompatDelegateImplV23(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 14) {
            return new AppCompatDelegateImplV14(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 11) {
            return new AppCompatDelegateImplV11(context, window, callback);
        } else {
            return new AppCompatDelegateImplV9(context, window, callback);
        }
    }

我们直接来到AppCompatDelegateImplV9类

 @Override
    public void setContentView(View v) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        contentParent.addView(v);
        mOriginalWindowCallback.onContentChanged();
    }

    /*
     * 因为我们在外部传入的是id,所以会走到这个方法
     */
    @Override
    public void setContentView(int resId) {
         //这里是入口
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        //把我们的布局放到contentParent里面
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mOriginalWindowCallback.onContentChanged();
    }

    @Override
    public void setContentView(View v, ViewGroup.LayoutParams lp) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        contentParent.addView(v, lp);
        mOriginalWindowCallback.onContentChanged();
    }

private void ensureSubDecor() {
          //因为我们在onCreate中调用setContentView,所以这里的mSubDecorInstalled为false
        if (!mSubDecorInstalled) {
             //这里去进行创建我们的根布局
            mSubDecor = createSubDecor();
            CharSequence title = getTitle();
            if (!TextUtils.isEmpty(title)) {
                onTitleChanged(title);
            }

            applyFixedSizeWindow();

            onSubDecorInstalled(mSubDecor);

            mSubDecorInstalled = true;

            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
            if (!isDestroyed() && (st == null || st.menu == null)) {
                invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
            }
        }
    }
 private ViewGroup createSubDecor() {
        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);

        if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
            a.recycle();
            //如果不设置AppCompat主题,这里就会抛出异常
            throw new IllegalStateException(
                    "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
        }
        //初始化相关特征标志
        if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
              //一般我们的主题默认都是NoTitle
              //这里我们就会发现为什么我们设置activity全屏需要在setContentView之前
            requestWindowFeature(Window.FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
        }
        if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
            requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
        }
        if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
            requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
        }
        mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
        a.recycle();
        
        //通过PhoneWindow创建DecorView
        mWindow.getDecorView();

        final LayoutInflater inflater = LayoutInflater.from(mContext);
        ViewGroup subDecor = null;
        if (!mWindowNoTitle) {
            ...因为是NoTitle主题,所以不会进入这里
        } else {
            //这里去创建我们的subDecor布局
            if (mOverlayActionMode) {
                ///调用了requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY)会走进来
                subDecor = (ViewGroup) inflater.inflate(
                        R.layout.abc_screen_simple_overlay_action_mode, null);
            } else {
                subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
            }
                ...
        }

        if (subDecor == null) {
            throw new IllegalArgumentException(
                    "AppCompat does not support the current theme features: { "
                            + "windowActionBar: " + mHasActionBar
                            + ", windowActionBarOverlay: "+ mOverlayActionBar
                            + ", android:windowIsFloating: " + mIsFloating
                            + ", windowActionModeOverlay: " + mOverlayActionMode
                            + ", windowNoTitle: " + mWindowNoTitle
                            + " }");
        }

        if (mDecorContentParent == null) {
            mTitleView = (TextView) subDecor.findViewById(R.id.title);
        }

        ViewUtils.makeOptionalFitsSystemWindows(subDecor);
        
        //这里的contentView就是我们编写的布局的父布局,我们从下面的代码可以看出来。
        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);
        //这里的windowContentView是我们原来的布局的父容器
        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        if (windowContentView != null) {
            //在这里对原来的windowContentView里面的布局进行替换给contentView
            while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }
            //将原来windowContentView的id由原来的android.R.id.content设置为View.NO_ID
            windowContentView.setId(View.NO_ID);
            //这里将contentView的id设置为android.R.id.content
            contentView.setId(android.R.id.content);

            if (windowContentView instanceof FrameLayout) {
                ((FrameLayout) windowContentView).setForeground(null);
            }
        }
        //在这里就将我们的subDecor添加给了PhoneWindow
        mWindow.setContentView(subDecor);

        contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
            @Override
            public void onAttachedFromWindow() {}

            @Override
            public void onDetachedFromWindow() {
                dismissPopups();
            }
        });

        return subDecor;
    }

我们来到PhoneWindow这个类

    @Override
    public void setContentView(View view) {
        setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }

    /*
     * 我们主要看这个方法
     */
    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            //进行DecorView的初始化
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        //是否有transitions动画。没有,进入else
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
            //重要!!将这个subDecor添加到这个mContentParent里面了
            //mContentParent是FrameLayout,在之前设置的View.NO_ID
            mContentParent.addView(view, params);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
    private void installDecor() {
        if (mDecor == null) {
            //创建DecorView
            mDecor = generateDecor(-1);
            ...
        } 
        ...
        if (mContentParent == null) {
            //将DecorView传入进行进行mContentParent的初始化一系列操作
            mContentParent = generateLayout(mDecor);
            ....
        }
    }

generateDecor(-1)

    protected DecorView generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext().getResources());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }

generateLayout(mDecor)

    protected ViewGroup generateLayout(DecorView decor) {
        TypedArray a = getWindowStyle();
        //设置一堆标志位...
        ...
        mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
        
        if (!mForcedStatusBarColor) {
            //获取主题状态栏的颜色
            mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
        }
        if (!mForcedNavigationBarColor) {
            //获取底部NavigationBar颜色
            mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
        }
        //获取主题一些资源
       ...
        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            ...我们设置不同的主题以及样式,会采用不同的布局文件...
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!");
        }

        mDecor.startChanging();
        //把screen_simple放到了DecorView里面
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
        ...
        return contentParent;
    }

走到这里我们就知道了Actiity的setContentView的大部分的主要流程,创建DecorView,设置DecorView的ContentView,并将我们AppCompatDelegateImplV9中创建的subDecor里面的contentView的id替换成DecorView的ContentView的id。接下来就该将我们的布局添加至AppCompatDelegateImplV9中创建的subDecor的contentView中。

    @Override
    public void setContentView(int resId) {
        ensureSubDecor();
        //找到SubDecor中的contentView
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        //将我们的布局添加到contentParent中
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mOriginalWindowCallback.onContentChanged();
    }

从这里我们就会发现MainActivity.setContentView()->AppCompatDelegateImplV9.setContentView()->ensureSubDecor()->createSubDecor()->PhoneWindow.setContentView()->installDecor()->generateDecor()->generateLayout()->mContentParent.addView(view, params)->AppCompatDelegateImplV9.subDecor.contentView.addView()


相关文章

网友评论

      本文标题:setContentView源码分析

      本文链接:https://www.haomeiwen.com/subject/aviysftx.html