三、如何兼容 AppCompatActivity 的 Factory2
如何既能使用自己的 Factory2,又能让 AppCompatActivity
保留自己的 Facotory 呢?下面给出几种解决方法。
(一)代理给 AppCompatDelegate
在 AppCompatDelegate
内部有一个 createView
方法,不要和 Factory
、Factory2
的 onCreateView
混淆。
我们仅仅只需要修改 setFactory2
,将不需要处理的情况代理给 AppCompatDelegate
:
运行一下,TextView
变成了 RedTextView
,Button
变成了 AppCompatButton
,成功!
(二)重写 viewInflaterClass
我们看一下 AppCompatDelegate
的 createView
方法,当 AppCompatViewInflater
没有初始化时,会通过反射创建。要初始化的类由 R.styleable.AppCompatTheme_viewInflaterClass
指定,默认就是 AppCompatViewInflater
。
对 FactoryActivity
的 theme 进行如下修改:
就可以让 AppCompatDelegate
使用我们自定义的 AppCompatViewInflater
的子类 CustomViewInflater
:
Google 的 Material Design Components 实际上就是使用这种方法来将 Button
修改为对应的 MaterialButton
,在 这里 可以看到 。
这个方法很强大,它可以让你的 App 使用 Material Design Components 这样的类库,却仅仅只需要设置合适的主题。
注意 AppCompatViewInflater
还提供了一个可以被重写的 createView()
方法,用来处理默认情况下没有被处理的新的组件。当 AppCompatViewInflater
没有处理特定的组件类型,就可以使用这个方法。
(三)自定义 LayoutInflater
第三种方法是重写 Activity
的 attachBaseContext
,改写 ContextThemeWrapper
的 getSystemService
方法,返回自定义的 LayoutInflater
。自定义的 LayoutInflater
可以重写 setFactory2
方法,加入自己的处理逻辑。这个方法是我从 ViewPump 学到的。
四、一些小细节
下面介绍了 AppCompatDelegate 在进行视图加载过程中的几个小细节。
(一)onCreateView
我们希望 Factory2
的 onCreateView
方法直接调用 createView
(代理给 AppCompatDelegate
那一小节中提到过) 。事实上,的确也是这么做的。但是代码中还多了一点东西 - 调用了 callActivityOnCreateView
。在 AppCompatDelegateImplV14
中是这样的:
看一下 LayoutInflater
的 源码 , createViewFromTag
尝试通过 factory 获取 view 。如果没有获取到,会使用 mPrivateFactory
。如果依旧没有获取到,会通过视图标签去创建 view 。mPrivateFactory
是在 Activity 中设置的。
有意思的是, mPrivateFactory
的作用是解析 fragment
标签。
在 API 14 之前,LayoutInflater
并没有提供 mPrivateFactory
让 Activity 可以有个兜底方案来创建 View 。因此,callActivityOnCreateView
在低版本中提供了这一功能。但这现在都没有关系了,反正 AppCompat 目前只兼容 Api 14+ 。
另一个有意思的知识点是 Window.Callback 。Window.Callback
是一个回调,让调用者可以拦截 key 的分发,面板,菜单等等。它让 AppCompatActivity 可以处理一些特定时间,例如菜单键,返回键等。
(二)createView
总的来说,AppCompatDelegateImplV9
做了两件事。首先,创建了 AppCompatViewInflater
或者在 theme 中指定的其他子类。第二,通过 inflater 创建 View 。
AppCompatViewInflater
的 createView
使用了正确的 Context
(考虑到支持 app:theme
和 android:theme
,需要对 Context 进行包装),根据组件名称创建对应的 AppCompat 组件(例如,如果是 TextView
,就调用 createTextView
方法返回 AppCompatTextView
)。
(三)支持 app:theme
从 Android 5.0 开始,可以给 View 设置 app:theme
以覆盖特定 View 及其子类的属性。AppCompat 通过继承父 View 的 context 在 Android 5.0 之前复制这一行为。
在 AppCompat 加载 View 之前,它先拿到父 View 的 Context,然后尝试创建一个 ContextThemeWrapper(android:theme
或者 app:theme
),保证使用正确的 context 来加载组件。
另外,如果开发者明确声明需要在资源中使用矢量图,AppCompat 在 Android 5.0 之前还提供了 TintContextWrapper
来包装 Context 。
(四)View 的创建和兜底
通过这些信息,系统已经准备好如何创建 View 了。
遍历支持的组件列表,对于通用的 View,如 TextView
, ImageView
,直接生成对应的 AppCompat 子类。如果是未知类型的 View,将使用正确的 Context 调用 createView
,默认返回 null,但一般会被 AppCompatViewInflater 的子类重写。
如果这时候 view 仍然是 null,会检查 view 的原始 context 是否和父 View 的 context 一致。这种情况会发生在子 View 的 android:theme
和 父 View 不一致。
在检查 android:onClick
之后,view 就被返回了。
五、总结和使用实例
总结一下,AppCompatActivity
通过给 LayoutInflater
设置 Factory2
来介入 View 的创建过程,以提供向后兼容性(为组件提供 tint,处理 android:theme
等)。它也保证了可扩展性,开发者可以进行一些定制处理。
除了 Appcompat,这一技巧被用来完成了更多有意思的事情。Probe (现已废弃) 提供了 OvermeasureInterceptor 来记录 View 的测量次数,LayoutBoundsInterceptor 来高亮 View 的边界。
Calligraphy 使用这一技巧方便的为 TextView 添加字体。它使用了ViewPump 库,在 wiki 中提供了一些可能的使用方式。
最后,Google 的 Material Components for Android 通过自定义 AppCompatViewInflater
将 Button
替换为 MaterialButton
。
原文作者:ahmed el-helw
全套视频资料:
一、面试合集
二、源码解析合集
学习分享
在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了
很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘
如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。
2021最新上万页的大厂面试真题
七大模块学习资料:如NDK模块开发、Android框架体系架构…
2021大厂面试真题:
只有系统,有方向的学习,才能在短时间内迅速提高自己的技术,只有不断地学习,不懈的努力才能拥有更好的技术,才能在互联网行业中立于不败之地。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
到真正的技术提升。**
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!