参考
脑图:https://note.youdao.com/s/Q1e6r39j
最终效果:
Demo源码:点击跳转
技术点分析
换肤的核心思路主要是在setContentView()
之前调用setFactory2()
来收集控件属性,然后在Factory的onCreateView()
中利用收集到的属性来创建view。
不懂?没事,往下看。
弄明白换肤技术的实现之前,得有上图这几个知识储备。
首先得知道控件是在setContentView()
方法中通过XmlPullParser
解析我们在xml中定义的控件,然后显示在界面上的。
LayoutInflater.java(451,注:本文源码为安卓9.0,api 28,下同)
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
...
if (TAG_MERGE.equals(name)) {
...
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);
...
}
...
而且在createViewFromTag()
方法中,有一个判断:当mFactory2 != null
的时候,就会把从xml中解析到的属性等传给mFactory2.onCreateView(parent, name, context, attrs)
方法,利用mFactory2
来创建view。
先看源码片段:
LayoutInflater.java(748)
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
...
try {
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);
}
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
} catch (InflateException e) {
...
}
}
所以,我们只要通过LayoutlnflaterCompat.setFactory2(xx, yу)
设置了Factory,就可以拦截到所有控件及其在xml中定义的属性了。
如此一来,问题就变成了如何在setContentView(R.layout.xxx)
之前setFactory2()
?
答案就是利用AOP方法切面:registerActivityLifecycleCallbacks(xxx)
。ActivityLifecycleCallbacks
的onActivityCreated()
方法正是在setContentView(R.layout.xxx)
之前执行。
所以,我们可以实现Application.ActivityLifecycleCallbacks
,然后在onActivityCreated()
方法中LayoutInflaterCompat.setFactory2(xx, yy)
,这样换肤技术的核心部分,就被我们突破了。
参考代码:
public class SkinActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
...
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
...
skinFactory = new SkinFactory(activity);
// mFactorySet = true是无法设置成功的(源码312行)
LayoutInflaterCompat.setFactory2(layoutInflater, skinFactory);
// 注册观察者(监听用户操作,点击了换肤,通知观察者更新)
SkinEngine.getInstance().addObserver(skinFactory);
}
...
}
About