zoukankan      html  css  js  c++  java
  • 从setContentView()谈起

    从setContentView()谈起

    本文主要讲解View或者ViewGroup是如何添加到应用程序的窗口中的。

    1. 最简单的Activity

    一个Activity最简单的结构是如下:

         public class MainActivity extends Activity {
            private Button taskSwitch;
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                //那就从这个setContentView开启我们的View或者ViewGroup加载之旅吧
                setContentView(R.layout.activity_main);
            }
        }
    

    R.layout.activity_main.xml结构如下:

        <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingBottom="@dimen/activity_vertical_margin"
            android:paddingLeft="@dimen/activity_horizontal_margin"
            android:paddingRight="@dimen/activity_horizontal_margin"
            android:paddingTop="@dimen/activity_vertical_margin"
            tools:context=".MainActivity" >
        
            <Button android:id="@+id/taskSwitch"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/taskSwitch"/>
        
        </RelativeLayout>
    

    2. setContentView关键流程顺序图

    Step1 Activity.setContentView()

    这个方法是Activity里面的方法,直接回调了Window的getContentView(),而mWindow对象实际上是PhoneWindow对象。
    frameworks/base/core/java/android/app/Activity.java

        public class Activity extends ContextThemeWrapper                                                                       
                implements LayoutInflater.Factory2,                                                                             
                Window.Callback, KeyEvent.Callback,                                                                             
                OnCreateContextMenuListener, ComponentCallbacks2 {
                
            public void setContentView(int layoutResID) {  
                getWindow().setContentView(layoutResID);  
            }  
              
            public Window getWindow() {  
                return mWindow;   //Window对象,实际上是一个PhoneWindow对象,它是在AMS初始化的时候创建的  
            }
        }
    

    AMS初始化的时候会调用PolicyManager.makeNewWindow(this);

        public class Activity extends ContextThemeWrapper                                                                       
                implements LayoutInflater.Factory2,                                                                             
                Window.Callback, KeyEvent.Callback,                                                                             
                OnCreateContextMenuListener, ComponentCallbacks2 {
                
            final void attach(Context context, ActivityThread aThread,                                                          
                    Instrumentation instr, IBinder token, int ident,
                    Application application, Intent intent, ActivityInfo info,                                                  
                    CharSequence title, Activity parent, String id,                                                             
                    NonConfigurationInstances lastNonConfigurationInstances,
                    Configuration config) {
                attachBaseContext(context);
                
                mFragments.attachActivity(this, mContainer, null);
                 
                mWindow = PolicyManager.makeNewWindow(this); //这里创建的实际上是PhoneWindow对象
                mWindow.setCallback(this);
                mWindow.getLayoutInflater().setPrivateFactory(this);                                                            
                ……
            } 
        }
    

    而PolicyManager是调用的IPolicy的接口实现穿件Window的。

        public final class PolicyManager {
             private static final String POLICY_IMPL_CLASS_NAME =
                 "com.android.internal.policy.impl.Policy";
                 
             private static final IPolicy sPolicy;
             ……
             public static Window makeNewWindow(Context context) {                                                                
                 return sPolicy.makeNewWindow(context);                                                                           
             }
             ……
        }
    

    frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java实现了IPolicy接口,创建了Window对象,实际上是PhoneWindow对象

         // Simple implementation of the policy interface that spawns the right
         // set of objects
         public class Policy implements IPolicy {                                                                                 
             private static final String TAG = "PhonePolicy"; 
             ……
             public Window makeNewWindow(Context context) {
             return new PhoneWindow(context); //创建了Window对象,实际上是PhoneWindow对象
             }
        }
    

    到此,我们知道Activity.setContentView()最终调用到的是PhoneWindow.setContentView()。

    Step2 PhoneWindow.setContentView()

    如果第一次调用mDecor和mContentParent对象都为null,会调用installDecor(),如果不是,则调用mContentParent.removeAllViews()来删除所有的子View,然后inflate所以的View,再添加到mContentParent中。

        public class PhoneWindow extends Window implements MenuBuilder.Callback {
             @Override
             public void setContentView(int layoutResID) {
                 //如果第一次调用mDecor和mContentParent对象都为null,会调用installDecor(),如果不是,则调用mContentParent.removeAllViews()
                 if (mContentParent == null) {
                     installDecor();
                 } else {
                     mContentParent.removeAllViews();
                 }
                 mLayoutInflater.inflate(layoutResID, mContentParent);
                 final Callback cb = getCallback();
                 if (cb != null && !isDestroyed()) {
                     cb.onContentChanged();
                 }
             }
        }
    

    Step2.1 PhoneWindow. installDecor()

        public class PhoneWindow extends Window implements MenuBuilder.Callback {
        
            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();
        
                    mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
                    if (mTitleView != null) {
                        mTitleView.setLayoutDirection(mDecor.getLayoutDirection());
                        if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                            View titleContainer = findViewById(com.android.internal.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);
                        }
                    } else {
                        mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
                        if (mActionBar != null) {
                            mActionBar.setWindowCallback(getCallback());
                            if (mActionBar.getTitle() == null) {
                                mActionBar.setWindowTitle(mTitle);
                            }
                            final int localFeatures = getLocalFeatures();
                            if ((localFeatures & (1 << FEATURE_PROGRESS)) != 0) {
                                mActionBar.initProgress();
                            }
                            if ((localFeatures & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
                                mActionBar.initIndeterminateProgress();
                            }
        
                            final ActionBarOverlayLayout abol = (ActionBarOverlayLayout) findViewById(
                                    com.android.internal.R.id.action_bar_overlay_layout);
                            if (abol != null) {
                                abol.setOverlayMode(
                                        (localFeatures & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0);
                            }
        
                            boolean splitActionBar = false;
                            final boolean splitWhenNarrow =
                                    (mUiOptions & ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW) != 0;
                            if (splitWhenNarrow) {
                                splitActionBar = getContext().getResources().getBoolean(
                                        com.android.internal.R.bool.split_action_bar_is_narrow);
                            } else {
                                splitActionBar = getWindowStyle().getBoolean(
                                        com.android.internal.R.styleable.Window_windowSplitActionBar, false);
                            }
                            final ActionBarContainer splitView = (ActionBarContainer) findViewById(
                                    com.android.internal.R.id.split_action_bar);
                            if (splitView != null) {
                                mActionBar.setSplitView(splitView);
                                mActionBar.setSplitActionBar(splitActionBar);
                                mActionBar.setSplitWhenNarrow(splitWhenNarrow);
        
                                final ActionBarContextView cab = (ActionBarContextView) findViewById(
                                        com.android.internal.R.id.action_context_bar);
                                cab.setSplitView(splitView);
                                cab.setSplitActionBar(splitActionBar);
                                cab.setSplitWhenNarrow(splitWhenNarrow);
                            } else if (splitActionBar) {
                                Log.e(TAG, "Requested split action bar with " +
                                        "incompatible window decor! Ignoring request.");
                            }
        
                            if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||
                                    (mIconRes != 0 && !mActionBar.hasIcon())) {
                                mActionBar.setIcon(mIconRes);
                            } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&
                                    mIconRes == 0 && !mActionBar.hasIcon()) {
                                mActionBar.setIcon(
                                        getContext().getPackageManager().getDefaultActivityIcon());
                                mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
                            }
                            if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||
                                    (mLogoRes != 0 && !mActionBar.hasLogo())) {
                                mActionBar.setLogo(mLogoRes);
                            }
        
                            // Post the panel invalidate for later; avoid application onCreateOptionsMenu
                            // being called in the middle of onCreate or similar.
                            mDecor.post(new Runnable() {
                                public void run() {
                                    // Invalidate if the panel menu hasn't been created before this.
                                    PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
                                    if (!isDestroyed() && (st == null || st.menu == null)) {
                                        invalidatePanelMenu(FEATURE_ACTION_BAR);
                                    }
                                }
                            });
                        }
                    }
                }
            }
        }
    

    首先判断mDecor是否为空,如果是空,则调用generateDecor()创建一个新的DecorView赋值给mDecor,该类是一个FrameLayout的子类,是一个ViewGroup,当用HierarchyViewer查看Layoutt的时候每个窗口最顶层都有的一个FrameLayout,这个FrameLayout就是这边的这个DecorView。

        protected DecorView generateDecor() {
            return new DecorView(getContext(), -1);
        }
    
        private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
        }
    

    然后判断mContentParent是否为空,如果为空,则调用generateLayout(mDecor)创建mContentParent。

        protected ViewGroup generateLayout(DecorView decor) {
            // Apply data from current theme.
            
            // 1. 从com.android.internal.R.styleable.Window也就是android:theme=“”配置的值中获取Window的Style属性
            // 2. 通过requestFeature方法设置Window.mFeatures和PhoneWindow.mLocalFeatures属性,设置宽口风格
            
            TypedArray a = getWindowStyle();
    
            if (false) {
                System.out.println("From style:");
                String s = "Attrs:";
                for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) {
                    s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "="
                            + a.getString(i);
                }
                System.out.println(s);
            }
    
            mIsFloating = a.getBoolean(com.android.internal.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(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
                requestFeature(FEATURE_NO_TITLE);
            } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {
                // Don't allow an action bar if there is no title.
                requestFeature(FEATURE_ACTION_BAR);
            }
    
            if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {
                requestFeature(FEATURE_ACTION_BAR_OVERLAY);
            }
    
            if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionModeOverlay, false)) {
                requestFeature(FEATURE_ACTION_MODE_OVERLAY);
            }
    
            if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
                setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
            }
    
            if (a.getBoolean(com.android.internal.R.styleable.Window_windowTranslucentStatus,
                    false)) {
                setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS
                        & (~getForcedWindowFlags()));
            }
    
            if (a.getBoolean(com.android.internal.R.styleable.Window_windowTranslucentNavigation,
                    false)) {
                setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION
                        & (~getForcedWindowFlags()));
            }
    
            if (a.getBoolean(com.android.internal.R.styleable.Window_windowOverscan, false)) {
                setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));
            }
    
            if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {
                setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
            }
    
            if (a.getBoolean(com.android.internal.R.styleable.Window_windowEnableSplitTouch,
                    getContext().getApplicationInfo().targetSdkVersion
                            >= android.os.Build.VERSION_CODES.HONEYCOMB)) {
                setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));
            }
    
            a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
            a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
            if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor)) {
                if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
                a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor,
                        mFixedWidthMajor);
            }
            if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor)) {
                if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
                a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor,
                        mFixedWidthMinor);
            }
            if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor)) {
                if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
                a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor,
                        mFixedHeightMajor);
            }
            if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor)) {
                if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
                a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor,
                        mFixedHeightMinor);
            }
    
            // 3.读取SDK版本信息
            final Context context = getContext();
            final int targetSdk = context.getApplicationInfo().targetSdkVersion;
            final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
            final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
            final boolean targetHcNeedsOptions = context.getResources().getBoolean(
                    com.android.internal.R.bool.target_honeycomb_needs_options_menu);
            final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
    
            if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
                addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
            } else {
                clearFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
            }
            
            if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
                    >= android.os.Build.VERSION_CODES.HONEYCOMB) {
                if (a.getBoolean(
                        com.android.internal.R.styleable.Window_windowCloseOnTouchOutside,
                        false)) {
                    setCloseOnTouchOutsideIfNotSet(true);
                }
            }
            
            // 4. 获取LayoutParams信息用来判断是否输入法、是否模糊后面的,是否有窗口动画
            WindowManager.LayoutParams params = getAttributes();
    
            if (!hasSoftInputMode()) {
                params.softInputMode = a.getInt(
                        com.android.internal.R.styleable.Window_windowSoftInputMode,
                        params.softInputMode);
            }
    
            if (a.getBoolean(com.android.internal.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);
                }
            }
    
            if (params.windowAnimations == 0) {
                params.windowAnimations = a.getResourceId(
                        com.android.internal.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(
                                com.android.internal.R.styleable.Window_windowBackground, 0);
                    }
                    if (mFrameResource == 0) {
                        mFrameResource = a.getResourceId(com.android.internal.R.styleable.Window_windowFrame, 0);
                    }
                    if (false) {
                        System.out.println("Background: "
                                + Integer.toHexString(mBackgroundResource) + " Frame: "
                                + Integer.toHexString(mFrameResource));
                    }
                }
                mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000);
            }
    
            // Inflate the window decor.
    
            // 5. 获取上面设置的PhoneWindow.mLocalFeatures,根据一些列的条件判断需要是哪个layoutResource,比如是否是全屏,是否有标题栏
            // 有如下7种类型的layout:
            // com.android.internal.R.layout.screen_title_icons
            // com.android.internal.R.layout.screen_progress
            // com.android.internal.R.layout.screen_custom_title
            // com.android.internal.R.layout.screen_action_bar
            // com.android.internal.R.layout.screen_title
            // com.android.internal.R.layout.screen_simple_overlay_action_mode
            // com.android.internal.R.layout.screen_simple
            
            int layoutResource;
            int features = getLocalFeatures();
            // System.out.println("Features: 0x" + Integer.toHexString(features));
            if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
                if (mIsFloating) {
                    TypedValue res = new TypedValue();
                    getContext().getTheme().resolveAttribute(
                            com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
                    layoutResource = res.resourceId;
                } else {
                    layoutResource = com.android.internal.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 = com.android.internal.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(
                            com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);
                    layoutResource = res.resourceId;
                } else {
                    layoutResource = com.android.internal.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(
                            com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
                    layoutResource = res.resourceId;
                } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                    layoutResource = com.android.internal.R.layout.screen_action_bar;
                } else {
                    layoutResource = com.android.internal.R.layout.screen_title;
                }
                // System.out.println("Title!");
            } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
                layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
            } else {
                // Embedded, so no decoration is needed.
                layoutResource = com.android.internal.R.layout.screen_simple;
                // System.out.println("Simple!");
            }
    
            mDecor.startChanging();
    
            // 6. 填充相应的layoutResource,并把它加到decor里面
            View in = mLayoutInflater.inflate(layoutResource, null);
            decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    
            // 7. 找出相应的ID_ANDROID_CONTENT的ViewGroup,实际想就是上面layoutResource里面的<FrameLayout android:id="@android:id/content" /> 这个FrameLayout
            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);
                }
            }
    
            // Remaining setup -- of background and title -- that only applies
            // to top-level windows.
            if (getContainer() == null) {
                Drawable drawable = mBackgroundDrawable;
                if (mBackgroundResource != 0) {
                    drawable = getContext().getResources().getDrawable(mBackgroundResource);
                }
                mDecor.setWindowBackground(drawable);
                drawable = null;
                if (mFrameResource != 0) {
                    drawable = getContext().getResources().getDrawable(mFrameResource);
                }
                mDecor.setWindowFrame(drawable);
    
                // System.out.println("Text=" + Integer.toHexString(mTextColor) +
                // " Sel=" + Integer.toHexString(mTextSelectedColor) +
                // " Title=" + Integer.toHexString(mTitleColor));
    
                if (mTitleColor == 0) {
                    mTitleColor = mTextColor;
                }
    
                if (mTitle != null) {
                    setTitle(mTitle);
                }
                setTitleColor(mTitleColor);
            }
    
            mDecor.finishChanging();
    
            return contentParent;
        }
    

    此时,我们拿一个常用的com.android.internal.R.layout.screen_title窗口布局文件来分析一下。
    这个窗口布局文件分为3个部分:

    1. action mode
    2. 标题栏内容区域
    3. Activity的SetContentView的Layout文件实际放置的俯视图就是这个id为conten的FrameLayout
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:fitsSystemWindows="true"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <!-- Popout bar for action modes -->
            <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" />
            <RelativeLayout android:id="@android:id/title_container"
                style="?android:attr/windowTitleBackgroundStyle"
                android:layout_width="match_parent"
                android:layout_height="?android:attr/windowTitleSize">
                <!-- The title background has 9px left padding. -->
                <ImageView android:id="@android:id/left_icon"
                    android:visibility="gone"
                    android:layout_marginEnd="9dip"
                    android:layout_width="16dip"
                    android:layout_height="16dip"
                    android:scaleType="fitCenter"
                    android:layout_alignParentStart="true"
                    android:layout_centerVertical="true" />
                <ProgressBar android:id="@+id/progress_circular"
                    style="?android:attr/progressBarStyleSmallTitle"
                    android:visibility="gone"
                    android:max="10000"
                    android:layout_centerVertical="true"
                    android:layout_alignParentEnd="true"
                    android:layout_marginStart="6dip"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
                <!-- There are 6dip between this and the circular progress on the right, we
                     also make 6dip (with the -3dip margin_left) to the icon on the left or
                     the screen left edge if no icon. This also places our left edge 3dip to
                     the left of the title text left edge. -->
                <ProgressBar android:id="@+id/progress_horizontal"
                    style="?android:attr/progressBarStyleHorizontal"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="-3dip"
                    android:layout_toStartOf="@android:id/progress_circular"
                    android:layout_toEndOf="@android:id/left_icon"
                    android:layout_centerVertical="true"
                    android:visibility="gone"
                    android:max="10000" />
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="horizontal"
                    android:layout_toStartOf="@id/progress_circular"
                    android:layout_toEndOf="@android:id/left_icon"
                    >
                    <TextView android:id="@android:id/title"
                        style="?android:attr/windowTitleStyle"
                        android:layout_width="0dip"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:background="@null"
                        android:fadingEdge="horizontal"
                        android:scrollHorizontally="true"
                        android:gravity="center_vertical"
                        android:layout_marginEnd="2dip"
                        />
                    <!-- 2dip between the icon and the title text, if icon is present. -->
                    <ImageView android:id="@android:id/right_icon"
                        android:visibility="gone"
                        android:layout_width="16dip"
                        android:layout_height="16dip"
                        android:layout_weight="0"
                        android:layout_gravity="center_vertical"
                        android:scaleType="fitCenter"
                        />
                    </LinearLayout>
            </RelativeLayout>
            <FrameLayout android:id="@android:id/content"
                android:layout_width="match_parent"
                android:layout_height="0dip"
                android:layout_weight="1"
                android:foregroundGravity="fill_horizontal|top"
                android:foreground="?android:attr/windowContentOverlay" />
        </LinearLayout>
    

    Step2.2 LayoutInflater.inflate()

    前一步结束之后,我们需要的mDecor和mContentParent都已经初始化完毕,mContentParent( )就是我们Activity中setContentView的视图所要填充到的父视图。

        public View inflate(int resource, ViewGroup root) {
            return inflate(resource, root, root != null);
        }
    
        public View inflate(XmlPullParser parser, ViewGroup root) {
            return inflate(parser, root, root != null);
        }
    
        public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
            if (DEBUG) System.out.println("INFLATING from resource: " + resource);
            XmlResourceParser parser = getContext().getResources().getLayout(resource);
            try {
                return inflate(parser, root, attachtoroot);
            } finally {
                parser.close();
            }
        }
    
        public view inflate(xmlpullparser parser, viewgroup root, boolean attachtoroot) {
            synchronized (mconstructorargs) {
                trace.tracebegin(trace.trace_tag_view, "inflate");
    
                final attributeset attrs = xml.asattributeset(parser);
                context lastcontext = (context)mconstructorargs[0];
                mconstructorargs[0] = mcontext;
                view result = root;
    
                try {
                    // 1. 查找并初始化跟试图节点
                    // look for the root node.
                    
                    ……
                    final string name = parser.getname();
                    
    
                    if (tag_merge.equals(name)) {
                        if (root == null || !attachtoroot) {
                            throw new inflateexception("<merge /> can be used only with a valid "
                                    + "viewgroup root and attachtoroot=true");
                        }
    
                        rinflate(parser, root, attrs, false);
                    } else {
                        // temp is the root view that was found in the xml
                        view temp;
                        if (tag_1995.equals(name)) {
                            temp = new blinklayout(mcontext, attrs);
                        } else {
                            temp = createviewfromtag(root, name, attrs);
                        }
    
                        viewgroup.layoutparams params = null;
    
                        if (root != null) {
                            if (debug) {
                                system.out.println("creating params from root: " +
                                        root);
                            }
                            // create layout params that match root, if supplied
                            params = root.generatelayoutparams(attrs);
                            if (!attachtoroot) {
                                // set the layout params for temp if we are not
                                // attaching. (if we are, we use addview, below)
                                temp.setlayoutparams(params);
                            }
                        }
    
                        if (debug) {
                            system.out.println("-----> start inflating children");
                        }
                        // 创建所有的子试图节点
                        // inflate all children under temp
                        rinflate(parser, temp, attrs, true);
                        
                        ……
                    }
    
                } catch (xmlpullparserexception e) {
                    inflateexception ex = new inflateexception(e.getmessage());
                    ex.initcause(e);
                    throw ex;
                } catch (ioexception e) {
                    inflateexception ex = new inflateexception(
                            parser.getpositiondescription()
                            + ": " + e.getmessage());
                    ex.initcause(e);
                    throw ex;
                } finally {
                    // don't retain static reference on context.
                    mconstructorargs[0] = lastcontext;
                    mconstructorargs[1] = null;
                }
    
                trace.traceend(trace.trace_tag_view);
    
                return result;
            }
        }
    

    实际上mLayoutInflater实际上是PhoneLayoutInflater的对象,可以分析Step1中PhoneWindow的初始化过程得知。可以看如下代码:

        public class PhoneWindow extends Window implements MenuBuilder.Callback {
            public PhoneWindow(Context context) {
                super(context);
                mLayoutInflater = LayoutInflater.from(context);
            }
        }
        
        public abstract class LayoutInflater {
            public static LayoutInflater from(Context context) {
                LayoutInflater LayoutInflater =
                        (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                if (LayoutInflater == null) {
                    throw new AssertionError("LayoutInflater not found.");
                }
                return LayoutInflater;
            }
        }
        
        class ContextImpl extends Context {
            static {
                registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
                        public Object createService(ContextImpl ctx) {
                            return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
                        }});
            }
            
            private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP =
            new HashMap<String, ServiceFetcher>();
    
            private static int sNextPerContextServiceCacheIndex = 0;
            private static void registerService(String serviceName, ServiceFetcher fetcher) {
                if (!(fetcher instanceof StaticServiceFetcher)) {
                    fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
                }
                SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
            }
    
            public Object getSystemService(String name) {
                ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
                return fetcher == null ? null : fetcher.getService(this);
            }
        }
        
        public final class PolicyManager {
            private static final String POLICY_IMPL_CLASS_NAME =
                "com.android.internal.policy.impl.Policy";
        
            private static final IPolicy sPolicy;
        
            static {
                // Pull in the actual implementation of the policy at run-time
                try {
                    Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
                    sPolicy = (IPolicy)policyClass.newInstance();
                } 
                ……
            }
            public static LayoutInflater makeNewLayoutInflater(Context context) {
                return sPolicy.makeNewLayoutInflater(context);
            }
        }
    
    
        public class Policy implements IPolicy {
            public LayoutInflater makeNewLayoutInflater(Context context) {
                return new PhoneLayoutInflater(context);
            }
        }
    

    至此,总的流程都已经结束了,我们的Acitivity.setContentView就差不多分析完毕了。总的来说上面的过程可以概括为3步:

    1、创建一个DecorView对象,该对象将作为整个应用窗口的根视图
    2、创建不同的窗口布局文件,并且获取Activity的布局文件该存放的地方,即该窗口布局文件内id为content的FrameLayout指定() 。
    3、将Activity的布局文件添加至id为content的FrameLayout内。

    3. 最后我们来分析一下每一个View或者ViewGroup是如何被创建的。

    从上面我们可以得知,每一个View或者ViewGroup都是通过调用LayoutInflater.createviewfromtag()来进行View的穿件

        View createViewFromTag(View parent, String name, AttributeSet attrs) {
            if (name.equals("view")) {
                name = attrs.getAttributeValue(null, "class");
            }
    
            if (DEBUG) System.out.println("******** Creating view: " + name);
    
            try {
                View view;
                // 此处几个Factory都不会执行
                if (mFactory2 != null) view = mFactory2.onCreateView(parent, name, mContext, attrs);
                else if (mFactory != null) view = mFactory.onCreateView(name, mContext, attrs);
                else view = null;
    
                if (view == null && mPrivateFactory != null) {
                    view = mPrivateFactory.onCreateView(parent, name, mContext, attrs);
                }
                if (view == null) {
                // 根据View是否是内置判断需要调用的方法:
                // 如果是内置的(android.widget或android.webkit或andriod.view包里的)android自身类就调用LayoutInflater.onCreateView (3parameters),如果不是就调用LayoutInflater.createView
                如果不是内置(自定义的,或者目录不在上述几个包中的)则调用LayoutInflater.createView(name, null, attrs);
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(parent, name, attrs);
                    } else {
                        view = createView(name, null, attrs);
                    }
                }
    
                if (DEBUG) System.out.println("Created view is: " + view);
                return view;
    
            ……
        }
    

    根据View是否是内置判断需要调用的方法:
    如果是内置的(android.widget或android.webkit或andriod.view包里的)android自身类就调用LayoutInflater.onCreateView (3parameters),如果不是就调用LayoutInflater.createView;
    如果不是内置(自定义的,或者目录不在上述几个包中的)则调用LayoutInflater.createView(name, null, attrs);
    如果是内置的(android.widget或android.webkit)android自身类就调用LayoutInflater.createView,如果不是就调用LayoutInflater.onCreateView(2 parameters);

        public abstract class LayoutInflater {
            protected View onCreateView(String name, AttributeSet attrs)
                    throws ClassNotFoundException {
                return createView(name, "android.view.", attrs);
            }
        
            protected View onCreateView(View parent, String name, AttributeSet attrs)
                    throws ClassNotFoundException {
                // 1. 根据多态性,直接调用PhoneLayoutInflater.onCreateView(2 parameters)
                return onCreateView(name, attrs);
            }
        }
        
        public class PhoneLayoutInflater extends LayoutInflater {
            private static final String[] sClassPrefixList = {
                "android.widget.",
                "android.webkit."
            };
    
            @Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
                // 2. 如果是内置的(android.widget或android.webkit)android自身类就调用LayoutInflater.createView,如果不是就调用LayoutInflater.onCreateView(2 parameters)
                for (String prefix : sClassPrefixList) {
                    try {
                        View view = createView(name, prefix, attrs);
                        if (view != null) {
                            return view;
                        }
                    } catch (ClassNotFoundException e) {
                        // In this case we want to let the base class take a crack
                        // at it.
                    }
                }
        
                // 2. 
                return super.onCreateView(name, attrs);
            }
        }
        
        不过最终都是要调用createView()方法来进行View或者ViewGroup的创建。createView()的方法气势很简答,就是通过反射机制把View或者ViewGroup给创建出来。
        
        public final View createView(String name, String prefix, AttributeSet attrs)
                throws ClassNotFoundException, InflateException {
            // 1. 如果sConstructorMap里面已经有对应的类,则直接取出来,
            // 如果没有,则通过发射机制把对应的类给loadClass进来;
            Constructor<? extends View> constructor = sConstructorMap.get(name);
            Class<? extends View> clazz = null;
    
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
    
                if (constructor == null) {
                    // Class not found in the cache, see if it's real, and try to add it
                    clazz = mContext.getClassLoader().loadClass(
                            prefix != null ? (prefix + name) : name).asSubclass(View.class);
                    
                    if (mFilter != null && clazz != null) {
                        boolean allowed = mFilter.onLoadClass(clazz);
                        if (!allowed) {
                            failNotAllowed(name, prefix, attrs);
                        }
                    }
                    constructor = clazz.getConstructor(mConstructorSignature);
                    sConstructorMap.put(name, constructor);
                } else {
                    // If we have a filter, apply it to cached constructor
                    if (mFilter != null) {
                        // Have we seen this name before?
                        Boolean allowedState = mFilterMap.get(name);
                        if (allowedState == null) {
                            // New class -- remember whether it is allowed
                            clazz = mContext.getClassLoader().loadClass(
                                    prefix != null ? (prefix + name) : name).asSubclass(View.class);
                            
                            boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
                            mFilterMap.put(name, allowed);
                            if (!allowed) {
                                failNotAllowed(name, prefix, attrs);
                            }
                        } else if (allowedState.equals(Boolean.FALSE)) {
                            failNotAllowed(name, prefix, attrs);
                        }
                    }
                }
    
                Object[] args = mConstructorArgs;
                args[1] = attrs;
    
                // 2. 创建对应的View或者ViewGroup对象。
                constructor.setAccessible(true);
                final View view = constructor.newInstance(args);
                if (view instanceof ViewStub) {
                    // always use ourselves when inflating ViewStub later
                    final ViewStub viewStub = (ViewStub) view;
                    viewStub.setLayoutInflater(this);
                }
                return view;
    
            }
            ……
        }
    
  • 相关阅读:
    话题: 工人重度烧伤 厂方疑暗示“安乐死”后再赔偿
    Linux下使用mail命令发送邮件
    跨境电商优秀网站
    smarty模版使用php标签,如何获取模版变量
    弹窗效果处理和改进
    Keepalived + MySQLfailover + GTIDs 高可用
    linux的常用命令
    linux简单介绍,helloworld,vi使用,用户管理
    linux语言设置i18n(转)
    丢手帕问题
  • 原文地址:https://www.cnblogs.com/GMCisMarkdownCraftsman/p/3754407.html
Copyright © 2011-2022 走看看