Activity布局加载流程

一、布局加载流程

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //分析->布局文件的加载流程,最终调用的是 PhoneWindow.setContentView(int layoutResID)
        setContentView(R.layout.activity_main);
    }
}

二、分析步骤

1.PhoneWindow.setContentView(int layoutResID)

@Override
public void setContentView(int layoutResID) {
    if (mContentParent == null) {
        //步骤2
        installDecor();
    }

    //通过LayoutInflater 把 Activity 的布局文件中的内容添加到系统布局中一个 id 为 content 的 FrameLayout 中
    mLayoutInflater.inflate(layoutResID, mContentParent);        
}

2.PhoneWindow.installDecor()

private void installDecor() {
    if (mDecor == null) {
        //步骤3->创建 DecorView
        mDecor = generateDecor(-1);
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        //步骤4
        mContentParent = generateLayout(mDecor);
    }
}

3.PhoneWindow.generateDecor(int featureId)

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());
}

4.PhoneWindow.generateLayout(DecorView decor)

protected ViewGroup generateLayout(DecorView decor) {
    // Apply data from current theme.

    TypedArray a = getWindowStyle();

    if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
        requestFeature(FEATURE_NO_TITLE);
    } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
        // Don't allow an action bar if there is no title.
        requestFeature(FEATURE_ACTION_BAR);
    }

    // Inflate the window decor.
    //做一系列的判断,去加载系统的 layout 资源文件
    int layoutResource;
    int features = getLocalFeatures();

    //各种判断加载系统布局
    if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
        // If no other features and not embedded, only need a title.
        // If the window is floating, we need a dialog layout
        if (mIsFloating) {
            TypedValue res = new TypedValue();
            getContext().getTheme().resolveAttribute(
                    R.attr.dialogTitleDecorLayout, res, true);
            layoutResource = res.resourceId;
        } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
            layoutResource = a.getResourceId(
                    R.styleable.Window_windowActionBarFullscreenDecorLayout,
                    R.layout.screen_action_bar);
        } else {
            layoutResource = R.layout.screen_title;//DecroView 通常使用的是这个布局文件
        }
        // System.out.println("Title!");
    } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
        layoutResource = R.layout.screen_simple_overlay_action_mode;
    } else {
        // Embedded, so no decoration is needed.
        layoutResource = R.layout.screen_simple;
        // System.out.println("Simple!");
    }

    mDecor.startChanging();
    //步骤5->把系统布局加载到 DecorView 中
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

    /**
          * The ID that the main layout in the XML layout file should have.
          * public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
          *
          * 找一个 id 为 content 的 FrameLayout,Activity 对应的布局内容就是添加在这个 FrameLayout 中
          */
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    if (contentParent == null) {
        throw new RuntimeException("Window couldn't find content container view");
    }

    mDecor.finishChanging();

    //返回 contentParent
    return contentParent;
}

5.PhoneWindow.onResourcesLoaded(LayoutInflater inflater, int layoutResource)

//将得到的布局文件加载到 DecorView 中
    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {

        mDecorCaptionView = createDecorCaptionView(inflater);
        //解析、实例化系统的布局
        final View root = inflater.inflate(layoutResource, null);
        if (mDecorCaptionView != null) {
            if (mDecorCaptionView.getParent() == null) {
                addView(mDecorCaptionView,
                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            }
            //把系统的布局加入到 DecorView 中
            mDecorCaptionView.addView(root,
                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
        } else {

            // Put it below the color views.
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mContentRoot = (ViewGroup) root;
    }

三、总结

  1. Activity.setContentView() 实现了把布局文件中对应的内容加载到 DecroView 中
  2. contentParent 是一个 id 为 content 的 FrameLayout,Activity 对应的布局内容就是添加在这个 FrameLayout 中

四、注意

在 Activity 的 onCreate()、onResume() 中,不能直接获取 View 的宽高,因为 View 需要 onMeasure() 之后才能获取到真实宽高,onMeasure() 在 Activity 的 onResume() 之后执行的

猜你喜欢

转载自blog.csdn.net/qq_21586317/article/details/79736542