美文网首页
Android中setContentView()源码流程解析

Android中setContentView()源码流程解析

作者: NengLee | 来源:发表于2022-10-13 15:42 被阅读0次

    setContentView() 在Activity创建对应的布局,是怎样的工作流程?

    Android32源码进行分析

    //有三个不同的重载函数
    
    public abstract void setContentView(@LayoutRes int resId);
    
    public abstract void setContentView(View v);
    
    public abstract void setContentView(View v, ViewGroup.LayoutParams lp);
    
    

    俩个父类的区别

    setContentView 在继承不同的Activity,其内部的源码有所不同,其本质都是一样,只不过AppCompatActivity是高版本兼容性,在Activity setContentView 上进用了装饰者模式,更好的适应新版本,先以Activity分析 ,再分析AppCompatActivity就如鱼得水

    继承Activity
    public class MainActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);  //跳入 PhoneWind.java
        }
    }
    
    
    继承AppCompatActivity
    
    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);  //跳入 AppCompatDelegateImpl.java
        }
    }
    

    继承自 Activity 的 setContentView

    Activity.java

    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID); //通过getWindow()调用
        initWindowDecorActionBar();
    }
    

    Window

    这里的getWindow 是通过抽象类Window,调用了抽象函数 setContentView(@LayoutRes int resId)

    那么具体实现是在PhoneWind.Java中 ,其PhoneWindwind的子类,且实现了父类中的抽象函数

    public abstract class Window {
        
        public abstract void setContentView(@LayoutRes int layoutResID);
        //...
    }
    
    public class PhoneWindow extends Window implements MenuBuilder.Callback {
        @Override
        public void setContentView(int layoutResID) {
            //下面有具体分析
        }
        //...
    }
    

    PhoneWind类

    PhoneWind是 抽象Window的子类,那么PhoneWind是什么时候创建的呢?

    PhoneWind创建流程:
    1. ActivityThread.performLaunchActivity() --> activity.attach
    2. Activity.attach() --> new PhoneWindow
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //....
        //启动创建一个Activity
        activity = mInstrumentation.newActivity(mClassLoader, component.getClassName(), r.intent);
        //通过启动activity.attach() 
        activity.attach(appContext, this, getInstrumentation(), r.token,
                            r.ident, app, r.intent, r.activityInfo, title, r.parent,
                            r.embeddedID, r.lastNonConfigurationInstances, config,
                            r.referrer, r.voiceInteractor, window, r.configCallback,
                            r.assistToken, r.shareableActivityToken);
        //....
    }
    
    
    final void attach(Context context,....) {
        //省略
        mWindow = new PhoneWindow(this, window, activityConfigCallback); //这里进行New
    }
    

    通过ActivityThread.java 内的performLaunchActivity() 通过newActivity()反射启动对应的actiivty

    在调用基类中的Activity中的attach(),最终PhoneWindnew出来

    简单回忆一下启动调用流程,继续回到PhoneWindow.java ...

    PhoneWindow.java

    @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); //2.重点❤
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true; //3.标识作用❤
    }
    

    重点解析

    1. 其中主要目的创建 DecorView 拿到 mContentParent
    2. 通过Activity的 R.layout.activity.xml 渲染到mContentParent进行布局合成【参考下底部合成图】
    3. 标识状态,在requestWindowFeature(int featureId)必须设置在 setContentView()前才有效,窗口的Title,NoActionBar基本属性

    installDecor()

    • 创建 DecorView 拿到 mContentParent ,带着疑问在源码中进行查看
    private void installDecor() {
            mForceDecorInstall = false;
           // 创建 DecorView 并绑定当前Window
            if (mDecor == null) {
                //创建 DecorView
                mDecor = generateDecor(-1); //1.重点❤ 
                mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
                mDecor.setIsRootNamespace(true);
                if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                    mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
                }
            } else {
                //拿到DecorView与PhoneWind进行绑定
                mDecor.setWindow(this);//2.重点❤ 
            }
        
            if (mContentParent == null) {
                //创建 mContentParent,拿到该对象
                mContentParent = generateLayout(mDecor); //3.重点❤  
    
                // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
                mDecor.makeFrameworkOptionalFitsSystemWindows();
                //....
            }
        
                //根据配置 设置 DecorView
                //...
        
             if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
                //创建TransitionManager 来管理过渡配置
                ...
            }
    
    
    1. generateDecor(-1)DecorViewnull的时候需要创建DecorView
    2. PhoneWindDecorView进行绑定
    3. 创建拿到 mContentParent = generateLayout(mDecor)

    generateDecor(-1)

        protected DecorView generateDecor(int featureId) {
            // System process doesn't have application context and in that case we need to directly use
            // the context we have. Otherwise we want the application context, so we don't cling to the
            // activity.
            Context context;
            if (mUseDecorContext) {
                Context applicationContext = getContext().getApplicationContext();
                if (applicationContext == null) {
                    context = getContext();
                } else {
                    context = new DecorContext(applicationContext, this);
                    if (mTheme != -1) {
                        context.setTheme(mTheme);
                    }
                }
            } else {
                context = getContext();
            }
            //重点❤, 直接new DecorView 返回对象
            return new DecorView(context, featureId, this, getAttributes());
        }
    

    mDecor.setWindow(this)

    void setWindow(PhoneWindow phoneWindow) {
            mWindow = phoneWindow;
            Context context = getContext();
            if (context instanceof DecorContext) {
                DecorContext decorContext = (DecorContext) context;
                //重点❤,DecorView.setPhoneWind(PhoneWindow) 绑定
                decorContext.setPhoneWindow(mWindow);
            }
            if (mPendingWindowBackground != null) {
                Drawable background = mPendingWindowBackground;
                mPendingWindowBackground = null;
                setWindowBackground(background);
            }
        }
    

    mContentParent = generateLayout(mDecor)

    protected ViewGroup generateLayout(DecorView decor) {
            // Apply data from current theme.
            TypedArray a = getWindowStyle();
            //....
            if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
                //重点❤,可以关注该函数内部有标识 常量 【mContentParentExplicitlySet = true; //3.标识作用❤】 
                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);
            }
       
            //重点❤,拿到layoutResource 根据IDE创建xml选择不同创建的样式
            int layoutResource;
            if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
                //默认为 R.layout.screen_simple.xml
                layoutResource = R.layout.screen_simple;
            }
    
            mDecor.startChanging();
            //重点❤ 内部会通过 inflate, 并且add到DecorView中的R.id.content位置 【后面分析】
            mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); 
    
            return 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>
    

    会拿到android:id/contentID控件,把当初activity xml 进行添加到这里形成新的mDecorCaptionView

    onResourcesLoaded(mLayoutInflater, layoutResource)

    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
            if (mBackdropFrameRenderer != null) {
                loadBackgroundDrawablesIfNeeded();
                mBackdropFrameRenderer.onResourcesLoaded(
                        this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
                        mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
                        getCurrentColor(mNavigationColorViewState));
            }
    
            mDecorCaptionView = createDecorCaptionView(inflater);
           //重点❤  通过拿到模板【R.layout.screen_simple】解析成View
            final View root = inflater.inflate(layoutResource, null);
            if (mDecorCaptionView != null) {
                if (mDecorCaptionView.getParent() == null) {
                    addView(mDecorCaptionView,
                            new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
                }
                mDecorCaptionView.addView(root,
                        new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
            } else {
    
                // 重点❤ 通过addView添加到DecorView
                addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            }
            mContentRoot = (ViewGroup) root;
            initializeElevation();
        }
    

    继承Activity流程图

    SetContentView继承Activity流程图.png

    继承自 AppCompatActivity 的 setContentView

    setContentView()
         @Override
        public void setContentView(@LayoutRes int layoutResID) {
            initViewTreeOwners();
            //重点❤
            getDelegate().setContentView(layoutResID); 
        }
    
        @Override
        public void setContentView(View view) {
            initViewTreeOwners();
            getDelegate().setContentView(view);
        }
    
        @Override
        public void setContentView(View view, ViewGroup.LayoutParams params) {
            initViewTreeOwners();
            getDelegate().setContentView(view, params);
        }
    
    
    getDelegate()
        public AppCompatDelegate getDelegate() {
            if (mDelegate == null) {
                //创建Delegate
                mDelegate = AppCompatDelegate.create(this, this);
            }
            return mDelegate;
        }
    
    
        public static AppCompatDelegate create(@NonNull Activity activity,
                @Nullable AppCompatCallback callback) {
            return new AppCompatDelegateImpl(activity, callback);
        }
    

    核心 setContentView

        @Override
        public void setContentView(int resId) {
            //1.重点❤ 创建Decor 具体细分
            ensureSubDecor();
            //拿到模板资源 android.R.id.content
            ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
            //移除所有View
            contentParent.removeAllViews();
            //2.重点❤ 替换 把 activity xml 进行添加到 contentParent中
            LayoutInflater.from(mContext).inflate(resId, contentParent);
            mAppCompatWindowCallback.getWrapped().onContentChanged();
        }
    

    ensureSubDecor

    private void ensureSubDecor() {
        if (!mSubDecorInstalled) {
            //1.1 重点❤ 创建SubDecor , 其实和Activity中的DecorView一样,命名不同 这里是 SubDecor 容器是ViewGroup对象
            mSubDecor = createSubDecor();
    
            // If a title was set before we installed the decor, propagate it now
            CharSequence title = getTitle();
            if (!TextUtils.isEmpty(title)) {
                if (mDecorContentParent != null) {
                    mDecorContentParent.setWindowTitle(title);
                } else if (peekSupportActionBar() != null) {
                    peekSupportActionBar().setWindowTitle(title);
                } else if (mTitleView != null) {
                    mTitleView.setText(title);
                }
            }
    
               this.ensureWindow();
                this.mWindow.getDecorView();
                LayoutInflater inflater = LayoutInflater.from(this.mContext);
                ViewGroup subDecor = null;
    
            //1.2 重点❤ 标识
            mSubDecorInstalled = true; 
    
            // 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 (!mIsDestroyed && (st == null || st.menu == null)) {
                invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
            }
        }
    }
    
    createSubDecor
    private ViewGroup createSubDecor() {
        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
    
        // Style Theme 的包为AppCompatTheme
        if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
            a.recycle();
            throw new IllegalStateException(
                    "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
        }
        //...
        //重点❤ 创建DecorView 拿到mContentParent【和前面一样 】
        ensureWindow();
        mWindow.getDecorView();
    
        final LayoutInflater inflater = LayoutInflater.from(mContext);
        //重点❤ 变量 subDecor
        ViewGroup subDecor = null;
        //...
        if (mOverlayActionMode) {
                subDecor = (ViewGroup) inflater.inflate(
                        R.layout.abc_screen_simple_overlay_action_mode, null);
            } else {
                subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
        }
       
        //...
        ViewUtils.makeOptionalFitsSystemWindows(subDecor);
        //重点❤  拿到R.layout.abc_screen_simple 内部的view 【subDecor】 
        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);
    
        //重点❤  拿的是Activity里面R.layout.screen_simple内部的View 【ViewDecor】
        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        if (windowContentView != null) {
            //把R.layout.screen_simple 中的Content复制到 R.layout.abc_screen_simple 
            while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }
            //VideDecor 的id设置null
            windowContentView.setId(View.NO_ID); 
            //subDecor的id,设置成content
            contentView.setId(android.R.id.content); 
       
        }
    
        //wubDecor放到phonewind里面去
        mWindow.setContentView(subDecor);
    
        contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
            @Override
            public void onAttachedFromWindow() {}
    
            @Override
            public void onDetachedFromWindow() {
                dismissPopups();
            }
        });
    
        return subDecor;
    }
    

    R.layout.abc_screen_simple

    <androidx.appcompat.widget.FitWindowsLinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/action_bar_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:fitsSystemWindows="true">
    
        <androidx.appcompat.widget.ViewStubCompat
            android:id="@+id/action_mode_bar_stub"
            android:inflatedId="@+id/action_mode_bar"
            android:layout="@layout/abc_action_mode_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
        <include layout="@layout/abc_screen_content_include" />
    
    </androidx.appcompat.widget.FitWindowsLinearLayout>
    
    

    R.layout.abc_screen_content_include

    <!-- 注意是 merge -->
    <merge xmlns:android="http://schemas.android.com/apk/res/android">
    
        <androidx.appcompat.widget.ContentFrameLayout
                android:id="@id/action_bar_activity_content"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:foregroundGravity="fill_horizontal|top"
                android:foreground="?android:attr/windowContentOverlay" />
    
    </merge>
    

    ensureWindow(); 会拿到Activity里面的基本布局R.layout.screen_simple,拿到ViewDecormContentParent

    再用新变量subDecor拿到R.layout.abc_screen_simple 内部的@id/action_bar_activity_content

    在通过替换,把subDecor布局的id进行修改 ,viewDecorid设置成NO_ID

    SetContentView继承AppCompatActivity流程图.png

    渲染布局 mLayoutInflater.inflate(layoutResID, mContentParent)

        public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
            final Resources res = getContext().getResources();
            if (DEBUG) {
                Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                      + Integer.toHexString(resource) + ")");
            }
    
            View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
            if (view != null) {
                return view;
            }
            XmlResourceParser parser = res.getLayout(resource);
            try {
                 //重点❤ XmlResourceParser解析
                return inflate(parser, root, attachToRoot);
            } finally {
                parser.close();
            }
        }
    

    inflate

    public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
    
            final Context inflaterContext = mContext;
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            View result = root;
    
            try {
                advanceToRootNode(parser);
                final String name = parser.getName();
    
                if (DEBUG) {
                    System.out.println("**************************");
                    System.out.println("Creating root view: "
                            + name);
                    System.out.println("**************************");
                }
                
                //merge 标签
                if (TAG_MERGE.equals(name)) {
                    //root==null attachToRoot=false 会报错
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }
                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    //重点❤ 通过反射创建View  --- 布局的rootView
                    final View temp = createViewFromTag(root, name, inflaterContext, 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);
                        // 当root!=null  || attachToRoot=false
                        if (!attachToRoot) {
                            temp.setLayoutParams(params);
                        }
                    }
    
                    if (DEBUG) {
                        System.out.println("-----> start inflating children");
                    }
    
                    //重点❤
                    rInflateChildren(parser, temp, attrs, true);
    
                    if (DEBUG) {
                        System.out.println("-----> done inflating children");
                    }
    
                    // We are supposed to attach all the views we found (int temp)
                    // to root. Do that now.
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }
    
                 
                    // 当root==null  || attachToRoot=false
                    if (root == null || !attachToRoot=false) {
                        result = temp;
                    }
                }
    
            } catch (XmlPullParserException e) {
                final InflateException ie = new InflateException(e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } catch (Exception e) {
                final InflateException ie = new InflateException(
                        getParserStateDescription(inflaterContext, attrs)
                        + ": " + e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } finally {
                // Don't retain static reference on context.
                mConstructorArgs[0] = lastContext;
                mConstructorArgs[1] = null;
    
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
    
            return result;
        }
    }
    

    createViewFromTag

    @UnsupportedAppUsage
    View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class");
        }
    
        // Apply a theme wrapper, if allowed and one is specified.
        if (!ignoreThemeAttr) {
            final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
            final int themeResId = ta.getResourceId(0, 0);
            if (themeResId != 0) {
                context = new ContextThemeWrapper(context, themeResId);
            }
            ta.recycle();
        }
    
        try {
            View view = tryCreateView(parent, name, context, attrs);
    
            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    //重点❤ 判断是否是自定义布局
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(context, parent, name, attrs);
                    } else {
                        view = createView(context, name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }
    
            return view;
        } catch (InflateException e) {
            throw e;
    
        } catch (ClassNotFoundException e) {
            final InflateException ie = new InflateException(
                    getParserStateDescription(context, attrs)
                    + ": Error inflating class " + name, e);
            ie.setStackTrace(EMPTY_STACK_TRACE);
            throw ie;
    
        } catch (Exception e) {
            final InflateException ie = new InflateException(
                    getParserStateDescription(context, attrs)
                    + ": Error inflating class " + name, e);
            ie.setStackTrace(EMPTY_STACK_TRACE);
            throw ie;
        }
    }
    

    if (-1 == name.indexOf('.')) 判断是否控件包名带有. 依据为自定义控件

    onCreateView(context, parent, name, attrs) 通过自定义构造出具体得view,通过PhoneLayoutInflater.onCreateView(name, attrs);

    PhoneLayoutInflater.java

    @Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
        for (String prefix : sClassPrefixList) {
            try {
                //内部继续调用  view = createView(context, name, null, attrs);
                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.
            }
        }
    
        return super.onCreateView(name, attrs);
    }
    

    createView

    @Nullable
    public final View createView(@NonNull Context viewContext, @NonNull String name,
            @Nullable String prefix, @Nullable AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
    
        //....
        Class<? extends View> clazz = null;
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
    
            if (constructor == null) {
                //重点❤ 反射获取View
                clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
                        mContext.getClassLoader()).asSubclass(View.class);
    
                if (mFilter != null && clazz != null) {
                    boolean allowed = mFilter.onLoadClass(clazz);
                    if (!allowed) {
                        failNotAllowed(name, prefix, viewContext, attrs);
                    }
                }
                //重点❤ 对应的构造函数
                constructor = clazz.getConstructor(mConstructorSignature);
                constructor.setAccessible(true);
                sConstructorMap.put(name, constructor);
            }
    
            Object lastContext = mConstructorArgs[0];
            mConstructorArgs[0] = viewContext;
            Object[] args = mConstructorArgs;
            args[1] = attrs;
    
            try {
                //重点❤  创建具体得view
                final View view = constructor.newInstance(args);
                if (view instanceof ViewStub) {
                    //特殊的viewWtub处理
                    final ViewStub viewStub = (ViewStub) view;
                    viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
                }
                return view;
            } finally {
                mConstructorArgs[0] = lastContext;
            }
        } 
        //....
    }
    

    rInflate

    void rInflate(XmlPullParser parser, View parent, Context context,
            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
    
        final int depth = parser.getDepth();
        int type;
        boolean pendingRequestFocus = false;
    
        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
    
            if (type != XmlPullParser.START_TAG) {
                continue;
            }
    
            final String name = parser.getName();
    
            if (TAG_REQUEST_FOCUS.equals(name)) {
                pendingRequestFocus = true;
                consumeChildElements(parser);
            } else if (TAG_TAG.equals(name)) {
                parseViewTag(parser, parent, attrs);
            } else if (TAG_INCLUDE.equals(name)) {
                if (parser.getDepth() == 0) {
                    throw new InflateException("<include /> cannot be the root element");
                }
                parseInclude(parser, context, parent, attrs);
            } else if (TAG_MERGE.equals(name)) {
                throw new InflateException("<merge /> must be the root element");
            } else {
                //重点❤, createViewFromTag 创建对应的View  上面有分析
                final View view = createViewFromTag(parent, name, context, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflateChildren(parser, view, attrs, true);
                viewGroup.addView(view, params);
            }
        }
    
        if (pendingRequestFocus) {
            parent.restoreDefaultFocus();
        }
    
        if (finishInflate) {
            parent.onFinishInflate();
        }
    }
    

    inflate参数

    View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)

    • resource:需要加载布局的文件id,将这个布局文件加载出UI会话中来
    • root:需要附加到resource资源文件的根控件,inflate会返回一个view对象,会根据第三个参数attachToRoot 影响
      • attachToRoot=True,就是将root作为根对象返回
      • attachToRoot=False,仅仅将这个root对象的LayoutParams属性返回到resource对象的根布局上,相当于没有根
    • attachToRoot:是否将root附加到布局文件的根视图上
    LayoutInflate的参数的作用
        
    // 方式一:将布局添加成功
    View view = inflater.inflate(R.layout.inflate_layout, layoutRoot, true);
    
    // 方式二:报错,一个View只能有一个父亲(The specified child already has a parent.)
    View view = inflater.inflate(R.layout.inflate_layout, layoutRoot, true); // 已经addView
    ll.addView(view);
    
    // 方式三:布局成功,第三个参数为false
    // 目的:想要 inflate_layout 的根节点的属性(宽高)有效,又不想让其处于某一个容器中
    View view = inflater.inflate(R.layout.inflate_layout, layoutRoot false);
    ll.addView(view);
    
    // 方式四:root = null,这个时候不管第三个参数是什么,显示效果一样
    // inflate_layout 的根节点的属性(宽高)设置无效,只是包裹子View,
    // 但是子View(Button)有效,因为Button是出于容器下的
    View view = inflater.inflate(R.layout.inflate_layout, null, false);
    ll.addView(view);
    

    merge

    • 优化布局
    • 必须作为rootView

    include

    • Id的特殊性,引用布局设置了Id,内部的rootView便无效,反之!

    • 不能作为rootView根根元素,需要放在ViewGroup

    ViewStub

    • include特性基础一致
    • 其作用隐藏,懒加载

    相关文章

      网友评论

          本文标题:Android中setContentView()源码流程解析

          本文链接:https://www.haomeiwen.com/subject/heufzrtx.html