Android中setContentView()源码流程解析

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特性基础一致
  • 其作用隐藏,懒加载
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,980评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,422评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,130评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,553评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,408评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,326评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,720评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,373评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,678评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,722评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,486评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,335评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,738评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,283评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,692评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,893评论 2 335

推荐阅读更多精彩内容