一览setContentView

作者: 慕涵盛华 | 来源:发表于2019-01-17 15:30 被阅读12次

    Activity -- setContentView

    public void setContentView(View view) {
        getWindow().setContentView(view);
        initWindowDecorActionBar();
    }
    
    public Window getWindow() {
        return mWindow;
    }
    
    final void attach(。。。) {
        ...
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        ...
    }
    

    ActivitysetContentView方法中调用了Window对应的方法,Window本身是一个抽象类,这里它的实现类是PhoneWindow,实际上调用的是PhoneWindowsetContentView方法。

    PhoneWindow -- setContentView

    ViewGroup mContentParent;
    
    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();   //注释1
        } 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);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
    

    第一次调用的时候mContentParent为null,会执行installDecor()方法,在该方法中会创建mContentParent;继续执行下面会把传入的布局添加到mContentParent中:mLayoutInflater.inflate(layoutResID, mContentParent);

    //注释1
    private void installDecor() {
        ...
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            ...
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            ...
        }
    }
    

    installDecor方法中,会创建mDecor(DecorView)和mContentParent

    protected DecorView generateDecor(int featureId) {
        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());
    }
    
    public class DecorView extends FrameLayout
    

    直接new DecorViewDecorView本质是一个FrameLayout

    protected ViewGroup generateLayout(DecorView decor) {
        ......
       //根据设置的feature来选择Activity的根部局
        int layoutResource;
        int features = getLocalFeatures();    
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
            setCloseOnSwipeEnabled(true);
        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_title_icons;
            }
            removeFeature(FEATURE_ACTION_BAR); 
        } 
        ......
        else {
            layoutResource = R.layout.screen_simple;   
        }
    
        mDecor.startChanging();
        //往DecorView加载根部局
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
        //找到内容布局:不管选择哪个根部局,内容布局id都设置为ID_ANDROID_CONTENT这个参数值
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);  
        ...
        return contentParent;
    }
    
    public abstract class Window{
        public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
    }
    

    根据feature来选择Activity不同的布局,然后将布局添加到mDecor中,不管是哪种布局,内容的布局id都是ID_ANDROID_CONTENT,然后获取内容布局赋值给contentParent。

    R.layout.screen_simple

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        android:orientation="vertical">
        <ViewStub android:id="@+id/action_mode_bar_stub"
                  android:inflatedId="@+id/action_mode_bar"
                  android:layout="@layout/action_mode_bar"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:theme="?attr/actionBarTheme" />
        <FrameLayout
             android:id="@android:id/content"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:foregroundInsidePadding="false"
             android:foregroundGravity="fill_horizontal|top"
             android:foreground="?android:attr/windowContentOverlay" />
    </LinearLayout>
    

    R.layout.screen_simple添加到DecorView中,DecorViewPhoneWindow中,所以通过findViewById(ID_ANDROID_CON)获取到的是FrameLayout返回给mContentParent 。通过setContentView设置布局就是往mContentParent里面添加内容。它本质是一个FrameLayout

    布局层级图

    我们打开一个应用,最外层是一个PhoneWindow,它里层是一个DecorView,本质就是一个FrameLayout,然后它里层是一个LinearLayout,是一个垂直方向的,上面是延时加载的ActionBar,因为Activity可能没有actionBar,所以用ViewStub布局;下面是一个FrameLayout,我们通过setContentView设置的布局,就是往该FrameLayout中添加内容。

    Kotlin项目实战

    相关文章

      网友评论

        本文标题:一览setContentView

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