我们使用activity,设置布局的时候会用setContentView(R.layout.activity_home),那到底是怎么加载的呢?
public class HomeActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
}
}
1 首先进入AppCompatActivity---->setcontentview,看下源码
public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider
{
//首先调用的是父类(AppCompatActivity)的setcontentView方法
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);//接着调用的AppCompatDelegate的方法
}
//AppCompatDelegate 此类表示一个委托,您可以用来将AppCompat的支持扩展到任何activity 中
@NonNull
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
}
return mDelegate;
}
}
2 AppCompatDelegate 是一个抽象类,AppCompatDelegateImpl 是抽象类的实例。activity的步骤都在此类中代理操作。
public abstract class AppCompatDelegate {
@NonNull
public static AppCompatDelegate create(@NonNull Activity activity,
@Nullable AppCompatCallback callback) {
return new AppCompatDelegateImpl(activity, callback);//真实 的实例去操作
}
}
3 AppCompatDelegateImpl 的具体步骤
@RestrictTo(LIBRARY)
class AppCompatDelegateImpl extends AppCompatDelegate
implements MenuBuilder.Callback, LayoutInflater.Factory2 {
1//构造方法,传入当前activity 和 当前activity window.callback
AppCompatDelegateImpl(Activity activity, AppCompatCallback callback) {
this(activity, null, callback, activity);
}
2 //
private AppCompatDelegateImpl(Context context, Window window, AppCompatCallback callback,
Object host) {
mContext = context;
mAppCompatCallback = callback;
mHost = host; //当前activity
if (mLocalNightMode == MODE_NIGHT_UNSPECIFIED && mHost instanceof Dialog) {
final AppCompatActivity activity = tryUnwrapContext();
if (activity != null) {
// This code path is used to detect when this Delegate is a child Delegate from
// an Activity, primarily for Dialogs. Dialogs use the Activity as it's Context,
// so we want to make sure that the this 'child' delegate does not interfere
// with the Activity config. The simplest way to do that is to match the
// outer Activity's local night mode
mLocalNightMode = activity.getDelegate().getLocalNightMode();
}
}
if (mLocalNightMode == MODE_NIGHT_UNSPECIFIED) {
// Try and read the current night mode from our static store
final Integer value = sLocalNightModes.get(mHost.getClass());
if (value != null) {
mLocalNightMode = value;
// Finally remove the value
sLocalNightModes.remove(mHost.getClass());
}
}
if (window != null) {
attachToWindow(window);
}
// Preload appcompat-specific handling of drawables that should be handled in a special
// way (for tinting etc). After the following line completes, calls from AppCompatResources
// to ResourceManagerInternal (in appcompat-resources) will handle those internal drawable
// paths correctly without having to go through AppCompatDrawableManager APIs.
AppCompatDrawableManager.preload();
}
3//读取样式,设置window 背景等
private void attachToWindow(@NonNull Window window) {
if (mWindow != null) {
throw new IllegalStateException(
"AppCompat has already installed itself into the Window");
}
final Window.Callback callback = window.getCallback();
if (callback instanceof AppCompatWindowCallback) {
throw new IllegalStateException(
"AppCompat has already installed itself into the Window");
}
mAppCompatWindowCallback = new AppCompatWindowCallback(callback);
// Now install the new callback
window.setCallback(mAppCompatWindowCallback);
final TintTypedArray a = TintTypedArray.obtainStyledAttributes(
mContext, null, sWindowBackgroundStyleable);
final Drawable winBg = a.getDrawableIfKnown(0);
if (winBg != null) {
// Now set the background drawable
window.setBackgroundDrawable(winBg);
}
a.recycle();
mWindow = window;
}
4 //调用setContentView
@Override
public void setContentView(int resId) {
ensureSubDecor(); //确保 phonewindow decordeview 绘制完成
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content); //content 是我们自己绘制view 的父布局
contentParent.removeAllViews(); //先清理view
LayoutInflater.from(mContext).inflate(resId, contentParent);//通过LayoutInflater 解析xml 文件生成view,然后contentParent.addview(读取resId 创建的view);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
4.1//确保创建 主题样式的根布局
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
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);
}
}
applyFixedSizeWindow();
onSubDecorInstalled(mSubDecor);
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);
}
}
}
总结:调用流程:我们创建的activity.setcontentview() --> AppCompatActivity.setcontentview() -->AppCompatDelegateImpl.setcontentview()
如何把xml 文件添加到根视图呢?通过 LayoutInflater.inflate(),在通过XmlResourceParser 解析xml 文件,生成xmlview,然后 contentv view.add(xmlview),一个view 树就创建完了。