Activity 和 AppCompatActivity 的区别
Activity的实例化
//ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
//在这里进行activity的实例化,但对于对于初始化ViewTree却不是在这里
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo),
appContext.getAttributionSource());
if (r.state != null) {
r.state.setClassLoader(cl);
}
}
......
......
appContext.getResources().addLoaders(
app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
appContext.setOuterContext(activity);
//在这里对ViewTree 进行了初始化
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);
......
......
}
ActivityThread.performLaunchActivity
-->activity.attach
--> new PhoneWindow()
-->mInstrumentation.callActivityOnCreate
setContentView 的 流程
继承 Activity 的流程
PhoneWindow.setContentView --- 主要目的 创建 DecorView 拿到 Content
--》 installDecor(); // 创建 DecorView 拿到 mContentParent
--》 mDecor = generateDecor(-1);
--》 new DecorView()
--》 mContentParent = generateLayout(mDecor);
--》 R.layout.screen_simple --》 @android:id/content --》 mContentParent
// R.layout.screen_simple --》 添加到 DecorView(FrameLayout)
--》 mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
--》 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
--》 mLayoutInflater.inflate(layoutResID, mContentParent); // R.layout.activity_main 渲染到 mContentParent
PhoneWindow ---》分为3类,哪些地方会创建
1.Activity
2.Dialog
3.PopupWindow
4.Toast
继承 AppCompatActivity 的流程
AppCompatDelegate.setContentView
--> ensureSubDecor();
--> mSubDecor = createSubDecor();
--> ensureWindow(); // 从Activity 那PhoneWindow 请问Phonewindow是什么时候第一次创建?
--> mWindow.getDecorView();
--> installDecor(); // mContentParent
······ //剩下的参考Activity的流程
// R.layout.screen_simple 里面的 content ---》 把 content 的 View 复制到 R.id.action_bar_activity_content
--> final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
--> windowContentView.setId(View.NO_ID); // 将原始的 content id 置为 NO_ID
--> contentView.setId(android.R.id.content); // subDecerView R.id.action_bar_activity_content --> 置为 content
--> mWindow.setContentView(subDecor); //
--> ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
如何将xml文件加载到视图里面
// R.layout.activity_main View 创建
--> LayoutInflater.from(mContext).inflate(resId, contentParent);
// 通过反射创建View --- 布局的rootView
--> final View temp = createViewFromTag(root, name, inflaterContext, attrs);
// 是否是sdk
--》if (-1 == name.indexOf('.')) { // LinearLayout
view = onCreateView(context, parent, name, attrs);
--》 PhoneLayoutInflater.onCreateView(name, attrs);
--> View view = createView(name, prefix, attrs);
} else { // name = androidx.constraintlayout.widget.ConstraintLayout
view = createView(context, name, null, attrs);
// 通过反射创建 View 对象
--> clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
mContext.getClassLoader()).asSubclass(View.class);
--> constructor = clazz.getConstructor(mConstructorSignature);
--> final View view = constructor.newInstance(args);
}
--> rInflateChildren(parser, temp, attrs, true); // 创建子View
--> rInflate
--> View view = createViewFromTag(parent, name, context, attrs);
private static final String[] sClassPrefixList = {
"android.widget.",
"android.webkit.",
"android.app.",
"android.view."
};
关于inflate四个参数的意义
LayoutInflate的参数的作用
// 方式一:将布局添加成功
View view = inflater.inflate(R.layout.inflate_layout, ll, true);
// 方式二:报错,一个View只能有一个父亲(The specified child already has a parent.)
View view = inflater.inflate(R.layout.inflate_layout, ll, true); // 已经addView
// if (root != null && attachToRoot) {
// root.addView(temp, params);
// }
ll.addView(view);
// 方式三:布局成功,第三个参数为false
// 目的:想要 inflate_layout 的根节点的属性(宽高)有效,又不想让其处于某一个容器中
// if (!attachToRoot) {
// // Set the layout params for temp if we are not
// // attaching. (If we are, we use addView, below)
// temp.setLayoutParams(params);
// }
View view = inflater.inflate(R.layout.inflate_layout, ll, false);
ll.addView(view);
// 方式四:root = null,这个时候不管第三个参数是什么,显示效果一样
// inflate_layout 的根节点的属性(宽高)设置无效,只是包裹子View,
// if (root == null || !attachToRoot) {
// result = temp;
// }
// 但是子View(Button)有效,因为Button是出于容器下的
View view = inflater.inflate(R.layout.inflate_layout, null, false);
ll.addView(view);
View 没有父容器 ---》 尺寸大小 参数的设置无效 View的绘制流程
//在这个过程我们会发现一些标签, 区别如下
merge
- 优化布局
- 必须作为rootView
include
- id 要注意
- 不能作为 root_View
ViewStub 标签
- 跟include 差不多 --> 隐藏作用,懒加载
插件化换肤 ---> setConteView的流程
面试题
1.为什么requestWindowFeature()要在setContentView()之前调用
requestWindowFeature 实际调用的是 PhoneWindow.requestFeature,
在这个方法里面会判断如果变量 mContentParentExplicitlySet 为true则报错,
而这个变量会在 PhoneWindow.setContentView
调用的时候设置为true。
- 为什么这么设计呢?
DecorView的xml布局是通过设置的窗口特征进行选择的。 - 为什么 requestWindowFeature(Window.FEATURE_NO_TITLE);设置无效?
需要用 supportRequestWindowFeature(Window.FEATURE_NO_TITLE);,因为继承的是AppCompatActivity,这个类里面会覆盖设置。
2.LayoutInflate几个参数的作用?
LayoutInflater inflater = LayoutInflater.from(this);
// 方式一:布局添加成功,里面执行了 ll.addView(view)
View view = inflater.inflate(R.layout.inflate_layout, ll, true);
// 方式二:报错,一个View只能有一个父亲(The specified child already has a parent.)
//
View view = inflater.inflate(R.layout.inflate_layout, ll, true);
ll.addView(view);
// 方式三:布局成功,第三个参数为false
// 目的:想要 inflate_layout 的根节点的属性(宽高)有效 --> 获取根节点的宽高,又不想让其处于某一个容器中
View view = inflater.inflate(R.layout.inflate_layout, ll, 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);
3.描述下merge、include、ViewStub标签的特点
include:
- 不能作为根元素,需要放在 ViewGroup中
- findViewById查找不到目标控件,这个问题出现的前提是在使用include时设置了id,而在findViewById时却用了被include进来的布局的根元素id。
- 为什么会报空指针呢?
如果使用include标签时设置了id,这个id就会覆盖 layout根view中设置的id,从而找不到这个id
代码:LayoutInflate.parseInclude
--》final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
--》if (id != View.NO_ID) {
view.setId(id);
}
- 为什么会报空指针呢?
merge:
- merge标签必须使用在根布局
- 因为merge标签并不是View,所以在通过LayoutInflate.inflate()方法渲染的时候,第二个参数必须指定一个父容器,
且第三个参数必须为true,也就是必须为merge下的视图指定一个父亲节点. - 由于merge不是View所以对merge标签设置的所有属性都是无效的.
ViewStub:就是一个宽高都为0的一个View,它默认是不可见的
- 类似include,但是一个不可见的View类,用于在运行时按需懒加载资源,只有在代码中调用了viewStub.inflate()
或者viewStub.setVisible(View.visible)方法时才内容才变得可见。 - 这里需要注意的一点是,当ViewStub被inflate到parent时,ViewStub就被remove掉了,即当前view hierarchy中不再存在ViewStub,
而是使用对应的layout视图代替。
ViewStub.java
public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
// 获取 InflatedId
mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
// 设置不显示
setVisibility(GONE);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 设置宽高为0
setMeasuredDimension(0, 0);
}
public View inflate() {
final ViewParent viewParent = getParent();
if (viewParent != null && viewParent instanceof ViewGroup) {
if (mLayoutResource != 0) {
final ViewGroup parent = (ViewGroup) viewParent;
// 设置ID
final View view = inflateViewNoAdd(parent);
// 替换自己和View
replaceSelfWithView(view, parent);
mInflatedViewRef = new WeakReference<>(view);
if (mInflateListener != null) {
mInflateListener.onInflate(this, view);
}
return view;
} else {
throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
}
} else {
throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
}
}
private View inflateViewNoAdd(ViewGroup parent) {
if (mInflatedId != NO_ID) {
view.setId(mInflatedId);
}
}
private void replaceSelfWithView(View view, ViewGroup parent) {
final int index = parent.indexOfChild(this);
// 移除自己
parent.removeViewInLayout(this);
final ViewGroup.LayoutParams layoutParams = getLayoutParams();
// 添加View
if (layoutParams != null) {
parent.addView(view, index, layoutParams);
} else {
parent.addView(view, index);
}
}