常规的控件是怎么变为AppCompat控件的,还有AppCompat相关的两个bug

众所周知,通过布局xml文件来加载view都需要使用LayoutInflater来对布局里的控件进行实例化,获取Inflater对象时都需要context参数,而我们一般获取Inflater对象也是使用的activity来获取的,从大概安卓5.0开始,support包中多了一个AppCompatActivity,控件类型的偷梁换柱也就是在这干的,onCreate的时候就开始了

实现类AppCompatDelegateImpl方法的实现如下,首先调用LayoutInflater#from获取到当前Context的LayoutInfater对象,然后调用LayoutInflaterCompat#setFactory2来完成对InflaterFactory的替换

LayoutInflaterCompat#setFactory2的实现如下,调用了原Infater对象的setFactory2替换为新的InflaterFactory,而新的Factory,就是上图调用的时候传的this,既AppCompatDelegateImpl

这时候,AppCompat已经完成了对InfaterFactory的替换,接着再继续看LayoutInfater中是怎么用Factory的,在LayoutInInfater中完成了对xml解析后,会调用tryCreateView函数通过tag类型完成View的实例化。优先使用了mFactory2#onCreateView来实例化View对象

而Factory2就是刚才替换的那个Factory(AppCompatDelegateImpl),然后看下AppCompatDelegateImpl#tryCreateView的实现长啥样

代码太长了,就不粘贴了,一通操作之后,调用mAppCompatViewInflater#createView创建了View,代码如下

而createTextView就长这样

这就完事了最终,所有通过AppCompatActivity加载xml所实例化的对象大部分(得看AppCompat里实现了几个)都会被转为AppCompatXXX类型,而使用其他context的则不会,Context使用的时候还是要多留意

再说bug

第一个在一个列表页,创建列表Adapter的时候使用的是Activity的baseContext,而baseContext是Activity的一个参数,并不会被替换对应的LayoutInfater,所以实例化出来的控件都是非AppCompat的,之后又用了一个AppCompat的参数,然后类型不对,找不到参数,自然就崩了(是在某个版本的TextView的源码中,貌似是源码的bug,具体忘了)

第二个,首页底部tab使用了RadioButton+RadioGroup来实现,使用<item name=”android:button”>@null</item>隐藏来原来的圆形实现的,但是在4.4的手机上按钮又出来了。排查发现,在AppCompatRadioButton中,给RadioButton设置了一个主题radioButtonStyle,是AppCompat包里带的,指向Widget.AppCompat.CompoundButton.RadioButton,最终指向了Base.Widget.AppCompat.CompoundButton.RadioButton。而这个Base的主题呢,有两个实现一个在values里,一个在values-v21里values里的长这样

values-v21里长这样,引用了系统的主题

values里给button和buttonCompat设置了资源文件,buttonCompat引用的就是Material样式的按钮,为的就是让低版本的控件也能有Material的样子,然后呢AppCompatRadioButton里干了什么呢

先设置了buttonCompat的Drawable,然后buttonCompat有参数的话就不给下面的button配置机会了然后就导致了上面说的bug,最后给buttonCompat也配置为@null就解决了

留下评论

电子邮件地址不会被公开。 必填项已用*标注