02 Jetpack-ViewBinding
2021-11-22 本文已影响0人
凤邪摩羯
1 前言
在Android开发,代码里获取View一般是使用findViewById()获取目标布局文件里的指定View。但是这样使用会有大量代码重复工作并且有空指针危险。为了减少重复工作有很多大神都八仙过海各显神通,但是这些神通多多少少都有缺点。
- 大名鼎鼎的黄油刀bufferknife,缺点增加了编译速度(因为原理是它需要生成一份对应查找View的代码),并且需要时刻更新最新版本否则AndroidStudio更新后可能会出现无法编译的问题。(另外bufferknife与ViewBinding是冲突的)
- DataBinding,缺点更明显,需要更多的xml编写工作量,并且一不小心会延伸到一些邪恶的用法,那就是在xml写逻辑判断,甚至在xml增加一些业务功能。这对代码维护是恐怖的,因为xml逻辑的可读性可比纯Java代码差多了。并且如果混乱到2头都写逻辑判断,维护起来十分痛苦。
- AndroidStudio的插件功能自动生成代码,比如LayoutCreator,减少了工作量但是并没有减少代码的冗余,代码看起来一样是不简洁的。
google在AndroidStudio 3.6 版本后推出了ViewBinding,一方面可以让代码更加简洁并且提高编译速度防止空指针。另一方面AndroidStudio是支持ViewBinding进行关联互动的,所以让你在Java代码与xml之间的跳转更方便。
2 前提条件
AndroidStudio 需要更新到3.6版本以上。
在build.gradle文件里增加下面的代码,开启viewBinding
android {
//略...
buildFeatures{
viewBinding = true
}
}
3 各处简单的使用Demo
首先你需要知道一个关键点,在启用ViewBinding后。每一个layout文件都会自动生成一份Java类。它会自动根据下划线进行驼峰命名。比如一个叫 activity_mian_demo.xml 的布局文件,它对应自动生成的类叫ActivityMianDemoBinding。这就意味着我们可以在任何需要导入布局的地方都使用ViewBinding。
- Activity里:
public class MainActivity extends AppCompatActivity {
private ActivityMianDemoBinding mBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = ActivityMianDemoBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
mBinding.btn1.setText("这是按键1");
mBinding.btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
}
}
- Fragment里:
public class FragmentDemo extends Fragment {
private FragmentDemoBinding mBinding;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mBinding = FragmentDemoBinding.inflate(getLayoutInflater());
return mBinding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mBinding.textView.setText("textView");
}
}
- 在Dialog里:
public class DemoDialog extends Dialog {
private DialogDemoBinding mBinding;
public DemoDialog(@NonNull Context context) {
super(context);
mBinding = DialogDemoBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
mBinding.textView.setText("hello");
}
}
- Adapter里:
public class DemoAdapter extends RecyclerView.Adapter<DemoAdapter.ViewHolder> {
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(ItemDemoBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.binding.textView2.setText("demo");
}
@Override
public int getItemCount() {
return 0;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
ItemDemoBinding binding;
public ViewHolder(@NonNull ItemDemoBinding itemDemoBinding) {
super(itemDemoBinding.getRoot());
this.binding = itemDemoBinding;
}
}
}
4 ViewBinding封装基类(BaseActivity,BaseFragment)
- BaseActivity:
public class BaseActivity<T extends ViewBinding> extends AppCompatActivity {
protected T binding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Type superclass = getClass().getGenericSuperclass();
Class<?> aClass = (Class<?>) ((ParameterizedType) superclass).getActualTypeArguments()[0];
try {
Method method = aClass.getDeclaredMethod("inflate", LayoutInflater.class);
binding = (T) method.invoke(null, getLayoutInflater());
setContentView(binding.getRoot());
} catch (NoSuchMethodException | IllegalAccessException| InvocationTargetException e) {
e.printStackTrace();
}
}
}
- 使用
public class MainActivity extends BaseActivity<ActivityMainBinding> {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// binding.button1 ……
}
}
- BaseFragment
public class BaseFragment<T extends ViewBinding> extends Fragment {
protected T binding;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Type superclass = getClass().getGenericSuperclass();
Class<?> aClass = (Class<?>) ((ParameterizedType) superclass).getActualTypeArguments()[0];
try {
Method method = aClass.getDeclaredMethod("inflate", LayoutInflater.class,ViewGroup.class,boolean.class);
binding = (T) method.invoke(null, getLayoutInflater(),container,false);
} catch (NoSuchMethodException | IllegalAccessException| InvocationTargetException e) {
e.printStackTrace();
}
return binding.getRoot();
}
}
- 使用
public class BlankFragment extends BaseFragment<FragmentBlankBinding> {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//binding.textView ……
}
}
- kotlin写法
open class BaseActivity<VB:ViewBinding> :AppCompatActivity() {
protected val binding: VB by lazy {
//使用反射得到viewbinding的class
val type = javaClass.genericSuperclass as ParameterizedType
val aClass = type.actualTypeArguments[0] as Class<*>
val method = aClass.getDeclaredMethod("inflate", LayoutInflater::class.java)
method.invoke(null, layoutInflater) as VB
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
}
}
open class BaseFragment<VB:ViewBinding>:Fragment(){
lateinit var binding: VB
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val type = javaClass.genericSuperclass as ParameterizedType
val aClass = type.actualTypeArguments[0] as Class<*>
val method = aClass.getDeclaredMethod("inflate", LayoutInflater::class.java,ViewGroup::class.java,Boolean::class.java)
binding = method.invoke(null,layoutInflater,container,false) as VB
return binding.root
}
}
- 注意事项
- 这样方式定义的基类调用时候,只能是直属父类
- 如果BaseActivity有多层继承关系,需要将该方式定义在最底层