Android 探究 LayoutInflater setFactory

LayoutInflaterCompat

  • setFactory(LayoutInflater inflater, LayoutInflaterFactory factory)

好了,下面我们就来写段代码看看该方法如何使用:

我们新建一个Activity,在其onCreate中调用setFactory

public class MainActivity extends AppCompatActivity

{

private static final String TAG = “MainActivity”;

@Override

protected void onCreate(Bundle savedInstanceState)

{

LayoutInflaterCompat.setFactory(LayoutInflater.from(this), new LayoutInflaterFactory()

{

@Override

public View onCreateView(View parent, String name, Context context, AttributeSet attrs)

{

Log.e(TAG, "name = " + name);

int n = attrs.getAttributeCount();

for (int i = 0; i < n; i++)

{

Log.e(TAG, attrs.getAttributeName(i) + " , " + attrs.getAttributeValue(i));

}

return null;

}

});

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

然后,我们运行项目,你会发现打印的log为(部分log):

MainActivity﹕ name = TextView

MainActivity﹕ layout_width , -2

MainActivity﹕ layout_height , -2

MainActivity﹕ text , @2131099670

这里针对布局文件中的一个TextView,可以看到打印了该TextView的name以及所有的attr相关信息。这些信息有什么用,我们后面的文章会介绍。

那么这个方法能干什么呢?

从字面上理解onCreateView是创建View,那么我们修改部分代码,添加如下代码:

if (name.equals(“TextView”))

{

Button button = new Button(context, attrs);

return button;

}

运行你会发现,界面上的TextView变成了Button

是不是很惊奇,但是我们已经能够确定这个方法的确能够根据布局文件中的信息去创建对应的View了。

你可以会问,谁没事干把TextView换成Button哇,就没什么靠谱的作用吗?

还真有,假设你的项目编写了一半,忽然有个需求需要自定义一个TextView(称为:MyTextView)来替换系统的TextView:

那么现在你就不必去打开以前的布局文件把TextView全部进行修改,直接在BaseActivity里面进行下面的操作就可以了:

if (name.equals(“TextView”))

{

MyTextView view = new com.zhy.MyTextView(context,attrs);

return view;

}

对于自定义的View,你也可以通过比对name(随便设置个name都可以,不需要去完整的编写全路径了),然后直接去new出该对象。这么做有一个好处,相比系统去帮你创建,效率会高一点,因为系统有一些逻辑需要走,并且最终是通过反射的方式帮你创建View。

不过,对于setFactory的使用,可能会面临下面的问题。

现在开发App的时候,我们一般Activity都继承于AppCompatActivity,而在AppCompatActivity中,实际上也调用了setFactory方法。

如果你自己还调用了setFactory就可能带来一些问题,因为setFactory并不能重复调用。

3、setFactory已经被v7包占领


打开AppCompatActivity的源码,找到onCreate,你会发现如下代码:

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

getDelegate().installViewFactory();

//…

}

installViewFactory的具体实现为:

@Override

public void installViewFactory() {

LayoutInflater layoutInflater = LayoutInflater.from(mContext);

if (layoutInflater.getFactory() == null) {

LayoutInflaterCompat.setFactory(layoutInflater, this);

} else {

Log.i(TAG, “The Activity’s LayoutInflater already has a Factory installed”

  • " so we can not install AppCompat’s");

}

}

这里你会发现,AppCompatActivity中也尝试去setFactory,如果我们再其之前调用了setFactory,就会打印上面的info信息,并且造成其setFactroy不会生效,其实从Log中也能看出:

AppCompatDelegate﹕ The Activity’s LayoutInflater

already has a Factory installed

so we can not install AppCompat’s

这么来看,如果我们使用AppCompatActivity,我们是不应该按照我们前面的方式,直接设置setFactory,否则我们可能会带来一些影响。

我们会带来什么影响呢?

其实AppCompatActivity的setFactory也是想根据name去生成一些类,大家还记得,更新v7包的时候,忽然我们的TextView就支持了一些属性,比如tint属性,以前是不支持的,怎么能够做到使用v7包,然后就能支持且向下兼容的呢?

哈哈,原理就在这里,看下面的代码片段

switch (name) {

case “TextView”:

view = new AppCompatTextView(context, attrs);

break;

case “ImageView”:

view = new AppCompatImageView(context, attrs);

break;

case “Button”:

view = new AppCompatButton(context, attrs);

break;

case “EditText”:

view = new AppCompatEditText(context, attrs);

break;

//…

}

可以看到系统其实是利用setFactory,瞒着我们把TextView等类早就换成AppCompatTextView等类了。

如果你使用AppCompatActivty你可以通过打印textview.getClass()来验证。

ok,看到这里,我们上面的一个问题也就知道答案了:

我们按照前面的方式直接设置setFactory,会带来什么影响呢?

会造成没有办法使用一些新的特性,比如tint等。

4、解决方案


我们具体看下appcompat中onCreateView的全部代码:

@Override

public final View onCreateView(View parent, String name,

Context context, AttributeSet attrs) {

// First let the Activity’s Factory try and inflate the view

final View view = callActivityOnCreateView(parent, name, context, attrs);

if (view != null) {

return view;

}

// If the Factory didn’t handle it, let our createView() method try

return createView(parent, name, context, attrs);

}

可以看到其最终是调用:createView方法完成view的创建,并且值得高兴的是该方法是public的。

也就是说,我们可以自己设置factory中,依然可以保证appcompat中创建View的代码的执行。

LayoutInflaterCompat.setFactory(LayoutInflater.from(this), new LayoutInflaterFactory()

{

@Override

public View onCreateView(View parent, String name, Context context, AttributeSet attrs)

{

//你可以在这里直接new自定义View

//你可以在这里将系统类替换为自定义View

//appcompat 创建view代码

AppCompatDelegate delegate = getDelegate();

View view = delegate.createView(parent, name, context, attrs);

return view;

}

});

下面看一个我们常见的场景。

5、高效统一设置app中所有字体


很多时候我们为了app更加个性,然后整体采用外部引入的字体。

很多开发者的实现是这样的,在BaseActivity的onCreate中去从跟布局去递归遍历所有的View,类似的代码如下:

public void setTypeface(ViewGroup root, Typeface typeface){

if(rootnull || typefacenull){

return;

}

int count = root.getChildCount();

for(int i=0;i<count;++i){

View view = root.getChildAt(i);

if(View instanceof TextView){

((TextView)view).setTypeface(typeface);

}else if(View instanceof ViewGroup){

setTypeface((ViewGroup)view, typeface);

}

}

}

这种方式虽然方便,但是肯定会带来一定性能问题。

说到这,我估计你心理已经有全新的解决方案了,那就是利用setFactory,相关代码如下(在BaseActivity中):

public static Typeface typeface;

@Override

protected void onCreate(Bundle savedInstanceState)

{

if (typeface == null)

{

typeface = Typeface.createFromAsset(getAssets(), “hwxk.ttf”);

}

LayoutInflaterCompat.setFactory(LayoutInflater.from(this), new LayoutInflaterFactory()

{

@Override

public View onCreateView(View parent, String name, Context context, AttributeSet attrs)

{

AppCompatDelegate delegate = getDelegate();

View view = delegate.createView(parent, name, context, attrs);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

学习分享

①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

程,基本涵盖了95%以上Android开发知识点,真正体系化!**

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

[外链图片转存中…(img-BjuX7tTM-1713760458790)]

学习分享

①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包

[外链图片转存中…(img-rRR57qFC-1713760458791)]

[外链图片转存中…(img-bOGhHPEf-1713760458792)]

[外链图片转存中…(img-81YkbmQ9-1713760458793)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值