Android Butterknife終結者---ViewBinding

一. 關於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更好用, 所以我覺得很有必要去了解一下.

二. ViewBinding基本使用

1. app/build.gradle下

android {
    viewBinding {
        enabled = 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<T extends ViewBinding> 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<ActivityMainBinding>{
    @Override
    protected void init() {
        binding.tvText.setText("1234567890");
    }
}
四. RecyclerView中的使用

關鍵點: ViewHolder對象維護一個Binding泛型對象就可以了. 具體Adapter怎麼封裝看個人了.

public class BaseViewHolder<T extends ViewBinding> extends RecyclerView.ViewHolder{
    T viewbinding;
    public BaseViewHolder(@NonNull T viewbinding) {
        super(viewbinding.getRoot());
        this.viewbinding = viewbinding;
    }
}
五. 原理:
  1. 我們每個xml佈局,都會生成對應的Binding類(如下圖)
  2. xml文件裏,只要有id命名的控件,都會在這裏初始化, 生成一個public的成員變量
  3. 正因爲是根據id自動生成的, 所以也就不存在View與id不匹配的錯誤.
  4. Binding類裏面的代碼都挺簡單的, 這裏就不貼出來了,太佔篇幅了, 有興趣的可以自己去看看.

補充說明:

  1. 如果某個xml文件不想使用ViewBinding, 怎麼辦? 在xml根佈局添加 tools:viewBindingIgnore="true".
<LinearLayout 
   ...
    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 <" + type + "> is of type " + className);
        }
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章