赞
踩
提到换肤,我的第一反应就是if+else去弄(俗称静态换肤),这样带来的结果就是代码臃肿,扩展性非常差,所以pass掉。那么动态换肤就不会存在这个问题,弄懂原理,加上AOP的思想去干,就类似于插件化了。
那么它的原理是怎样的呢?下面让我们来看:
都知道Acitivity的setContentView是用来设置布局的,而我们的换肤功能就是要动态改变控件的属性值,所以从setContentView的源码走起。
setContentView -> getDelegate().setContentView(layoutResID) -> AppCompatDelegateImpl.setContentView(int id) -> LayoutInflater.from(mContext).inflate(resId, contentParent); -> return inflate(parser, root, attachToRoot); -> final View temp = createViewFromTag(root, name, inflaterContext, attrs); -> View view = tryCreateView(parent, name, context, attrs);
在tryCreateView这个方法里,我们就会发现一个变量名为mFactory2的东东,是通过它的onCreateView方法去创建控件的,仔细一看,这个LayoutInflate类里的一个接口,继续找它的实现方法,在AppCompatDelegateImpl这个类,实现了该接口以及该方法,一路狂点,直达最核心源码,最终发现在这个类里的createView方法看到了创建控件的代码。
再点进去一看,
原来的TextView被替换成了AppCompatTextView,至此,我们可以想象,他们能替换,为什么我们不能替换呢? 是不是我自己弄一个mFactory2的实例,我也能创建我想要的东西呢?之前我们已经看到是通过mFactory2来创建view,那么接下来,我们就要寻找这个东西是在哪里使用的,该如何去使用它?
还是回到MainActivity(又回到最初的起点,呆呆的。。。点进去看源码!!!)
从super.onCreate -> delegate.installViewFactory(); ->AppCompatDelegateImpl.installViewFactory -> LayoutInflaterCompat.setFactory2 -> LayoutInflater.setFactory2
可以看出,是在这里设置了mFactory2
接下来我们做个测试,把xml布局文件里的TextView动态改成Button
布局文件 activity_test
通过代码设置自定义mFactory2
运行看结果
没错,一个活生生的TextView已经被拦截修改为Button,于是,换肤的思路大概就出来了,拦截->修改!!
有一个要注意的小地方,设置mFactory2要在super.onCreate之前,因为在源码的LayoutInflater.setFactory2这个方法中,已经有明确的错误抛出
可以看出,条件就是mFactorySet这个标识,如果是在 super.onCreate之后,这个标识已经被修改为true(如第二个箭头所示),但是如果非要在super.onCreate之后,也不是不可以,通过反射修改这个标识的值为false,我们还是愉快的运行代码- -
好啦,通过源码知道了系统的运行顺序以及逻辑,那么接下来我们就采用AOP的思想,动手做我们的换肤。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。