首页 > 其他分享 >Android视图加载优化——Factory2设置方法

Android视图加载优化——Factory2设置方法

时间:2023-06-22 11:03:06浏览次数:45  
标签:Factory2 LayoutInflater 视图 theme Android AppCompatActivity TextView View


前言

Factory2是直接继承于Factory,继续跟踪下Factory的源码,比Factory的功能更加强大。 当我们新建 Activity 的时候,大部分情况是继承 AppCompatActivity 。提供了向后兼容性。

本文将深入探索 AppCompatActivity视图加载,探索将 xml 布局文件中的 TextView 替换成 AppCompatTextView 的全过程,并由浅入深介绍了Factory2 的一些奇技淫巧,帮助各位Android 开发者简化开发,提高效率。


Android视图加载优化——Factory2设置方法_android studio

一、Factory2

在 Android 中,我们经常在 xml 文件中书写布局。这些文件被打包进 app(因为性能原因由 aapt/2 转换为二进制 xml),并且在运行时由 LayoutInflater 加载。

LayoutInflater 中有两个方法 setFactorysetFactory2 ,文档中是这样描述的:

当使用 LayoutInflater 创建 View 的时候,绑定一个自定义的 factory 实例。不能为 null,并且只能设置一次,设置之后无法修改,当 xml 中每一个元素名字被解析的时候调用。若 factory 返回一个 View,将被添加到视图层级中;若返回 null,factory 的下一个默认方法 onCreateView(View, String, AttributeSet) 将被调用。

注意,Factory2 implements Factory ,所以对于 Api 11+ 的应用来说,应该使用 setFactory2 。这就相当于给了我们介入 xml 中每一个 View 元素的创建过程的机会。让我们看一个实际使用:

Android视图加载优化——Factory2设置方法_自定义_02

上面的代码中,我们仅仅为当前 ContextLayoutInflater 设置了一个 Factory2 。这样只要发现了 TextView ,都会被替换为我们自己的实现类 RedTextView

RedTextViewTextView 的子类,提供了 setBackgroundColor 方法,将背景置为红色:

Android视图加载优化——Factory2设置方法_xml_03

布局文件 factory.xml 是这样的:

Android视图加载优化——Factory2设置方法_java_04

运行应用并使用 Layout Inspector,我们发现所有的 TextView 都变成了 RedTextView 。棒极了!

二、Appcompat Activity 和 Factory2

如果把上面的 FactoryActivity 修改为继承 AppCompatActivity ,我们会看到 TextView 确实变成了 RedTextView 。但是我们添加的 Button 仍然是 Button,并没有变成 AppCompatButton ,这是为什么?

AppCompatActivityonCreate 方法的前两行是:

Android视图加载优化——Factory2设置方法_android_05

getDelegate() 根据 api 版本的不同返回对应的代理类(AppCompatDelegateImplV14, AppCompatDelegateImplV23, AppCompatDelegateImplN 等等)。

下一行代码 delegate.installViewFactory() ,当 layoutInflater.getFactory() 为空的时候,会调用 setFactory2。如果不为空,什么都不会做。

所以 Button 没有发生变化的原因是,已经设置过了 Factory,导致 AppcompatActivity 自己的 factory 没有被 install。

注意,FactoryActivitysetFactory2() 方法是在 super.onCreate 之前调用的。如果不是的话,当父类是 AppcompatActivitysetFactory2 会抛出异常。因为 AppCompatActivity 设置了自己的 Factory 。文档中是这样描述的:它不能为空,且只能被设置一次;在设置之后,你不能对 Factory 进行改变

三、如何兼容 AppCompatActivity 的 Factory2

如何既能使用自己的 Factory2,又能让 AppCompatActivity 保留自己的 Facotory 呢?下面给出几种解决方法。

(一)代理给 AppCompatDelegate

AppCompatDelegate 内部有一个 createView 方法,不要和 FactoryFactory2onCreateView 混淆。

Android视图加载优化——Factory2设置方法_自定义_06

我们仅仅只需要修改 setFactory2 ,将不需要处理的情况代理给 AppCompatDelegate

Android视图加载优化——Factory2设置方法_自定义_07

运行一下,TextView 变成了 RedTextViewButton 变成了 AppCompatButton ,成功!

(二)重写 viewInflaterClass

我们看一下 AppCompatDelegatecreateView 方法,当 AppCompatViewInflater 没有初始化时,会通过反射创建。要初始化的类由 R.styleable.AppCompatTheme_viewInflaterClass 指定,默认就是 AppCompatViewInflater

FactoryActivity 的 theme 进行如下修改:

Android视图加载优化——Factory2设置方法_自定义_08

就可以让 AppCompatDelegate 使用我们自定义的 AppCompatViewInflater 的子类 CustomViewInflater :

Android视图加载优化——Factory2设置方法_android studio_09

Google 的 Material Design Components 实际上就是使用这种方法来将 Button 修改为对应的 MaterialButton ,在 这里 可以看到 。

这个方法很强大,它可以让你的 App 使用 Material Design Components 这样的类库,却仅仅只需要设置合适的主题。

注意 AppCompatViewInflater 还提供了一个可以被重写的 createView() 方法,用来处理默认情况下没有被处理的新的组件。当 AppCompatViewInflater 没有处理特定的组件类型,就可以使用这个方法。

(三)自定义 LayoutInflater

第三种方法是重写 ActivityattachBaseContext ,改写 ContextThemeWrappergetSystemService 方法,返回自定义的 LayoutInflater 。自定义的 LayoutInflater 可以重写 setFactory2 方法,加入自己的处理逻辑。这个方法是我从 ViewPump 学到的。

四、一些小细节

下面介绍了 AppCompatDelegate 在进行视图加载过程中的几个小细节。

(一)onCreateView

我们希望 Factory2onCreateView 方法直接调用 createView (代理给 AppCompatDelegate 那一小节中提到过) 。事实上,的确也是这么做的。但是代码中还多了一点东西 - 调用了 callActivityOnCreateView 。在 AppCompatDelegateImplV14 中是这样的:

Android视图加载优化——Factory2设置方法_android_10

看一下 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 。

AppCompatViewInflatercreateView 使用了正确的 Context (考虑到支持 app:themeandroid: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 就被返回了。

Android视图加载优化——Factory2设置方法_android studio_11

五、总结和使用实例

总结一下,AppCompatActivity 通过给 LayoutInflater 设置 Factory2 来介入 View 的创建过程,以提供向后兼容性(为组件提供 tint,处理 android:theme 等)。它也保证了可扩展性,开发者可以进行一些定制处理。

除了 Appcompat,这一技巧被用来完成了更多有意思的事情。Probe (现已废弃) 提供了 OvermeasureInterceptor 来记录 View 的测量次数,LayoutBoundsInterceptor 来高亮 View 的边界。

Calligraphy 使用这一技巧方便的为 TextView 添加字体。它使用了ViewPump 库,在 wiki 中提供了一些可能的使用方式。

最后,Google 的 Material Components for Android 通过自定义 AppCompatViewInflaterButton 替换为 MaterialButton

原文作者:ahmed el-helw

AppCompat View Inflation

标签:Factory2,LayoutInflater,视图,theme,Android,AppCompatActivity,TextView,View
From: https://blog.51cto.com/u_16163453/6534426

相关文章

  • 6轮面试辛苦拿到阿里Android开发offer,却从22k降到15k,在逗我?
    一小伙工作快3年了,拿到了阿里云Android开发岗位P6的offer,算HR面一起,加起来有6轮面试了,将近3个月的时间,1轮同级+1轮Android用人部门leader+1轮Android组leader+1轮项目CTO+1轮HR+1轮HRBP。一路上各种事件分发机制、自定义View、handler原理、多线程、hashmap、手写算法、......
  • 三年Android开发就这水平?我还不如去招应届生
    公司前段缺人,也面了不少android程序员,结果竟然没有一个合适的。一开始瞄准的就是中级的水准,也没指望来大牛,提供的薪资在10-20k,面试的人很多,但平均水平很让人失望。看简历很多都是3年工作经验,但面试中,不提算法逻辑,仅仅基础的技术很多也知之不详,多数人数年的工作经验仅仅是用大量第三......
  • 【Android】如何实现同一个布局保证高度不变,使用不同高度的背景
    背景预实现一个切换tab,实现选中与未选中的背景切换,特别之处在于选中背景图和未选中背景图高度不相同,切换之后需要在java代码中动态设置LayoutParams改变高度。预期效果当前问题点选中背景为.9图,未选中背景为xml中通过shape实现。将当前ViewGroup设置为选中状态的固定高度选中效果正......
  • Android binder 机制驱动核心源码详解
    前言应用程序中执行getService()需与ServiceManager通过binder跨进程通信,此过程中会贯穿Framework、Natve层以及Linux内核驱动。binder驱动的整体分层如上图,下面先来宏观的了解下getService()在整个Android系统中的调用栈,ServiceManager本身的获取:与ServiceManage......
  • Android面试技巧总结,这下offer稳啦
    最近有很多朋友给我后台留言:自己投了不少简历,但是收到的面试邀请却特别少;好不容易收到了大厂的面试邀请,但是面试官问得太深了,结果也挂了;对于面试官的问题,明明知道该怎么做,但是却说不清楚。这些问题不是个例,很多人都有这样的困扰。很大一部分是技术层面的问题。薪资比较高的前端岗位......
  • 六年Android开发从组员到Leader的心路历程分享
    前言在互联网工作的这些年,大厂和小厂都待过,也接触过各种各样的管理者和组员,直到近两年自己开始成为技术Leader,算是在两种角色上都有些切身的心得体会,这里给大家分享下,希望能给大家的职场工作带来一些启发。简单说明下,在毕业不久加入阿里的第一年,团队大概十几个人,作为三个新人之一,......
  • Android太太太太太卷了,累了
    我们聊到互联网行业的时候,一个不可避免的话题就是“内卷”,而在程序员这个群体中,Android,绝对是卷得最厉害的。毕竟前几年Android兴起的时候,入门门槛低,培训机构培养了大批Android开发,市面上的初级前端根本不缺,他们很多也是在一些中小厂里写一些重复性的业务代码,再加上后面又有更多的A......
  • Android - Jetpack ViewModel源码探秘
    ViewModel使用场景当横竖屏切换时,希望数据不丢失,可以用ViewModel当成存储媒介;可作为Activity&Fragment通讯的媒介;ViewModel的创建//Activity中构建MyViewModelViewModelProvider(this).get(MyViewModel::class.java)//ViewModelProviders类中publicViewModelProvider(@NonNu......
  • 干了六年Android开发现在裸辞失业了,再过2个月就30了,该怎么继续生活?
    这是我在某论坛看到别人分享的故事,觉得可以展开聊一下,对于我们这些中年程序员,可以裸辞吗?前言首先介绍一下主人公的情况。目前所在的是一家小的创业公司,待了3年多,薪资一般吧,之前在一家中型上市企业也干了三年,因为想涨薪所以跳到现在这家小公司。就在年前,公司年终总结,公司老板会和各......
  • 字节跳动总监封神之作《Android11.0最新Framework解析》,1595页,限时免费下载高清PDF文
    Framework始终穿插在App整个研发生命周期中,不管是从0到1的建立阶段,还是从1到N打磨阶段,都离不开Framework。成为一名AndroidFramework高手,就会成为招聘中非常稀缺的人才,可以成为你的敲门砖。很多同学都表示在面试时必问Framework相关问题。因为目前大公司的app开发都要基......