Android setContentView流程[通俗易懂]

Android setContentView流程[通俗易懂]MainActivity继承Activity的流程MainActivity继承至Activityimportandroid.app.Activity;importandroid.os.Bundle;publicclassMainActivityextendsActivity{@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(saved

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

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

文章目录

MainActivity 继承Activity的setContentView流程

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1) MainActivity 继承至 Activity

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

/** * 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

@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

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

protected DecorView generateDecor(int featureId) { 
   
    ......
    // 初始化DecorView
    return new DecorView(context, featureId, this, getAttributes());
}

1.1.1.1.2) PhoneWindow#generateLayout

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

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

<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

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

渲染操作

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

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

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

@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方法

@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

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

@NonNull
protected AppCompatTextView createTextView(Context context, 
AttributeSet attrs) { 

return new AppCompatTextView(context, attrs);
}

1.1.1.2.1.2) LayoutInflater#onCreateView

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

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

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

// 通过反射创建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之后调用,会报错,必须在之前调用才可以?

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,就会报错!

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

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

@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

@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

@Override
public void setContentView(@LayoutRes int layoutResID) { 

initViewTreeOwners();
getDelegate().setContentView(layoutResID);
}

1.2.1) AppCompatDelegateImpl#setContentView

@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

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

public final @NonNull View getDecorView() { 

if (mDecor == null || mForceDecorInstall) { 

// 同MainActivity继承Activity的逻辑,看那个就行
installDecor();
}
return mDecor;
}

1.2.1.1.2) abc_screen_simple.xml

<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

<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

渲染操作

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

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

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

@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方法

@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

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

@NonNull
protected AppCompatTextView createTextView(Context context, 
AttributeSet attrs) { 

return new AppCompatTextView(context, attrs);
}

1.2.1.2.1.2) LayoutInflater#onCreateView

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

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

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

// 通过反射创建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类里面会覆盖设置。

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这个:

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,标志位改变了

@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

<?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

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) 打印结果

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,属性没有获取

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

<?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

<?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

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);
}
}

正常的:
在这里插入图片描述
不带有布局参数的:
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • plsql编程语言_编程语言有哪些

    plsql编程语言_编程语言有哪些–pl/sql编程语言–pl/sql编程语言是对sql语言的扩展,是的sql语言具有过程化编程的特性–pl/sql编程语言比一般的过程化编程语言,更加灵活高效–pl/sql编程语言主要用来编写存储过程和存储函数等。–声明方法,定义变量–赋值操作可以用:=也可以使用into查询语句赋值declareinumber(2):=10;–数值型变量…

  • 完全二叉树和二叉树性质「建议收藏」

    完全二叉树和二叉树性质「建议收藏」一.完全二叉树特点:1.叶子节点只能出现在最下面2层2.层序遍历时连续的二.二叉树性质第i层,最多有2的(i-1)次方个节点深度为k,最多有2的k次方-1个结点叶子节点为n0,度为2的结点为n2,则n0=n2+1n个节点的完全二叉树,深度为log[(2,n)+1]取下地板n个节点的完全二叉树,按层序编号,任一结点ia.i=1,则结点为根,若i&…

  • 为什么使用框架

    为什么使用框架

  • 小程序的后台数据 前端加载时页面展示

    小程序的后台数据 前端加载时页面展示

  • vmware不可恢复的vcpu-0_vmware与device不兼容

    vmware不可恢复的vcpu-0_vmware与device不兼容VMwareWorkstation不可恢复错误:(vcpu-0)解决方法我的虚拟机解决方案,真实实用你可以使用下面的解决办法:1、在安装的虚拟机处右键-&amp;amp;gt;设置&amp;amp;gt;处理器&amp;amp;gt;虚拟化引擎&amp;amp;gt;首选模式,选择“IntelVT-xorAMD-V&amp;quot;;2、在运行虚拟机后提示:此主机支持IntelVT-x,但被禁用了!重启电脑进入BIO

  • 数据挖掘选择题_数据挖掘算法例题

    数据挖掘选择题_数据挖掘算法例题目录一、填空题二、计算题一、填空题❃随着信息技术的高速发展,数据库应用的规模、范围和深度不断扩大,网络环境成为主流等等。产生“数据丰富而信息贫乏”现象。❃“数据丰富而信息贫乏”现象导致大数据概念。❃数据(Data)、信息(Information)和知识(Knowledge)是广义数据表现的不同形式。❃大数据时代的数据挖掘技术需求分析的流派:数据论、方法论、环境论、特征论…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号