前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android setContentView流程[通俗易懂]

Android setContentView流程[通俗易懂]

作者头像
全栈程序员站长
发布2022-09-13 11:21:41
7230
发布2022-09-13 11:21:41
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

更新于:2022-01-09 Android-30 implementation ‘androidx.appcompat:appcompat:1.2.0’ setContentView并没有将view添加到屏幕上,只是创建了DecorView,xml添加到DecorView而已。

文章目录

MainActivity 继承Activity的setContentView流程

1) MainActivity 继承至 Activity

代码语言:javascript
复制
import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity { 
   
    @Override
    protected void onCreate(Bundle savedInstanceState) { 
   
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

1.1) Activity#setContentView

代码语言:javascript
复制
/** * Set the activity content from a layout resource. The resource will be * inflated, adding all top-level views to the activity. * * @param layoutResID Resource ID to be inflated. * * @see #setContentView(android.view.View) * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) */
public void setContentView(@LayoutRes int layoutResID) { 
   
    // getWindow() 是 抽象类Window对象,其子类为 PhoneWindow
    // 这里调用的是 PhoneWindow的setContentView
    getWindow().setContentView(layoutResID);
    
    initWindowDecorActionBar();
}

1.1.1) PhoneWindow#setContentView

代码语言:javascript
复制
@Override
public void setContentView(int layoutResID) { 
   
    // 初始化DecorView,并对 mContentParent 赋值 mContentParent就是一个View
    // mContentParent 是 R.layout.screen_simple资源文件 中 FramLayout 这个View
    // 后面会提到这里
    if (mContentParent == null) { 
   
        installDecor();
    } 
    ......
    // 将 layoutResID 即 R.layout.activity_main 填入到 mContentParent,渲染操作
    mLayoutInflater.inflate(layoutResID, mContentParent);
    ......
    // 设置标志位
    // 如:当调用requsetWindowFeature时,如果在setContentView之后调用
    // 就会抛出异常,因为这里设置了值
    mContentParentExplicitlySet = true;
}

1.1.1.1) PhoneWindow#installDecor

代码语言:javascript
复制
private void installDecor() { 
   
    ......
    // This is the top-level view of the window, containing the window decor.
    // mDecor 是一个 DecorView 即顶层的 View,它包含 window
    mDecor = generateDecor(-1);
	
    ......
    // This is the view in which the window contents are placed.It is either
    // mDecor itself, or a child of mDecor where the contents go.
    // mContentParent 是 ViewGroup 即 它是用来放置view的
    // 要么是mDecor本身,要么是mDecor的子代
    mContentParent = generateLayout(mDecor);
    ......
}

1.1.1.1.1) PhoneWindow#generateDecor

代码语言:javascript
复制
protected DecorView generateDecor(int featureId) { 
   
    ......
    // 初始化DecorView
    return new DecorView(context, featureId, this, getAttributes());
}

1.1.1.1.2) PhoneWindow#generateLayout

初始化 mContentParent 根据不同的主题选择不同的资源文件。

代码语言:javascript
复制
protected ViewGroup generateLayout(DecorView decor) { 
   
    ......
    // 根据不同的主题,选择不同的资源文件
    // 这里以 R.layout.screen_simple 文件为例说明
    layoutResource = R.layout.screen_simple;
	
    ......
    // 将 R.layout.screen_simple 添加到 DecorView 即顶层的 View
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
	
    // 获取 id 为 ID_ANDROID_CONTENT 即 com.android.internal.R.id.content
    // contentParent 就是 R.layout.screen_simple资源文件 中 FramLayout 这个View
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    ......
	
    return contentParent;
}

1.1.1.1.2.1) screen_simple.xml

代码语言:javascript
复制
<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>

1.1.1.1.2.2) DecorView#onResourcesLoaded

代码语言:javascript
复制
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) { 
   
    ......
    // 加载资源文件 如上方提到的 R.layout.screen_simple
    final View root = inflater.inflate(layoutResource, null);
        
    ......
    // 将 资源文件 加载到 DecorView 中
    addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    ......
}

1.1.1.2) LayoutInflater#inflate

渲染操作

代码语言:javascript
复制
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) { 
   
    return inflate(resource, root, root != null);
}

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, 
    boolean attachToRoot) { 
   
	
    ......
    return inflate(parser, root, attachToRoot);
    ......
}

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, 
	boolean attachToRoot) { 
   
	
    ......
    // 通过反射创建View
    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
    ......
    ViewGroup.LayoutParams params = null;
    ......
    params = root.generateLayoutParams(attrs);
    ......
    // 创建子View
    rInflateChildren(parser, temp, attrs, true);
    ......
    root.addView(temp, params);
    ......
}

1.1.1.2.1) LayoutInflater#createViewFromTag

代码语言:javascript
复制
private View createViewFromTag(View parent, String name, Context context, 
	AttributeSet attrs) { 
   
    return createViewFromTag(parent, name, context, attrs, false);
}

View createViewFromTag(View parent, String name, Context context, 
	AttributeSet attrs, boolean ignoreThemeAttr) { 
   
    ......
    // 创建view
    View view = tryCreateView(parent, name, context, attrs);
    
    // 如果 view 为null,使用默认的创建 view 的方式创建
    // 也就是通过 AppCompatDelegateImpl 来创建 View
    if (view == null) { 
   
        final Object lastContext = mConstructorArgs[0];
        mConstructorArgs[0] = context;
        try { 
   
        	// 判断是否为sdk自己的
            if (-1 == name.indexOf('.')) { 
   
                view = onCreateView(context, parent, name, attrs);
            } else { 
   
                view = createView(context, name, null, attrs);
            }
        } finally { 
   
            mConstructorArgs[0] = lastContext;
        }
    }

    return view;
}

1.1.1.2.1.1) LayoutInflater#tryCreateView

创建View,当 mFactory2 不为空,就用 factory2 来创建view,否则就返回 view为null

当继承Activity时,并没有默认设置 factory2,当继承 AppCompatActivity 时,在 super.onCreate(savedInstanceState) 进行了设置 factory2

代码语言:javascript
复制
public final View tryCreateView(@Nullable View parent, @NonNull String name,
    @NonNull Context context,
    @NonNull AttributeSet attrs) { 
   
    if (name.equals(TAG_1995)) { 
   
        // Let's party like it's 1995!
        return new BlinkLayout(context, attrs);
    }

    View view;
    if (mFactory2 != null) { 
   
        view = mFactory2.onCreateView(parent, name, context, attrs);
    } else if (mFactory != null) { 
   
        view = mFactory.onCreateView(name, context, attrs);
    } else { 
   
        view = null;
    }

    if (view == null && mPrivateFactory != null) { 
   
        view = mPrivateFactory.onCreateView(parent, name, context, attrs);
    }

    return view;
}

1.1.1.2.1.1.1) AppCompatDelegateImpl#onCreateView

代码语言:javascript
复制
@Override
public final View onCreateView(View parent, String name, 
	Context context, AttributeSet attrs) { 
   
    return createView(parent, name, context, attrs);
}

1.1.1.2.1.1.1.1) AppCompatDelegateImpl#createView

从代码看,是调用 AppCompatViewInflater的createView方法

代码语言:javascript
复制
@Override
public View createView(View parent, final String name, 
	@NonNull Context context, @NonNull AttributeSet attrs) { 
   
    ......
    mAppCompatViewInflater = new AppCompatViewInflater();
    ......
    return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
            IS_PRE_LOLLIPOP,   
            true,              
            VectorEnabledTintResources.shouldBeUsed() 
    );
}

1.1.1.2.1.1.1.1.1) AppCompatViewInflater#createView

从下方代码可以看出,当创建的TextView等时,会进行替换操作,如TextView替换为AppCompatTextView

代码语言:javascript
复制
final View createView(View parent, final String name, @NonNull Context context, 
    @NonNull AttributeSet attrs, boolean inheritContext, 
    boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) { 
   
    ......
    
    View view = null;

    switch (name) { 
   
        case "TextView":
            view = createTextView(context, attrs);
            verifyNotNull(view, name);
            break;
        case "ImageView":
            view = createImageView(context, attrs);
            verifyNotNull(view, name);
            break;
        case "Button":
            view = createButton(context, attrs);
            verifyNotNull(view, name);
            break;
        case "EditText":
            view = createEditText(context, attrs);
            verifyNotNull(view, name);
            break;
        case "Spinner":
            view = createSpinner(context, attrs);
            verifyNotNull(view, name);
            break;
        case "ImageButton":
            view = createImageButton(context, attrs);
            verifyNotNull(view, name);
            break;
        case "CheckBox":
            view = createCheckBox(context, attrs);
            verifyNotNull(view, name);
            break;
        case "RadioButton":
            view = createRadioButton(context, attrs);
            verifyNotNull(view, name);
            break;
        case "CheckedTextView":
            view = createCheckedTextView(context, attrs);
            verifyNotNull(view, name);
            break;
        case "AutoCompleteTextView":
            view = createAutoCompleteTextView(context, attrs);
            verifyNotNull(view, name);
            break;
        case "MultiAutoCompleteTextView":
            view = createMultiAutoCompleteTextView(context, attrs);
            verifyNotNull(view, name);
            break;
        case "RatingBar":
            view = createRatingBar(context, attrs);
            verifyNotNull(view, name);
            break;
        case "SeekBar":
            view = createSeekBar(context, attrs);
            verifyNotNull(view, name);
            break;
        case "ToggleButton":
            view = createToggleButton(context, attrs);
            verifyNotNull(view, name);
            break;
        default:
            view = createView(context, name, attrs);
    }
    
    ......
    
    return view;
}

当为 TextView 的时候,就会被替换为 AppCompatTextView

代码语言:javascript
复制
@NonNull
protected AppCompatTextView createTextView(Context context, 
	AttributeSet attrs) { 
   
    return new AppCompatTextView(context, attrs);
}

1.1.1.2.1.2) LayoutInflater#onCreateView

代码语言:javascript
复制
public View onCreateView(@NonNull Context viewContext, @Nullable View parent,    
	@NonNull String name, @Nullable AttributeSet attrs) 
	throws ClassNotFoundException { 
   
	
    return onCreateView(parent, name, attrs);
}

protected View onCreateView(View parent, String name, AttributeSet attrs)
	throws ClassNotFoundException { 
   
    // 这里是两个参数的,调用的是 onCreateView 的 onCreateView,进行重写了
    return onCreateView(name, attrs);
}

1.1.1.2.1.2.1) PhoneLayoutInflater#onCreateView

代码语言:javascript
复制
private static final String[] sClassPrefixList = { 
   
    "android.widget.",
    "android.webkit.",
    "android.app."
};

@Override protected View onCreateView(String name, AttributeSet attrs) 
	throws ClassNotFoundException { 
   
	
    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.
        }
    }

    return super.onCreateView(name, attrs);
}

1.1.1.2.1.2.1.1) LayoutInflater#onCreateView

代码语言:javascript
复制
public final View createView(String name, String prefix, AttributeSet attrs)
	throws ClassNotFoundException, InflateException { 
   
    Context context = (Context) mConstructorArgs[0];
    if (context == null) { 
   
        context = mContext;
    }
    return createView(context, name, prefix, attrs);
}

1.1.1.2.1.2.1.1.1) LayoutInflater#createView

代码语言:javascript
复制
// 通过反射创建View
public final View createView(@NonNull Context viewContext, @NonNull String name, 
	@Nullable String prefix, @Nullable AttributeSet attrs) 
	throws ClassNotFoundException, InflateException { 
   

	clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
               mContext.getClassLoader()).asSubclass(View.class);

    constructor = clazz.getConstructor(mConstructorSignature);
    
    final View view = constructor.newInstance(args);
}

2) 为什么在 requestWindowFeature 在 setContentView之后调用,会报错,必须在之前调用才可以?

代码语言:javascript
复制
public class MainActivity extends Activity { 
   
    @Override
    protected void onCreate(Bundle savedInstanceState) { 
   
        super.onCreate(savedInstanceState);
// 在 setContentView 之前调用
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        
        setContentView(R.layout.activity_main);
        
// error
// requestWindowFeature(Window.FEATURE_NO_TITLE);
    }
}

报错内容:

查看requestWindowFeature的代码逻辑:

调用的是PhoneWindow的requestFeature方法,里面会判断mContentParentExplicitlySet是否已经设置过值了,如果为true,就会报错!

代码语言:javascript
复制
public boolean requestFeature(int featureId) { 
   
    if (mContentParentExplicitlySet) { 
   
        throw new AndroidRuntimeException(
        	"requestFeature() must be called before adding content");
    }
    ......
}

其实,在PhoneWindow#setContentView方法的最后一行,会设置 mContentParentExplicitlySet 为ture,所以在之后调用,就会报错了。

MainActivity 继承AppCompatActivity的setContentView流程

1) MainActivity 继承至 AppCompatActivity

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity { 
   
    @Override
    protected void onCreate(Bundle savedInstanceState) { 
   
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

1.1) AppCompatActivity#onCreate

在onCreate中,会设置factory,去看 installViewFactory

代码语言:javascript
复制
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) { 
   
    final AppCompatDelegate delegate = getDelegate();
    delegate.installViewFactory();
    delegate.onCreate(savedInstanceState);
    super.onCreate(savedInstanceState);
}

1.1.1) AppCompatDelegateImpl#installViewFactory

进行设置 Factory2

如:当创建 TextView 的时候,会因为这里设置了 Factory2,而把 TextView 替换为 AppCompatTextView

代码语言:javascript
复制
@Override
public void installViewFactory() { 
   
    LayoutInflater layoutInflater = LayoutInflater.from(mContext);
    if (layoutInflater.getFactory() == null) { 
   
        LayoutInflaterCompat.setFactory2(layoutInflater, this);
    } else { 
   
        if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImpl)) { 
   
            Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
                    + " so we can not install AppCompat's");
        }
    }
}

1.2) AppCompatActivity#setContentView

调用的是AppCompatDelegateImpl实现类中的 setContentView

代码语言:javascript
复制
@Override
public void setContentView(@LayoutRes int layoutResID) { 
   
    initViewTreeOwners();
    getDelegate().setContentView(layoutResID);
}

1.2.1) AppCompatDelegateImpl#setContentView

代码语言:javascript
复制
@Override
public void setContentView(int resId) { 
   
    // 确保ActionBar的特有UI结构构建完毕
    ensureSubDecor();
    ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
    // 确保ContentView的所有Child全部被移除干净
    contentParent.removeAllViews();
    // 将画面的内容布局解析并添加到ContentView下
    LayoutInflater.from(mContext).inflate(resId, contentParent);
    mAppCompatWindowCallback.getWrapped().onContentChanged();
}

1.2.1.1) AppCompatDelegateImpl#ensureSubDecor

代码语言:javascript
复制
private ViewGroup createSubDecor() { 
   
    .....
	
    mWindow.getDecorView();  // 同MainActivity继承Activity的逻辑,看那个就行
	
    ......
	
    subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
	
    final ContentFrameLayout contentView = (ContentFrameLayout) 
    	subDecor.findViewById(R.id.action_bar_activity_content);
	
	final ViewGroup windowContentView = (ViewGroup) 
		mWindow.findViewById(android.R.id.content);
	
	......

    if (windowContentView != null) { 
   
       // There might be Views already added to the Window's content view 
       // so we need to
       // migrate them to our content view
       while (windowContentView.getChildCount() > 0) { 
   
           final View child = windowContentView.getChildAt(0);
           windowContentView.removeViewAt(0);
           contentView.addView(child);
       }

       // Change our content FrameLayout to use the android.R.id.content id.
       // Useful for fragments.
       // 把 android.R.id.content 这里,改为 NO_ID
       windowContentView.setId(View.NO_ID);  
       
       // R.id.action_bar_activity_content 改为 android.R.id.content
       // 就是做了下替换操作
       contentView.setId(android.R.id.content); 

       // The decorContent may have a foreground drawable 
       // set (windowContentOverlay).
       // Remove this as we handle it ourselves
       if (windowContentView instanceof FrameLayout) { 
   
           ((FrameLayout) windowContentView).setForeground(null);
       }
    }
   
    // Now set the Window's content view with the decor
    mWindow.setContentView(subDecor);
    ......
    return subDecor;
}

1.2.1.1.1) PhoneWindow#getDecorView

代码语言:javascript
复制
public final @NonNull View getDecorView() { 
   
    if (mDecor == null || mForceDecorInstall) { 
   
        // 同MainActivity继承Activity的逻辑,看那个就行
        installDecor();
    }
    return mDecor;
}

1.2.1.1.2) abc_screen_simple.xml

代码语言:javascript
复制
<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>

1.2.1.1.3) abc_screen_content_include.xml

代码语言:javascript
复制
<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>

1.2.1.2) LayoutInflater#inflate

渲染操作

代码语言:javascript
复制
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) { 
   
    return inflate(resource, root, root != null);
}

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, 
	boolean attachToRoot) { 
   
    ......
    return inflate(parser, root, attachToRoot);
    ......
}

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, 
	boolean attachToRoot) { 
   
    ......
    // 通过反射创建View
    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
    ......
    ViewGroup.LayoutParams params = null;
    ......
    params = root.generateLayoutParams(attrs);
    ......
    // 创建子View
    rInflateChildren(parser, temp, attrs, true);
    ......
    root.addView(temp, params);
    ......
}

1.2.1.2.1) LayoutInflater#createViewFromTag

代码语言:javascript
复制
private View createViewFromTag(View parent, String name, Context context, 
	AttributeSet attrs) { 
   
    return createViewFromTag(parent, name, context, attrs, false);
}

View createViewFromTag(View parent, String name, Context context, 
	AttributeSet attrs, boolean ignoreThemeAttr) { 
   
    ......
    
    // 创建view
    View view = tryCreateView(parent, name, context, attrs);
    
    // 如果 view 为null,使用默认的创建 view 的方式创建
    // 也就是通过 AppCompatDelegateImpl 来创建 View
    if (view == null) { 
   
        final Object lastContext = mConstructorArgs[0];
        mConstructorArgs[0] = context;
        try { 
   
        	// 判断是否为sdk自己的 
            if (-1 == name.indexOf('.')) { 
   
                // sdk的
                view = onCreateView(context, parent, name, attrs);
            } else { 
   
                // 自己的,或者封装的
                view = createView(context, name, null, attrs);
            }
        } finally { 
   
            mConstructorArgs[0] = lastContext;
        }
    }

    return view;
}

1.2.1.2.1.1) LayoutInflater#tryCreateView

创建View,当 mFactory2 不为空,就用 factory2 来创建view,否则就返回 view为null

代码语言:javascript
复制
public final View tryCreateView(@Nullable View parent, @NonNull String name,
    @NonNull Context context,
    @NonNull AttributeSet attrs) { 
   
    if (name.equals(TAG_1995)) { 
   
        // Let's party like it's 1995!
        return new BlinkLayout(context, attrs);
    }

    View view;
    if (mFactory2 != null) { 
   
        view = mFactory2.onCreateView(parent, name, context, attrs);
    } else if (mFactory != null) { 
   
        view = mFactory.onCreateView(name, context, attrs);
    } else { 
   
        view = null;
    }

    if (view == null && mPrivateFactory != null) { 
   
        view = mPrivateFactory.onCreateView(parent, name, context, attrs);
    }

    return view;
}

1.2.1.2.1.1.1) AppCompatDelegateImpl#onCreateView

代码语言:javascript
复制
@Override
public final View onCreateView(View parent, String name, 
	Context context, AttributeSet attrs) { 
   
    return createView(parent, name, context, attrs);
}

1.2.1.2.1.1.1.1) AppCompatDelegateImpl#createView

从代码看,是调用 AppCompatViewInflater的createView方法

代码语言:javascript
复制
@Override
public View createView(View parent, final String name, 
	@NonNull Context context, @NonNull AttributeSet attrs) { 
   
    ......
    mAppCompatViewInflater = new AppCompatViewInflater();
    ......
    return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
            IS_PRE_LOLLIPOP,   
            true,              
            VectorEnabledTintResources.shouldBeUsed() 
    );
}

1.2.1.2.1.1.1.1.1) AppCompatViewInflater#createView

从下方代码可以看出,当创建的TextView等时,会进行替换操作,如TextView替换为AppCompatTextView

代码语言:javascript
复制
final View createView(View parent, final String name, @NonNull Context context, 
    @NonNull AttributeSet attrs, boolean inheritContext, 
    boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) { 
   
    ......
    
    View view = null;

    switch (name) { 
   
        case "TextView":
            view = createTextView(context, attrs);
            verifyNotNull(view, name);
            break;
        case "ImageView":
            view = createImageView(context, attrs);
            verifyNotNull(view, name);
            break;
        case "Button":
            view = createButton(context, attrs);
            verifyNotNull(view, name);
            break;
        case "EditText":
            view = createEditText(context, attrs);
            verifyNotNull(view, name);
            break;
        case "Spinner":
            view = createSpinner(context, attrs);
            verifyNotNull(view, name);
            break;
        case "ImageButton":
            view = createImageButton(context, attrs);
            verifyNotNull(view, name);
            break;
        case "CheckBox":
            view = createCheckBox(context, attrs);
            verifyNotNull(view, name);
            break;
        case "RadioButton":
            view = createRadioButton(context, attrs);
            verifyNotNull(view, name);
            break;
        case "CheckedTextView":
            view = createCheckedTextView(context, attrs);
            verifyNotNull(view, name);
            break;
        case "AutoCompleteTextView":
            view = createAutoCompleteTextView(context, attrs);
            verifyNotNull(view, name);
            break;
        case "MultiAutoCompleteTextView":
            view = createMultiAutoCompleteTextView(context, attrs);
            verifyNotNull(view, name);
            break;
        case "RatingBar":
            view = createRatingBar(context, attrs);
            verifyNotNull(view, name);
            break;
        case "SeekBar":
            view = createSeekBar(context, attrs);
            verifyNotNull(view, name);
            break;
        case "ToggleButton":
            view = createToggleButton(context, attrs);
            verifyNotNull(view, name);
            break;
        default:
            view = createView(context, name, attrs);
    }
    
    ......
    
    return view;
}

当为 TextView 的时候,就会被替换为 AppCompatTextView

代码语言:javascript
复制
@NonNull
protected AppCompatTextView createTextView(Context context, 
	AttributeSet attrs) { 
   
    return new AppCompatTextView(context, attrs);
}

1.2.1.2.1.2) LayoutInflater#onCreateView

代码语言:javascript
复制
public View onCreateView(@NonNull Context viewContext, @Nullable View parent,
        @NonNull String name, @Nullable AttributeSet attrs)
        throws ClassNotFoundException { 
   
    return onCreateView(parent, name, attrs);
}

protected View onCreateView(View parent, String name, AttributeSet attrs)
        throws ClassNotFoundException { 
   
    // 这里是两个参数的,调用的是 onCreateView 的 onCreateView,进行重写了
    return onCreateView(name, attrs);
}

1.2.1.2.1.2.1) PhoneLayoutInflater#onCreateView

代码语言:javascript
复制
private static final String[] sClassPrefixList = { 
   
    "android.widget.",
    "android.webkit.",
    "android.app."
};

@Override protected View onCreateView(String name, AttributeSet attrs) 
	throws ClassNotFoundException { 
   
    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.
        }
    }

    return super.onCreateView(name, attrs);
}

1.2.1.2.1.2.1.1) LayoutInflater#onCreateView

代码语言:javascript
复制
public final View createView(String name, String prefix, AttributeSet attrs)
        throws ClassNotFoundException, InflateException { 
   
    Context context = (Context) mConstructorArgs[0];
    if (context == null) { 
   
        context = mContext;
    }
    return createView(context, name, prefix, attrs);
}

1.2.1.2.1.2.1.1.1) LayoutInflater#createView

代码语言:javascript
复制
// 通过反射创建View
public final View createView(@NonNull Context viewContext, @NonNull String name,
    @Nullable String prefix, @Nullable AttributeSet attrs)
    throws ClassNotFoundException, InflateException { 
   

	clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
               mContext.getClassLoader()).asSubclass(View.class);

    constructor = clazz.getConstructor(mConstructorSignature);
    
    final View view = constructor.newInstance(args);
}

继承Activity时,设置requestWindowFeature(Window.FEATURE_NO_TITLE)可以生效,当继承AppCompatActivity时,就无效了?

需要使用supportRequestWindowFeature,因为AppCompatActivity类里面会覆盖设置。

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity { 
   
    @Override
    protected void onCreate(Bundle savedInstanceState) { 
   
        super.onCreate(savedInstanceState);

// requestWindowFeature(Window.FEATURE_NO_TITLE);

        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);

        setContentView(R.layout.activity_main);
    }
}

当调用requestWindowFeature,设置的代码如下,其中使用的mLocalFeatures这个:

代码语言:javascript
复制
public boolean requestFeature(int featureId) { 
   
    final int flag = 1<<featureId;
    mFeatures |= flag;
    mLocalFeatures |= mContainer != null ? (flag&~mContainer.mFeatures) : flag;
    return (mFeatures&flag) != 0;
}

当调用supportRequestWindowFeature,设置的代码如下,使用的mWindowNoTitle,标志位改变了

代码语言:javascript
复制
@Override
public boolean requestWindowFeature(int featureId) { 
   
    featureId = sanitizeWindowFeatureId(featureId);

    if (mWindowNoTitle && featureId == FEATURE_SUPPORT_ACTION_BAR) { 
   
        return false; // Ignore. No title dominates.
    }
    if (mHasActionBar && featureId == Window.FEATURE_NO_TITLE) { 
   
        // Remove the action bar feature if we have no title. 
        // No title dominates.
        mHasActionBar = false;
    }

    switch (featureId) { 
   
        case FEATURE_SUPPORT_ACTION_BAR:
            throwFeatureRequestIfSubDecorInstalled();
            mHasActionBar = true;
            return true;
        case FEATURE_SUPPORT_ACTION_BAR_OVERLAY:
            throwFeatureRequestIfSubDecorInstalled();
            mOverlayActionBar = true;
            return true;
        case FEATURE_ACTION_MODE_OVERLAY:
            throwFeatureRequestIfSubDecorInstalled();
            mOverlayActionMode = true;
            return true;
        case Window.FEATURE_PROGRESS:
            throwFeatureRequestIfSubDecorInstalled();
            mFeatureProgress = true;
            return true;
        case Window.FEATURE_INDETERMINATE_PROGRESS:
            throwFeatureRequestIfSubDecorInstalled();
            mFeatureIndeterminateProgress = true;
            return true;
        case Window.FEATURE_NO_TITLE:
            throwFeatureRequestIfSubDecorInstalled();
            mWindowNoTitle = true;
            return true;
    }

    return mWindow.requestFeature(featureId);
}

打印TextView,却输出的不是TextView?

1) activity_main.xml
代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/test_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="hello world"
        />
    
</LinearLayout>
2) MainActivity.java
代码语言:javascript
复制
public class MainActivity extends AppCompatActivity { 
   
    private static final String TAG = "AAAAAAA";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) { 
   
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView textView1 = findViewById(R.id.test_tv);
        Log.d(TAG, "textView1: " + textView1);

        TextView textView2 = new TextView(this);
        Log.d(TAG, "textView2: " + textView2);
    }
}
3) 打印结果
代码语言:javascript
复制
D/AAAAAAA: textView1: com.google.android.material.textview.MaterialTextView{ 
   d43c607 V.ED..... ......ID 0,0-0,0 #7f080195 app:id/test_tv}
D/AAAAAAA: textView2: android.widget.TextView{ 
   c9ba334 V.ED..... ......ID 0,0-0,0}
4) 分析

当继承 AppCompatActivity 时,Activity的 super.onCreate(savedInstanceState) 中会进行默认设置 factory2,然后在执行 LayoutInflater#createViewFromTag 方法时,其中的 tryCreateView 方法会使用到factory2 当factory2不为空时,就会用factory2去创建View,这个view是把TextView替换为了 AppCompatTextView。

LayoutInflater.inflate中参数的作用

LayoutInflater的部分源码:

1)当root不为空,attachToRoot为true,会执行一次addView 2)当root不为空,attachToRoot为false,会获取到属性 3)当root为空,attachToRoot为false,会直接return,属性没有获取

代码语言:javascript
复制
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, 
	boolean attachToRoot) { 
   

	......

    // Temp is the root view that was found in the xml
    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);
        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 against its context.
    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);
    }

    // Decide whether to return the root that was passed in or the
    // top view found in xml.
    if (root == null || !attachToRoot) { 
   
        result = temp;
    }
   
 	......
 	
	return result;
}

效果演示:

inflate_layout.xml
代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:background="@color/teal_200"
    android:gravity="center"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>
activity_main.xml
代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/activity_linear_layout">

</LinearLayout>
MainActivity
代码语言:javascript
复制
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.widget.LinearLayout;

public class MainActivity extends AppCompatActivity { 
   

    @Override
    protected void onCreate(Bundle savedInstanceState) { 
   
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        LinearLayout linear_layout = findViewById(R.id.activity_linear_layout);

        // 正常
        // LayoutInflater.from(this).inflate(R.layout.inflate_layout, 
        // linear_layout, true);


        // 报错
        // 当执行inflate的时候,已经addView了,当再次addView调用,会报错,
        // 一个View只能有一个parent
        // The specified child already has a parent. 
        // You must call removeView() on the child's parent first.
        // View view = LayoutInflater.from(this).inflate(
        // R.layout.inflate_layout, linear_layout, true);
        // linear_layout.addView(view);


        // 正常
        // 第三个参数为false,不会去addView,所以,当调用addView的时候,就没什么问题
        // View view = LayoutInflater.from(this).inflate(
        // R.layout.inflate_layout, linear_layout, false);
        // linear_layout.addView(view);


        // 能显示,但显示不正常,inflate_layout没有父容器了
        // inflate_layout的布局无效,由inflate_layout的内容即button的大小决定
        View view = LayoutInflater.from(this).inflate(
        	R.layout.inflate_layout, null, false);
        linear_layout.addView(view);
    }
}

正常的:

不带有布局参数的:

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/153268.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • MainActivity 继承Activity的setContentView流程
    • 1) MainActivity 继承至 Activity
      • 1.1) Activity#setContentView
        • 1.1.1) PhoneWindow#setContentView
          • 1.1.1.1) PhoneWindow#installDecor
            • 1.1.1.1.1) PhoneWindow#generateDecor
              • 1.1.1.1.2) PhoneWindow#generateLayout
                • 1.1.1.1.2.1) screen_simple.xml
                  • 1.1.1.1.2.2) DecorView#onResourcesLoaded
                    • 1.1.1.2) LayoutInflater#inflate
                      • 1.1.1.2.1) LayoutInflater#createViewFromTag
                        • 1.1.1.2.1.1) LayoutInflater#tryCreateView
                          • 1.1.1.2.1.1.1) AppCompatDelegateImpl#onCreateView
                            • 1.1.1.2.1.1.1.1) AppCompatDelegateImpl#createView
                              • 1.1.1.2.1.1.1.1.1) AppCompatViewInflater#createView
                                • 1.1.1.2.1.2) LayoutInflater#onCreateView
                                  • 1.1.1.2.1.2.1) PhoneLayoutInflater#onCreateView
                                    • 1.1.1.2.1.2.1.1) LayoutInflater#onCreateView
                                      • 1.1.1.2.1.2.1.1.1) LayoutInflater#createView
                                        • 2) 为什么在 requestWindowFeature 在 setContentView之后调用,会报错,必须在之前调用才可以?
                                        • MainActivity 继承AppCompatActivity的setContentView流程
                                          • 1) MainActivity 继承至 AppCompatActivity
                                            • 1.1) AppCompatActivity#onCreate
                                              • 1.1.1) AppCompatDelegateImpl#installViewFactory
                                                • 1.2) AppCompatActivity#setContentView
                                                  • 1.2.1) AppCompatDelegateImpl#setContentView
                                                    • 1.2.1.1) AppCompatDelegateImpl#ensureSubDecor
                                                      • 1.2.1.1.1) PhoneWindow#getDecorView
                                                        • 1.2.1.1.2) abc_screen_simple.xml
                                                          • 1.2.1.1.3) abc_screen_content_include.xml
                                                            • 1.2.1.2) LayoutInflater#inflate
                                                              • 1.2.1.2.1) LayoutInflater#createViewFromTag
                                                                • 1.2.1.2.1.1) LayoutInflater#tryCreateView
                                                                  • 1.2.1.2.1.1.1) AppCompatDelegateImpl#onCreateView
                                                                    • 1.2.1.2.1.1.1.1) AppCompatDelegateImpl#createView
                                                                      • 1.2.1.2.1.1.1.1.1) AppCompatViewInflater#createView
                                                                        • 1.2.1.2.1.2) LayoutInflater#onCreateView
                                                                          • 1.2.1.2.1.2.1) PhoneLayoutInflater#onCreateView
                                                                            • 1.2.1.2.1.2.1.1) LayoutInflater#onCreateView
                                                                              • 1.2.1.2.1.2.1.1.1) LayoutInflater#createView
                                                                                • 继承Activity时,设置requestWindowFeature(Window.FEATURE_NO_TITLE)可以生效,当继承AppCompatActivity时,就无效了?
                                                                                  • 打印TextView,却输出的不是TextView?
                                                                                    • 1) activity_main.xml
                                                                                    • 2) MainActivity.java
                                                                                    • 3) 打印结果
                                                                                    • 4) 分析
                                                                                • LayoutInflater.inflate中参数的作用
                                                                                  • LayoutInflater的部分源码:
                                                                                    • 效果演示:
                                                                                      • inflate_layout.xml
                                                                                      • activity_main.xml
                                                                                      • MainActivity
                                                                                  相关产品与服务
                                                                                  容器服务
                                                                                  腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                                                                                  领券
                                                                                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档