https://blog.csdn.net/qq_30889373/article/details/80538211#Android_View_11
API 版本 27
我们先看看 AppCompatActivity 的跟这次主题相关的
重要方法
public class AppCompatActivity ...{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
final AppCompatDelegate delegate = getDelegate();
// 这个方法我们稍微记一下,待会要
delegate.installViewFactory();
// ....
super.onCreate(savedInstanceState);
}
@NonNull
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
}
return mDelegate;
}
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
}
AppCompatDelegate 是什么呢,我们进入继续观看
public abstract class AppCompatDelegate {
....
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
return create(activity, activity.getWindow(), callback);
}
// 根据不同的系统版本 创建不同的 Imp
private static AppCompatDelegate create(Context context, Window window,
AppCompatCallback callback) {
if (Build.VERSION.SDK_INT >= 24) {
return new AppCompatDelegateImplN(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 23) {
return new AppCompatDelegateImplV23(context, window, callback);
} else {
return new AppCompatDelegateImplV14(context, window, callback);
}
}
}
我们 查看一下继承关系
对应的类点击查看发现
public class AppCompatActivity ...{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
final AppCompatDelegate delegate = getDelegate();
// 这个方法我们稍微记一下,待会要进入主题
delegate.installViewFactory();
}
// 重点关注下 LayoutInflater.Factory2
class AppCompatDelegateImplV9 extends AppCompatDelegateImplBase
implements MenuBuilder.Callback, LayoutInflater.Factory2 {
@Override
public void installViewFactory() {
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
if (layoutInflater.getFactory() == null) {
// 重点方法,第二个参数是 LayoutInflater.Factory2 也就是自己实现了
LayoutInflaterCompat.setFactory2(layoutInflater, this);
} else {
if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImplV9)) {
Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
+ " so we can not install AppCompat's");
}
}
}
}
// 而 Factory2 接口就只有一个方法
public interface Factory2 extends Factory {
public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
}
这里可以自行跟进去看看,我们这里只讲一个重点知识,因为类似
https://github.com/ximsfei/Android-skin-support
通过替换 Factory2 来实现换肤的方案,需要知道这个知识点
public abstract class LayoutInflater {
public void setFactory2(Factory2 factory) {
// 可以看到只能设置一次,而上面说明的开源库,是通过反射修改这个值
// 替换自己的 Factory2
if (mFactorySet) {
throw new IllegalStateException("A factory has already been set on this LayoutInflater");
}
if (factory == null) {
throw new NullPointerException("Given factory can not be null");
}
mFactorySet = true;
if (mFactory == null) {
mFactory = mFactory2 = factory;
} else {
mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
}
}
}
那么上面讲了这么多跟 xml解析有什么关系呢,我们进入主题后,你就明白前面的意思了
···
// 1
public class AppCompatActivity ...{
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
}
// 2
class AppCompatDelegateImplV9 {
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
// 通过 LayoutInflater 解析,我们跟进去看下
LayoutInflater.from(mContext).inflate(resId, contentParent);
mOriginalWindowCallback.onContentChanged();
}
}
// 3
public abstract class LayoutInflater {
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) {
final Resources res = getContext().getResources();
// 通过 资源ID 获取 XmlResourceParser
final XmlResourceParser parser = res.getLayout(resource);
try {
// 进入查看
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
// 关键方法来了
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
....
if (TAG_MERGE.equals(name)) {
.. 其实内部最终也会调用 createViewFromTag
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// 关键方法 传入
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
}
}
// 4
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) {
//...可以看到
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
}
总结前面分析
关键方法 | 描述 |
---|---|
LayoutInflater.from(mContext).inflate(resId, contentParent) | 传入资源ID ,父容器,可空 |
XmlResourceParser parser = res.getLayout(resource); | 通过资源ID 获取 xml解析后的类 |
mFactory2.onCreateView(parent, name, context, attrs) | 而后调用 mFactory2.onCreateView 创建对应view |
mFactory2 在上面的 AppCompatDelegateImplV9 的 installViewFactory() 中已设置好了的,
其实 mFactory2 就是 AppCompatDelegateImplV9.
public class AppCompatDelegateImplV9{
@Override
public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
// First let the Activity's Factory try and inflate the view
final View view = callActivityOnCreateView(parent, name, context, attrs);
if (view != null) {
return view;
}
// If the Factory didn't handle it, let our createView() method try
return createView(parent, name, context, attrs);
}
@Override
public View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
if (mAppCompatViewInflater == null) {
....
mAppCompatViewInflater = new AppCompatViewInflater();
} else {
....
}
...
return mAppCompatViewInflater.createView(....);
}
}
下面我们查看 系统控件的创建过程
public AppCompatViewInflater {
/**
* 可以发现,如果是TextView,就直接返回V7包中的AppCompatTextView类,
* 所以这就是为什么当我们使 用AppCompatActivity的时候,TextView变成AppCompatTextView的原因。
*/
@NonNull
protected AppCompatTextView createTextView(Context context, AttributeSet attrs) {
return new AppCompatTextView(context, attrs);
}
// 创建 View
final View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs, boolean inheritContext,
boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
final Context originalContext = context;
....
switch (name) {
case "TextView":
view = createTextView(context, attrs);
verifyNotNull(view, name);
break;
default:
view = createView(context, name, attrs);
}
if (view == null && originalContext != context) {
// 使用名字 inflate,主题将会无效
view = createViewFromTag(context, name, attrs);
}
if (view != null) {
// 如果我们尝试创建一个 view, check its android:onClick
checkOnClickListener(view, attrs);
}
return view;
}
}
那么自定义控件,还有哪些需要 v7 包名的组件怎么创建的呢?
public AppCompatViewInflater {
// 默认前缀包名列表
private static final String[] sClassPrefixList = {
"android.widget.",
"android.view.",
"android.webkit."
};
private View createViewFromTag(Context context, String name, AttributeSet attrs) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
try {
mConstructorArgs[0] = context;
mConstructorArgs[1] = attrs;
// 尝试从 默认前缀包名列表 中查找
if (-1 == name.indexOf('.')) {
for (int i = 0; i < sClassPrefixList.length; i++) {
final View view = createViewByPrefix(context, name, sClassPrefixList[i]);
if (view != null) {
return view;
}
}
return null;
} else {
// 加载自定义View,也就是完整名 比如 com.xm.xbutton
return createViewByPrefix(context, name, null);
}
} catch (Exception e) {
return null;
} finally {
mConstructorArgs[0] = null;
mConstructorArgs[1] = null;
}
}
}
public interface Factory2 extends Factory {
pulic View onCreateView(View parent, String name, Context context, AttributeSet attrs);
}
}
简单几句话讲解下 xml的创建过程