Android Butterknife终结者—ViewBinding

时间:2021-7-21 作者:qvyue
一. 关于findViewById常用的方式, 只看java版
  1. 手写findViewById.
  2. 插件生成findViewById, 例如: FindViewByMe
  3. 注解的方式, 用反射解析, 例如:XUtils中的@ViewInject
  4. Butterknife, 这个应该是到目前为止, 大部分人使用的方案了.
  5. databinding, 主要的方向不在findViewById这个问题上, 所以也不好比较。
  6. ViewBinding, 只看findViewById的话, 应该是目前比较好的解决方案了.
二. ViewBinding认可度

我们先看看在 Butterknife的github地址上, 一开始就可以看到这样一段话(如下图):
大概意思是:Butterknife已经停止维护更新, 推荐我们使用ViewBinding.

连Butterknife的作者Jack大神都觉得ViewBinding比Butterknife更好用, 所以我觉得很有必要去了解一下.

Android Butterknife终结者---ViewBinding
Butterknife.png
二. ViewBinding基本使用

1. app/build.gradle下

android {
    viewBinding {
        enabled = true
    }
    //或
    buildFeatures{
         viewBinding = true
    }
}

2. Activity中

public class MainActivity extends AppCompatActivity {

    ActivityMainBinding binding;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        binding.tvText.setText("测试"); 
    }
}
三. BaseActivity的封装

关键点:利用泛型+反射获取Binding对象.
如果追求完美,不喜欢用反射,也可以子类重写getBinding()方法.

public abstract class BaseActivity extends AppCompatActivity {

    T binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = getBinding();
        setContentView(binding.getRoot());
        init();
    }

    protected abstract void init();

    protected T getBinding(){
        try {
            Type superClass = getClass().getGenericSuperclass();
            Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
            Class> clazz = ClassUtils.getRawType(type);
            Method method = clazz.getMethod("inflate", LayoutInflater.class);
            return (T) method.invoke(null, getLayoutInflater());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
public class MainActivity extends BaseActivity{
    @Override
    protected void init() {
        binding.tvText.setText("1234567890");
    }
}
四. RecyclerView中的使用

关键点: ViewHolder对象维护一个Binding泛型对象就可以了. 具体Adapter怎么封装看个人了.

public class BaseViewHolder extends RecyclerView.ViewHolder{
    T viewbinding;
    public BaseViewHolder(@NonNull T viewbinding) {
        super(viewbinding.getRoot());
        this.viewbinding = viewbinding;
    }
}
public abstract class BaseAdapter extends RecyclerView.Adapter> {

    public List mList;

    public BaseAdapter(List list){
        mList = list;
        if(mList==null){
            mList = new ArrayList();
        }
    }

    public abstract void onBindViewHolders(@NonNull V binding, T bean, int position);


    @NonNull
    @Override
    public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new BaseViewHolder(getBinding(parent.getContext(), viewType));
    }

    @Override
    public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
        onBindViewHolders(holder.getBinding(), mList.get(position), position);
    }


    @Override
    public int getItemCount() {
        return mList.size();
    }


    protected V getBinding(Context context, int viewType){
        try {
            Type superClass = getClass().getGenericSuperclass();
            Type type = ((ParameterizedType) superClass).getActualTypeArguments()[1];
            Class> clazz = ClassUtils.getRawType(type);
            Method method = clazz.getMethod("inflate", LayoutInflater.class);
            return (V) method.invoke(null, LayoutInflater.from(context));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
public class ClassUtils {
    // type不能直接实例化对象,通过type获取class的类型,然后实例化对象
    public static Class> getRawType(Type type) {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type rawType = parameterizedType.getRawType();
            return (Class) rawType;
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            return Array.newInstance(getRawType(componentType), 0).getClass();
        } else if (type instanceof TypeVariable) {
            return Object.class;
        } else if (type instanceof WildcardType) {
            return getRawType(((WildcardType) type).getUpperBounds()[0]);
        } else {
            String className = type == null ? "null" : type.getClass().getName();
            throw new IllegalArgumentException("Expected a Class, ParameterizedType, or GenericArrayType, but  is of type " + className);
        }
    }
}
五. 原理:
  1. 我们每个xml布局,都会生成对应的Binding类(如下图)
  2. xml文件里,只要有id命名的控件,都会在这里初始化, 生成一个public的成员变量
  3. 正因为是根据id自动生成的, 所以也就不存在View与id不匹配的错误.
  4. Binding类里面的代码都挺简单的, 这里就不贴出来了,太占篇幅了, 有兴趣的可以自己去看看.
Android Butterknife终结者---ViewBinding
ActivityMainBinding.png

补充说明:

  1. 如果某个xml文件不想使用ViewBinding, 怎么办? 在xml根布局添加 tools:viewBindingIgnore=”true”.
  1. 如果使用了DataBinding,就没必要用ViewBinding了,因为DataBinding也生成有对应Binding类,已经包含了ViewBinding的功能.

  2. 我们使用Butterknife的时候, OnClick也是自动生成,现在是要手写吗?
    可以参考我上一篇文章OnClickMe一款自动生成OnClick代码的插件

public class ClassUtils {
    // type不能直接实例化对象,通过type获取class的类型,然后实例化对象
    public static Class> getRawType(Type type) {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type rawType = parameterizedType.getRawType();
            return (Class) rawType;
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            return Array.newInstance(getRawType(componentType), 0).getClass();
        } else if (type instanceof TypeVariable) {
            return Object.class;
        } else if (type instanceof WildcardType) {
            return getRawType(((WildcardType) type).getUpperBounds()[0]);
        } else {
            String className = type == null ? "null" : type.getClass().getName();
            throw new IllegalArgumentException("Expected a Class, ParameterizedType, or GenericArrayType, but  is of type " + className);
        }
    }

}
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。