• 从setContentView()源码看起


    2.1 Activity::setContentView()
    public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
    }
    public Window getWindow() {
    return mWindow;
    }
    1
    2
    3
    4
    5
    6
    7
    2.2 mWindow的初始化是在Activity的attach方法中执行的。了解过Activity启动流程,我们知道:attach方法是在Activity启动流程中,ActivityThread::performLaunchActivity()方法中被调用的,是在Activity被创建后,完成初始化操作的。
    //android.app.Activity#attach()
    final void attach(Context context, ActivityThread aThread, ... Window window ...) {
    ...
    //这里进行mWindow的初始化,可以看到Activity中的Window实现类是PhoneWindow,
    //目前为止,PhoneWindow也是Window的唯一实现类
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    //这里是给Window设置了WindowManager,WindowManager是通过IPC获取的系统服务,
    //WindowManager只是一个接口类型,具体实现是WindowManagerImpl类,
    //当然WindowManagerImpl又将实际的逻辑实现交给了WindowManagerGlobal类
    mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
    mToken, mComponent.flattenToString(),
    (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    //Activity持有的WindowManager也是从Window中拿过来的
    mWindowManager = mWindow.getWindowManager();
    ....
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    2.3 继续看PhoneWindow的setContentView()实现代码
    @Override
    public void setContentView(int layoutResID) {
    //mContentParent其实就是android.R.id.content布局对应的实际展示的内容
    if (mContentParent == null) {
    //这个方法时构造一个顶层的DecorView对象,其实是直接通过new DecorView产生的实例对象,并赋值给mDecor变量
    installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    //第二次调用setContentView()方法时走这里,会先remove掉所有的子View再通过inflate进行加载布局
    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();
    }
    //这个变量,会在requestFeature()方法调用时判断时机是否正确,
    //如果实在setContentView之后调用的,会抛出"requestFeature() must be called before adding content"的异常
    mContentParentExplicitlySet = true;
    }
    //installDecorView():构造Activity视图框架的根视图,并通过LayoutInflater加载mContentParent,
    //我们一般操作的setContentView其实就是将布局展示到了mContentParent中
    private void installDecor() {
    ...
    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) {
    //DecorView已经构造好了,可以从DecorView中通过findViewById的方式实例化mContentParent对象了
    mContentParent = generateLayout(mDecor);
    }
    ...
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    2.4 generateLayout(mDecor)也是获取mContentParent对象的关键方法
    protected ViewGroup generateLayout(DecorView decor) {
    ...
    // Inflate the window decor.
    int layoutResource;
    int features = getLocalFeatures();
    // System.out.println("Features: 0x" + Integer.toHexString(features));
    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 if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
    && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
    layoutResource = R.layout.screen_progress;
    } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
    if (mIsFloating) {
    TypedValue res = new TypedValue();
    getContext().getTheme().resolveAttribute(
    R.attr.dialogCustomTitleDecorLayout, res, true);
    layoutResource = res.resourceId;
    } else {
    layoutResource = R.layout.screen_custom_title;
    }
    removeFeature(FEATURE_ACTION_BAR);
    } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
    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;
    }
    } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
    layoutResource = R.layout.screen_simple_overlay_action_mode;
    } else {
    layoutResource = R.layout.screen_simple;
    }
    ....
    //mDecorView虽然已经初始化了,但是他的布局还未加载,通过上面对features变量值的一堆if-else判断,
    //获取到对应的feature值的布局文件,再通过inflater对象加载布局
    mDecor.startChanging();
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    //这里获取的就是mContentParent对象,findViewById是View的方法,这里是间接调用了DecorView的findViewById方法,
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    ...
    return contentParent;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    2.5 查找ID_ANDROID_CONTENT变量,可以看到:其值是android.R.id.content
    /**
    * 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;
    1
    2
    3
    4
    2.6 另外: DecorView加载布局文件资源的方法是:onResourcesLoaded()
    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
    ...
    //这里inflate方法的root直接传null,的确这已经是跟布局了,肯定是没有父View可以传了
    final View root = inflater.inflate(layoutResource, null);
    ...
    }

  • 相关阅读:
    软件工程专业必须要会的算法
    软工实践寒假作业(1/2)
    我的思维导图
    个人简历
    当初希望自己是如何投入这个专业的学习的?曾经做过什么准备,或者立下过什么FLAG吗?
    当初对"软件工程"这个专业的期待和想象是什么?
    洛谷 题解 P1736 【创意吃鱼法】
    2018NOIP赛后总结+后阶段信奥学习个人规划
    洛谷 题解 P5015 【标题统计】 NOIP2018 普及组 T1
    NOIp考前注意事项
  • 原文地址:https://www.cnblogs.com/ly570/p/11460294.html
Copyright © 2020-2023  润新知