介绍
我们以前获取xml
中的View
,通常通过findViewById
但是样板代码太多,而且容易引用错误发生崩溃。
后续有很多自动注解方式来帮我们节省时间,其中butterknife
可以算是典型代表了。
但后续Android
升级之后再依赖库中的R
资源并不是find
了。同时当Gradle 5.0
之后Resource ID
将不会是final类型了。ButterKnife
官方也标注了ButterKnife
被废弃使用。(http://jakewharton.github.io/butterknife/ )
针对该需求,Google
推出了ViewBinding
来帮我们解决视图绑定。
而如果我们先接触的DataBinding
那么就很容易将ViewBinding
误解成两个是一样的。但其实他们并不一样。
一个叫做视图绑定(ViewBinding),而另外一个叫做数据绑定(DataBinding)。
1. ViewBinding
要想使用
ViewBinding
必须Android Studio 3.6 Canary 11 以上版本才行。
开启viewBinding
功能:在app/build.gradle
文件中添加:
//老版本 方法一:
android {
...
viewBinding {
enabled = true //关键点
}
}
//新版本 方法二:
android {
...
buildFeatures {
viewBinding true
}
}
以上两个方法都可以,建议用新版本,如果Studio比较老或者gradle版本老,可以用旧版本。
配置完毕后,Gradle
一下整个项目,AndroidStudio
会自动为每个XML
布局文件生成一个绑定类,并不需要我们给xml文件中添加任何代码。(Databinding
就需要我们调整xml
布局 才会生成。)
实例:假设创建了一个activity_main.xml 的布局文件:
代码语言:javascript复制<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@ id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="Detect"
app:layout_constraintStart_toStartOf="parent" />
...
</androidx.constraintlayout.widget.ConstraintLayout>
那么就会自动生成一个ActivityMainBinding
类。
如果我们布局中给View 添加了ID,那么就会在Binding类中存在,如果没有ID则不存在。
1.1 过滤xml转binding类
我们如果有些类不想自动生成对应的binding类,那么只需要在该布局的layout中添加忽略字段即可:tools:viewBindingIgnore="true"
。
实例如下:
代码语言:javascript复制<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:viewBindingIgnore="true" >
...
</androidx.constraintlayout.widget.ConstraintLayout>
1.2 Activity 使用ViewBinding
我们如果开启ViewBinding
之后,但是activity
中仍然是通过:setContentView(R.layout.activity_main)
加载布局的话。那么ViewBinding
功能就没有起作用了。我们需要通过ViewBinding类来注入。
实例 java版本:
代码语言:javascript复制 private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); //添加绑定
}
实例Kotlin版本:
代码语言:javascript复制 private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
而我们如果想使用布局中的View
,可以直接通过binding
对象操作即可:
实例:
代码语言:javascript复制 binding.button.setText("zinyan");
binding.button.setOnClickListener(new View.OnClickListener() {
viewModel.userClicked()
});
我们从此就不用害怕id写错了,或者引用其他布局中的view了。
1.3 Fragment中使用ViewBinding
如果是在Fragment中使用,大体上和Activity差不多。只是需要在onCreateView
方法中进行初始化即可。
实例:Java版
代码语言:javascript复制 private ActivityMainBinding binding;
@Override
public View onCreateView (LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
binding = ActivityMainBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
实例:Kotlin 版
代码语言:javascript复制 private var _binding: ActivityMainBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View? {
_binding = ActivityMainBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
有别于Activity
,我们需要在Fragment
的onDestoryView
中进行清除绑定引用。也就是上面实例中设置为null
。
这是因为Fragment的存活时间比它的视图时间长。否则会出现OOM
异常。
1.4 Adapter中使用ViewBinding
代码语言:javascript复制override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val viewBinding = DemoItemViewBinding.inflate(LayoutInflater.from(parent.context))
val layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
viewBinding.root.layoutParams = layoutParams
return MyViewHolder(viewBinding.root)
}
java版本:
代码语言:javascript复制@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
DemoItemViewBinding binding = DemoItemViewBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new MyViewHolder(binding);
}
2. 总结
与使用 findViewById
相比,视图绑定具有的优点:
- Null 安全:由于视图绑定会创建对视图的直接引用,因此不存在因视图 ID 无效而引发 Null 指针异常的风险。此外,如果视图仅出现在布局的某些配置中,则绑定类中包含其引用的字段会使用
@Nullable
标记。 - 类型安全:每个绑定类中的字段均具有与它们在 XML 文件中引用的视图相匹配的类型。这意味着不存在发生类转换异常的风险。
而相较于DataBinding 的优势在于:
- 更快的编译速度:视图绑定不需要处理注释,因此编译时间更短。
- 易于使用:视图绑定不需要特别标记的 XML 布局文件,因此在应用中采用速度更快。在模块中启用视图绑定后,它会自动应用于该模块的所有布局。
而DataBinding 的优势就在于:布局和数据的双向绑定了。
所以其实我们可以在项目之中同时使用视图绑定和数据绑定。根据具体的业务需求,使用不同的绑定方式获取布局对象
关于DataBinding的相关介绍可以通过这篇文章了解:https://zinyan.com/?p=105