3. LayoutInflater 源码阅读
layoutInflater 主要用来inflater 解析layout 布局
// 使用的三种方式:
// ----第一个方式,实际调用的下面的方法2。----
View layoutView = View.inflate(this, R.layout.activity_main, null);
// ----第二个方式,实际调用的第三个方法。----
layoutView = LayoutInflater.from(this).inflate(R.layout.activity_main, null);
// ----第三个方式。----
layoutView = LayoutInflater.from(this).inflate(R.layout.activity_main, null, false);
inflate() 就是调用的factory.inflate(resource, root);
// 第一个方法使用,调用第二个方法。
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
LayoutInflater factory = LayoutInflater.from(context);
return factory.inflate(resource, root); // 调用第二个方法。
}
// 第二种方法,最后调用第三种方法。
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root!=null); // 调用第三种方法。
}
// 所以最终只要看第三个方法即可。
3.1 LayoutInflater.from(this) 实例 怎么获取的。
// LayoutInflater
public static LayoutInflater from(@UiContext Context context) {
// 拿到系统的服务。
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return LayoutInflater;
}
// context -> 实现类ContextImpl.getSystemService()
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
// SystemServiceRegistry
HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS = new HashMap<>();
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); // 从静态map集合中拿 单例的设计模式
return fetcher!=null ? fetcher.getService(ctx) :null;
}
// 添加服务--注册服务, 让HashMap中有值。
static {
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
}
// LayoutInflater.from(this) 它是一个系统Service服务;它是单例的设计模式。内存中只有一个实例。
// LayoutInflaterCompat.setFactory2(layoutInflater, this); 给单例设置Factory。
3.2 inflater(resId, contentParent) 方法的源码
// LayoutInflater
-- inflate(int resource, ViewGroup root, boolean attachToRoot)
-- public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {}
-- final View temp = createViewFromTag(root, name, inflaterContext, attrs);
-- createViewFromTag(parent, name, context, attrs, false);
-- View view = tryCreateView(parent, name, context, attrs);
View view;
// 前提是看是否有设置mFactory2 和 mFactory。AppCompatActivity中设置了mFactory。
if (mFactory2 != null) { // 【关键代码】:TODO:这里就会走自己设置的Factory2 的逻辑。
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) { // TODO:这里就会走自己设置的Factory 的逻辑。
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
// 通过反射创建view
// ...
-- mFactory.onCreateView()--> 就会走自己的onCreateView()方法,
--> return mAppCompatViewInflater.createView(...);
-- -- AppCompatViewInflater.createView()
-- -- switch-case,拦截View的创建。
createViewFromTag
系统的View组件,
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组件
view = onCreateView(context, parent, name, attrs);
} else {
view = createView(context, name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
--> onCreateView(@NonNull Context viewContext, @Nullable View parent,
@NonNull String name, @Nullable AttributeSet attrs)
--> onCreateView(parent, name, attrs);
--> onCreateView(name, attrs);
--> createView(name, "android.view.", attrs) // 拼接系统view的默认前缀
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Context context = (Context) mConstructorArgs[0];
if (context == null) {
context = mContext; // 设置context上下文。
}
return createView(context, name, prefix, attrs);
}
public final View createView(@NonNull Context viewContext, @NonNull String name,
@Nullable String prefix, @Nullable AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Objects.requireNonNull(viewContext);
Objects.requireNonNull(name);
Constructor<? extends View> constructor = sConstructorMap.get(name); // 构造方法缓存在map中。
if (constructor != null && !verifyClassLoader(constructor)) {
constructor = null;
sConstructorMap.remove(name);
}
Class<? extends View> clazz = null;
try {
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
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);
}
}
// mConstructorSignature 是2个参数的View的 构造方法[xml中设置的布局]
constructor = clazz.getConstructor(mConstructorSignature); // 拿到构造函数
constructor.setAccessible(true); // 私有公有都可访问
sConstructorMap.put(name, constructor); // 加入缓存map中
} else {
// If we have a filter, apply it to cached constructor
if (mFilter != null) {
// Have we seen this name before?
Boolean allowedState = mFilterMap.get(name);
if (allowedState == null) {
// New class -- remember whether it is allowed
clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
mContext.getClassLoader()).asSubclass(View.class);
boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
mFilterMap.put(name, allowed);
if (!allowed) {
failNotAllowed(name, prefix, viewContext, attrs);
}
} else if (allowedState.equals(Boolean.FALSE)) {
failNotAllowed(name, prefix, viewContext, attrs);
}
}
}
Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = viewContext;
Object[] args = mConstructorArgs;
args[1] = attrs;
try {
final View view = constructor.newInstance(args); // 构造函数new一个实例,返回
if (view instanceof ViewStub) {
// Use the same context when inflating ViewStub later.
final ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
}
return view;
} finally {
mConstructorArgs[0] = lastContext;
}
}
}
// so:不要布局嵌套,因为view都是通过反射来创建view的。
// View创建的方式:
// 系统的Button, 不包含'.'一般会拼接 android.view;
// 自定义的com.jd.MyTextView;
// 包含点'.', 拼接
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
if (view != null) {
return view;
}
XmlResourceParser parser = res.getLayout(resource); // 解析器
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext; // 构造函数的 参数
View result = root;
if (root != null && root.getViewRootImpl() != null) {
root.getViewRootImpl().notifyRendererOfExpensiveFrame();
}
try {
advanceToRootNode(parser);
final String name = parser.getName();
if (TAG_MERGE.equals(name)) { // 处理merge
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
if (root == null && temp != null && temp.getViewRootImpl() != null) {
temp.getViewRootImpl().notifyRendererOfExpensiveFrame();
}
ViewGroup.LayoutParams params = null;
if (root != null) { // 这里决定了有时候设置的属性无效问题。
// 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);
}
}
// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true);
// 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;
}
}
} 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;
}
}
4. Hook 拦截 View 的创建,仿照系统的
仿照系统的AppCompatActivity 去设置Factory就可以。
public abstract class BaseSkinActivity extends BaseActivity {
private static final String TAG = "BaseSkinActivity";
// TODO 后期添加插件换肤功能。预留层。每次换肤,都遍历集合View,然后修改
@Override
protected void onCreate(Bundle savedInstanceState) {
// 添加换肤相关, 在super之前调用。
LayoutInflater layoutInflater = LayoutInflater.from(this);
LayoutInflaterCompat.setFactory(layoutInflater, new LayoutInflaterFactory() {
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
// 拦截到View的创建
Log.e(TAG, "拦截了View的创建");
if (name.equals("Button")) { // Button 变 TextView
TextView tv = new TextView(BaseSkinActivity.this);
tv.setText("555 我被拦截了,并修改了");
return tv;
}
return null;
}
});
super.onCreate(savedInstanceState);
}
}
5. 总结:
- Activity是怎么加载布局的
- AppCompatActivity怎么加载布局的
- 为什么5.0的MaterialDesign 看起来不一样,但是又兼容低版本。且低版本能使用高版本的一些特性
- 界面是怎么加载的。LayoutInflater.from(this)
- View 是通过反射创建的,但是可以拦截。看有没有设置Factory。
网友评论