Android开发经验谈Android开发

一览setContentView

2019-01-17  本文已影响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项目实战

上一篇下一篇

猜你喜欢

热点阅读