LayoutInflater setFactory

  • setFactory
  • setFactory2

这两个方法的功能基本是一致的,setFactory2是在SDK>=11以后引入的,所以我们要根据SDK的版本去选择调用上述方法。


值得高兴的是,v4包下有个类LayoutInflaterCompat帮我们完成了兼容性的操作,提供的方法为:

LayoutInflaterCompat
- setFactory(LayoutInflater inflater, 
             LayoutInflaterFactory factory)

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

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);
    }

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

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

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(root==null || typeface==null){
        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);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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

说到这,我估计你心理已经有全新的解决方案了,那就是利用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);

            if ( view!= null && (view instanceof TextView))
            {
                ((TextView) view).setTypeface(typeface);
            }
            return view;
        }
    });
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

6、扩展

setFactory还非常适合一个场景,就是换肤,换肤需要解决的核心问题有两个:

  • 外部资源的加载
  • 定位到需要换肤的View

第一个资源加载的问题可以通过构造AssetManager,反射调用其addAssetPath就可以完成。

第二个问题,就可以利用在onCreateView中,根据view的属性来定位,例如你可以让需要换肤的view添加一个自定义的属性skin_enabled=true(最开始有打印属性),并且利用一些手段拿到构造到的view,就能在View构造阶段定位的需要换肤的View。






























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值