View载入具体解释
阅读原文时间:2023年07月12日阅读:1

文章一開始我要对前面一篇文章做点补充

相信大家都知道View有两个方法。

 public boolean post(Runnable action)
 public boolean postDelayed(Runnable action, long delayMillis) {

这两个方法也能够实现对View的包装(实现延时发送消息和更新UI)那么它和Handler有什么差别呢下面给出源代码。

/**
     * <p>Causes the Runnable to be added to the message queue, to be run
     * after the specified amount of time elapses.
     * The runnable will be run on the user interface thread.</p>
     *<BR>使Runnable被加入到消息队列。执行将执行在用户界面线程。@param行动的执行将被执行。

* @返回返回true假设执行成功放置到
     *消息队列。

执行失败将返回False。一般是由于
    * looper处理消息队列退出。
     * @param action The Runnable that will be executed.
     * @param delayMillis The delay (in milliseconds) until the Runnable
     *        will be executed.
     *
     * @return true if the Runnable was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the Runnable will be processed --
     *         if the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     *
     * @see #post
     * @see #removeCallbacks
     */
    public boolean postDelayed(Runnable action, long delayMillis) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.postDelayed(action, delayMillis);
        }
        // Assume that post will succeed later
        ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
        return true;
    }

/**
     * <p>Causes the Runnable to be added to the message queue, to be run
     * after the specified amount of time elapses.
     * The runnable will be run on the user interface thread.</p>
     *使Runnable被加入到消息队列,能够执行在指定时间的流逝之后
     * @param action The Runnable that will be executed.
     * @param delayMillis The delay (in milliseconds) until the Runnable
     *        will be executed.
     *
     * @return true if the Runnable was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the Runnable will be processed --
     *         if the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     *
     * @see #post
     * @see #removeCallbacks
     */
    public boolean postDelayed(Runnable action, long delayMillis) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.postDelayed(action, delayMillis);
        }
        // Assume that post will succeed later
        ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
        return true;
    }

关键的凝视我也进行的翻译。相信大家如今已经明确了。

在post(Runnable action)方法里。View获得当前线程(即UI线程)的Handler。然后将action对象post到Handler里。在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。在Handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法。而此时,已经路由到UI线程里,因此。我们能够毫无顾虑的来更新UI。

这样的情况下。由于不是在新的线程中使用,所以千万别做复杂的计算逻辑。更不能进行耗时操作了

那大家又会问什么是ViewRootImpl?这特么又是什么鬼,这里给大家解说一下。我们从最基础的讲起。

相信大家都知道我们通过调用setContentView(int layoutResID)把xml文件载入到当前Activity上的。

那我们就从setContentView讲起。

public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

setContentView方法实现非常easy,里面调用两个方法,我们这里分析第一个方法:

getWindow()得到一个Window对象 mWindow ,Window是一个抽象类,用来描写叙述Activity视图最顶端的窗体显示和行为操作。Window是一个抽象类。那么里面的setContentView(layoutResID)是一个抽象方法。并没有具体的实现。既然Window是一个抽象类,那么在Activity里面就有一个Window抽象类的实现。们查找代码发现 mWindow对象赋值方法例如以下。

mWindow = PolicyManager.makeNewWindow(this);

继续看看 PolicyManager类

public final class More PolicyManager {
30    private static final String POLICY_IMPL_CLASS_NAME =
31        "com.android.internal.policy.impl.Policy";
32
33    private static final IPolicy sPolicy;
34
35    static {
36        // Pull in the actual implementation of the policy at run-time
37        try {
38            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
39            sPolicy = (IPolicy)policyClass.newInstance();
40        } catch (ClassNotFoundException ex) {
41            throw new RuntimeException(
42                    POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
43        } catch (InstantiationException ex) {
44            throw new RuntimeException(
45                    POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
46        } catch (IllegalAccessException ex) {
47            throw new RuntimeException(
48                    POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
49        }
50    }
51
52    // Cannot instantiate this class
53    private More PolicyManager() {}
54
55    // The static methods to spawn new policy-specific objects
56    public static Window More makeNewWindow(Context context) {
57        return sPolicy.makeNewWindow(context);
58    }

通过分析我们发现sPolicy对象是有第 38。39行通过径”com.android.internal.policy.impl.Policy“生成的(通过反射拿到类的对象而且实例化)重点57,

public Window makeNewWindow(Context context) {
        return new PhoneWindow(context);
    }

由此可见,我们最终找到Activity类中的 mWindow对象的实现类了。就是PhoneWindow类。

可能又会有些童学问什么是反射呢?what Fuck,要是特么这样扯下去,预计三天三夜也扯不完,等这篇写完后,会专一分一篇来解说Java的反射机制。

到PhoneWindow类发现了setContentView方法的实现也在这个类里面了

@Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor();
        } 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);
        }
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

首先推断mContentParent是否为null,mContentParent是什么呢?接下来会分析,一開始条件成立,进入installDecor()方法

到installDecor()方法里,实现例如以下:

 private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);

            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            mDecor.makeOptionalFitsSystemWindows();

            final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                    R.id.decor_content_parent);

            if (decorContentParent != null) {
                mDecorContentParent = decorContentParent;
                mDecorContentParent.setWindowCallback(getCallback());
                if (mDecorContentParent.getTitle() == null) {
                    mDecorContentParent.setWindowTitle(mTitle);
                }

                final int localFeatures = getLocalFeatures();
                for (int i = 0; i < FEATURE_MAX; i++) {
                    if ((localFeatures & (1 << i)) != 0) {
                        mDecorContentParent.initFeature(i);
                    }
                }

                mDecorContentParent.setUiOptions(mUiOptions);

                if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||
                        (mIconRes != 0 && !mDecorContentParent.hasIcon())) {
                    mDecorContentParent.setIcon(mIconRes);
                } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&
                        mIconRes == 0 && !mDecorContentParent.hasIcon()) {
                    mDecorContentParent.setIcon(
                            getContext().getPackageManager().getDefaultActivityIcon());
                    mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
                }
                if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||
                        (mLogoRes != 0 && !mDecorContentParent.hasLogo())) {
                    mDecorContentParent.setLogo(mLogoRes);
                }

                // Invalidate if the panel menu hasn't been created before this.
                // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
                // being called in the middle of onCreate or similar.
                // A pending invalidation will typically be resolved before the posted message
                // would run normally in order to satisfy instance state restoration.
                PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
                if (!isDestroyed() && (st == null || st.menu == null)) {
                    invalidatePanelMenu(FEATURE_ACTION_BAR);
                }
            } else {
                mTitleView = (TextView)findViewById(R.id.title);
                if (mTitleView != null) {
                    mTitleView.setLayoutDirection(mDecor.getLayoutDirection());
                    if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                        View titleContainer = findViewById(
                                R.id.title_container);
                        if (titleContainer != null) {
                            titleContainer.setVisibility(View.GONE);
                        } else {
                            mTitleView.setVisibility(View.GONE);
                        }
                        if (mContentParent instanceof FrameLayout) {
                            ((FrameLayout)mContentParent).setForeground(null);
                        }
                    } else {
                        mTitleView.setText(mTitle);
                    }
                }
            }

            if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
                mDecor.setBackgroundFallback(mBackgroundFallbackResource);
            }

            // Only inflate or create a new TransitionManager if the caller hasn't
            // already set a custom one.
            if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
                if (mTransitionManager == null) {
                    final int transitionRes = getWindowStyle().getResourceId(
                            R.styleable.Window_windowContentTransitionManager,
                            0);
                    if (transitionRes != 0) {
                        final TransitionInflater inflater =        TransitionInflater.from(getContext());
                        mTransitionManager = inflater.inflateTransitionManager(transitionRes,
                                mContentParent);
                    } else {
                        mTransitionManager = new TransitionManager();
                    }
                }

                mEnterTransition = getTransition(mEnterTransition, null,
                        R.styleable.Window_windowEnterTransition);
                mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION,
                        R.styleable.Window_windowReturnTransition);
                mExitTransition = getTransition(mExitTransition, null,
                        R.styleable.Window_windowExitTransition);
                mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION,
                        R.styleable.Window_windowReenterTransition);
                mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null,
                        R.styleable.Window_windowSharedElementEnterTransition);
                mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition,
                        USE_DEFAULT_TRANSITION,
                        R.styleable.Window_windowSharedElementReturnTransition);
                mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null,
                        R.styleable.Window_windowSharedElementExitTransition);
                mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition,
                        USE_DEFAULT_TRANSITION,
                        R.styleable.Window_windowSharedElementReenterTransition);
                if (mAllowEnterTransitionOverlap == null) {
                    mAllowEnterTransitionOverlap = getWindowStyle().getBoolean(
                            R.styleable.Window_windowAllowEnterTransitionOverlap, true);
                }
                if (mAllowReturnTransitionOverlap == null) {
                    mAllowReturnTransitionOverlap = getWindowStyle().getBoolean(
                            R.styleable.Window_windowAllowReturnTransitionOverlap, true);
                }
                if (mBackgroundFadeDurationMillis < 0) {
                    mBackgroundFadeDurationMillis = getWindowStyle().getInteger(
                            R.styleable.Window_windowTransitionBackgroundFadeDuration,
                            DEFAULT_BACKGROUND_FADE_DURATION_MS);
                }
                if (mSharedElementsUseOverlay == null) {
                    mSharedElementsUseOverlay = getWindowStyle().getBoolean(
                            R.styleable.Window_windowSharedElementsUseOverlay, true);
                }
            }
        }
    }

重点在代码的第 3 行我们看到 mDecor = generateDecor();方法调用,继续跳进 generateDecor()方法:

protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }

这里生成一个DecorView对象,DecorView是PhoneWindow类的内部类,继承自FrameLayout。到眼下为止。setContentView方法里生成一个FrameLayout类型的DecorView组件。

继续分析代码,看第 11 行:

mContentParent = generateLayout(mDecor);

把 DecorView 对象 mDecor 作为參数传递给 generateLayout方法得到 mContentParent。

generateLayout()方法中的代码实现例如以下:
protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.

        TypedArray a = getWindowStyle();

        .........

        /**下面这些是Activity 窗体属性特征的设置*/
        //窗体是否浮动,一般用于Dialog窗体是否浮动:是否显示在布局的正中间。
        mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
        int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
                & (~getForcedWindowFlags());
        if (mIsFloating) {
            setLayout(WRAP_CONTENT, WRAP_CONTENT);
            setFlags(0, flagsToUpdate);
        } else {
            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
        }
        //设置窗体是否支持标题栏。隐藏显示标题栏操作在此处。
        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);
        }
        //ActionBar导航栏是否不占布局空间叠加显示在当前窗体之上。
        if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {
            requestFeature(FEATURE_ACTION_BAR_OVERLAY);
        }

        if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {
            requestFeature(FEATURE_ACTION_MODE_OVERLAY);
        }

        if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
            requestFeature(FEATURE_SWIPE_TO_DISMISS);
        }
        //当前Activity是否支持全屏
        if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
        }

       ................

       //设置状态栏的颜色
        if (!mForcedStatusBarColor) {
            mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
        }
        //设置导航栏的颜色
        if (!mForcedNavigationBarColor) {
            mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
        }

        if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
                >= android.os.Build.VERSION_CODES.HONEYCOMB) {
            if (a.getBoolean(
                    R.styleable.Window_windowCloseOnTouchOutside,
                    false)) {
                setCloseOnTouchOutsideIfNotSet(true);
            }
        }

        WindowManager.LayoutParams params = getAttributes();
        //设置输入法的状态
        if (!hasSoftInputMode()) {
            params.softInputMode = a.getInt(
                    R.styleable.Window_windowSoftInputMode,
                    params.softInputMode);
        }

        if (a.getBoolean(R.styleable.Window_backgroundDimEnabled,
                mIsFloating)) {
            /* All dialogs should have the window dimmed */
            if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {
                params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
            }
            if (!haveDimAmount()) {
                params.dimAmount = a.getFloat(
                        android.R.styleable.Window_backgroundDimAmount, 0.5f);
            }
        }
        //设置当前Activity的出现动画效果
        if (params.windowAnimations == 0) {
            params.windowAnimations = a.getResourceId(
                    R.styleable.Window_windowAnimationStyle, 0);
        }

        // The rest are only done if this window is not embedded; otherwise,
        // the values are inherited from our container.
        if (getContainer() == null) {
            if (mBackgroundDrawable == null) {
                if (mBackgroundResource == 0) {
                    mBackgroundResource = a.getResourceId(
                            R.styleable.Window_windowBackground, 0);
                }
                if (mFrameResource == 0) {
                    mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);
                }
                mBackgroundFallbackResource = a.getResourceId(
                        R.styleable.Window_windowBackgroundFallback, 0);
                if (false) {
                    System.out.println("Background: "
                            + Integer.toHexString(mBackgroundResource) + " Frame: "
                            + Integer.toHexString(mFrameResource));
                }
            }
            mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
            mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
            mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);
        }

        //下面代码为当前Activity窗体加入 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;
        } 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;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
            // System.out.println("Title Icons!");
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            // Special case for a window with only a progress bar (and title).
            // XXX Need to have a no-title version of embedded windows.
            layoutResource = R.layout.screen_progress;
            // System.out.println("Progress!");
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            // Special case for a window with a custom title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_custom_title;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
        } else 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;
            }
            // 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();

        //通过布局加入器LayoutInflater获取layoutResource布局,
        View in = mLayoutInflater.inflate(layoutResource, null);
        //将XML资源为layoutResource的布局加入到decor容器里面。至此PhoneWindow 内部类DecorView就加入了之布局
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        mContentRoot = (ViewGroup) in;

        //此处非常重要。通过findViewById找到 contentParent容器,也是该方法的返回值。
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
            ProgressBar progress = getCircularProgressBar(false);
            if (progress != null) {
                progress.setIndeterminate(true);
            }
        }

        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            registerSwipeCallbacks();
        }

        // Remaining setup -- of background and title -- that only applies
        // to top-level windows.
        //下面代码设置Activity窗体的背景,标题等
        if (getContainer() == null) {
            final Drawable background;
            if (mBackgroundResource != 0) {
                background = getContext().getDrawable(mBackgroundResource);
            } else {
                background = mBackgroundDrawable;
            }
            mDecor.setWindowBackground(background);

            final Drawable frame;
            if (mFrameResource != 0) {
                frame = getContext().getDrawable(mFrameResource);
            } else {
                frame = null;
            }
            mDecor.setWindowFrame(frame);

            mDecor.setElevation(mElevation);
            mDecor.setClipToOutline(mClipToOutline);

            if (mTitle != null) {
                setTitle(mTitle);
            }

            if (mTitleColor == 0) {
                mTitleColor = mTextColor;
            }
            setTitleColor(mTitleColor);
        }

        mDecor.finishChanging();

        return contentParent;
    }

以上代码代比較复杂。主要做了下面几件事情。

从第 8 行到第110行,主要是初始化窗体的特征。是否显示标题栏。是否全屏,是否支持ActionBar浮动等等。

第112-187行。主要是给容器DecorView加入id为layoutResource布局的根布局。待会分析layoutResource布局。

第178行。通过LayoutInflater 将xml布局转换成VIEW.

第180行将找到的View加入到DecorView根布局其中。

第184行,从根布局中找到id为R.id.content的 contentParent 容器。也就是当前方法的返回值。

接下来,看看 id为layoutResource的布局究竟实现了什么?我们来看看171行的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>

原来我们的DecorView根布局里面加入了相似上面的布局,线性布局LinearLaout里包括两个组件,ViewStub是懒载入,默认不显示。FrameLayout是什么呢?看看id=content,就是我们184行找到的父容器 contentParent。那么这个父容器 contentParent有什么作用呢?

我们回到 Step4 的第 11行,mContentParent = generateLayout(mDecor); 获得 父容器 mContentParent。我们再次回到 Step3步的第17行, mLayoutInflater.inflate(layoutResID, mContentParent); 这里通过LayoutInflater将 setContentView(layoutResID)传进来的布局id载入到 父容器mContentParent中,至此。setContentView就将布局加入到Activity里面了。

如今我们来梳理一下流程:

Activity setContentView—>Window setContentView—>PhoneWindow setContentView—->PhoneWindow installDecor—–>PhoneWindow generateLayout——>PhoneWindow mLayoutInflater.inflate(layoutResID, mContentParent);

Activity 类中有一个Window抽象类的实现PhoneWindow类,该类中有个内部类DecorView。继承自FrameLayout。在DecorView容器中加入了根布局。根布局中包括了一个id为 contnet的FrameLayout 内容布局。我们的Activity载入的布局xml最后加入到 id为content的FrameLayout布局其中了

1.关于requestWindowFeature(Window.FEATURE_NO_TITLE); 去除标题栏的疑问,假设你自己的xxxActivity是继承自Activity。那么恭喜你使用以上方法能够去除标题栏,假设你自己的xxxActivity是继承自AppCompatActivity或者ActionBarActivity,那么非常遗憾告诉你,此次系统默认的标题栏已经在主题中去除,此时显示的标题栏是ActionBar导航栏,假设须要去除导航栏,你能够通过例如以下代码:getSupportActionBar().hide();来隐藏导航栏。

2.requestWindowFeature(Window.FEATURE_NO_TITLE);方法须要在 setContentView方法之前使用。由上面 Step5分析可得。设置Activity Window 特征是在setContentView方法中设置的,因此,假设须要改变Activity Window窗体特征,须要在setContentView方法之前。

事实上这里有疑问???为什么设置全屏的方法getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

关于mLayoutInflater.inflate我会在下篇给大家具体解说的。

太特么累了,我要喝脉动。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章