DataBinding 是 Google 在 Jetpack 中推出的一款数据绑定的支持库,他的目的是一个帮助我们实现数据和UI绑定,并可以进行双向绑定。
1、他其实和ViewBinding一样可以帮我们省去findViewById()代码,还有获取布局控件类型不匹配带来的运行期崩溃。(没学ViewBinding的建议去看一下我的上一篇文章哦ViewBinding)
2、DataBinding非常适合用于MVVM模式中充当View和ViewModel的双向绑定的工具
3、因为他编译期间需要对annotation(注解)处理,所以他会带来一定的编译时间并增加包体积(相对来说ViewModel会更快更小)
4、Databinding支持数据双向绑定,这样也意味着,定位问题和管理起来更复杂。
总结来说:如果你只想绑定布局简化findViewById()代码的话你就用ViewBinding绑定布局就可以了。如果你需要建立MVVM架构,或者需要双向数据绑定,那么用DataBinding更好一些(其实就是Google为了实现MVVM结构而实现的,懂了吧)。
需要在模块的build.gradle进行如下配置(和ViewBinding一样)
android {
...
buildFeatures {
dataBinding true
}
}
DataBinding 是一个 support 包,添加完后上面的脚本后会发现工程的的External Libraries中多了四个aar包。分别是adapters、commen、runtime和ViewBinding。(被你们发现了其实他跟ViewBinding是包含关系)
在DataBinding绑定布局需要对我们的布局作出一些改变,一起来看看吧。
首先选中根布局,然后按住 「Alt + 回车键」,点击 「Convert to data binding layout」 即可生成就可以生成 DataBinding 需要的布局规则 我们可以看到,操作和代码如下:
布局为data_bind_demo.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
//绑定数据,先别着急,后面会说怎么去绑定数据
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="30dp"
android:text="90000000"
tools:ignore="MissingConstraints" />
</LinearLayout>
</layout>
这时候你再去Build我们的项目,在相应的文件夹下就能发现生成的Bind类,它与XML布局文件的名字有对应关系,具体的联系就是,以XML布局文件为准,去掉下划线,所有单次以大驼峰的形式按顺序拼接,最后再加上Binding。比如,我的XML布局文件名是data_bind_demo.xml,生成的Binding类名就是DataBindDemoBinding.java。
class MainDataActivity : AppCompatActivity() {
lateinit var mainDataActivity: DataBindDemoBinding
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
//使用DataBindingUtil将布局与activity进行绑定
mainDataActivity = DataBindingUtil.setContentView(this, R.layout.data_bind_demo)
}
}
这时候我们就将布局和我们的Activity绑定了,万事俱备只欠东风,那么开始我们的数据通知了。
data class UserData(var userName: String = "", var age: Int = 0) {
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<!--如果我们导入的包名字有冲突,可以在import标签内为其设置一个别名-->
<!--
<import type="com.example.model.UserData" alias="UserData"/>
<import type="com.example.Utils.UserData" alias="UserData1"/>-->
<import type="com.example.dataBind.UserData"/> //这里
<variable
name="user"
type="UserData" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="30dp"
android:text="@{user.userName}" //这里
tools:ignore="MissingConstraints" />
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="30dp"
android:text="@{user.age, default = 99999}"。 //这里
tools:ignore="MissingConstraints" />
</LinearLayout>
</layout>
可以看到我们在data中新增了标签来绑定我们的数据,他们都代表了什么呢:
import:可以直接将实体import 进来,这样type就不用每次都指明整个包名路径了。
variable:声明实体变量。
type:指定我们绑定的实体。
name:绑定实体的别名。
@{user.userName}: 使用我们UserData的userName属性。
@{user.age, default = 99999}:使用我们UserData的age属性,默认值为99999。
直接利用bind给绑定在布局的对象赋值就可以。
class MainDataActivity : AppCompatActivity() {
lateinit var mainDataActivity: DataBindDemoBinding
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
//使用DataBindingUtil将布局与activity进行绑定
mainDataActivity = DataBindingUtil.setContentView(this, R.layout.data_bind_demo)
val userData = UserData("hello", 93788)
//给布局文件中的数据元素赋值
mainDataActivity.user = userData
}
}
运行一下可以看到,页面上的TextView展示为hello和93788。
注意:这时候大家可能有疑问,看起来麻烦的很,却只做了绑定。
回答:所以说如果只是绑定,大家可以考虑ViewBinding,而DataBinding大多数是用在做双向的数据监听绑定,那么怎么做双向监听绑定怎么做呢,这里我先预热一下:一种实体类继承BaseObserve,另一种就是jetpack中的ViewModel(大多数,毕竟人家是一家人全家桶)。
继续:我先把事件绑定和布局与数据绑定的情况说完。后续会更新BaseObserve的双向绑定。并且对比一下全家桶ViewModel+LiveData+DataBinding的双向绑定。(可以点进我的主页看看更新了没,先看完布局绑定的所有情况吧。)
同理我们首先创建一个工具类,在类中定义响应事件的方法。
class TextViewClickListener {
public fun onClick(view: View) {
Log.d("TextViewClickListener","onClick...")
}
}
然后在布局文件中添加点击事件的代码,如下所示。(还在上面的基础上改)
在Text中加入了android:onClick=“@{onViewClick.onClick}” 和上面的数据绑定一样
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
.....
<import type="com.example.dataBind.TextViewClickListener"/>
<variable
name="onViewClick"
type="TextViewClickListener" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
....
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="30dp"
android:text="@{user.age, default = 9999999}"
android:onClick="@{onViewClick.onClick}" //这里这里
tools:ignore="MissingConstraints" />
</LinearLayout>
</layout>
如果一个布局文件中使用了DataBinding,同时也使用了include标签,那么如何使用include标签引入的布局文件中中的数据呢。
此时,我们需要在同一级页面的include标签中,通过命名空间xmlns:android来引入布局变量User,将数据对象传递给二级页面,如下所示。
<include
layout="@layout/data_bind_demo_include"
xmlns:app="@{user}" />
布局表达式中直接传入页面变量user,include标签属性值可以任意取名,但是要注意的是,在二级页面的variable标签中的name属性,必须与一级页面中的include标签属性名一致。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.example.dataBind.UserData"/>
<variable
name="user"
type="UserData" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.userName}"
android:gravity="center"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
使用RcyclerView,就需要用到Adapter,在Adapter中实例化Item布局,然后将List中的数据绑定到布局中,而DataBinding就可以帮助开发者实例化布局并绑定数据。
首先,我们编写Adapter的item布局,在item布局中使用DataBinding将User数据进行绑定,item_user.xml的代码如下所示。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.example.dataBind.UserData" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.userName}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.age}" />
</LinearLayout>
</layout>
接下来,编写Adapter类业务处理,UserAdapter的代码如下所示。
class UserAdapter(mDataList: List<UserData>) :
RecyclerView.Adapter<UserAdapter.ViewHolder?>() {
private val mDataList: List<UserData>
init {
this.mDataList = mDataList
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ItemUserBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.item_user,
parent,
false
)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val model: UserData = mDataList[position]
holder.binding.user = model
}
override fun getItemCount(): Int {
return if (mDataList.isNotEmpty()) {
mDataList.size
} else 0
}
inner class ViewHolder(binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) {
var binding: ItemUserBinding
init {
this.binding = binding as ItemUserBinding
}
}
}
然后,按照RecyclerView的基本使用方法,我们在MainActivity添加一些测试数据,并将它和UserAdapter进行绑定,代码如下。
class MainActivity : AppCompatActivity() {
private var activityMainBinding: ActivityMainBinding? = null
private var userModels: MutableList<UserData>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
initData()
initRecyclerView()
}
private fun initData() {
userModels = ArrayList<UserData>()
for (i in 0..9) {
val userModel = UserData("zhangsan" + 1, "beijing$i", "age$i")
userModels!!.add(userModel)
}
}
private fun initRecyclerView() {
val layoutManager = LinearLayoutManager(this)
layoutManager.orientation = LinearLayoutManager.VERTICAL
activityMainBinding.recycle.setLayoutManager(layoutManager)
activityMainBinding.recycle.addItemDecoration(
DividerItemDecoration(
this,
DividerItemDecoration.VERTICAL
)
)
val adapter = UserAdapter(userModels!!)
activityMainBinding.recycle.setAdapter(adapter)
}
}
好了,这大概就是关于DataBinding的所有内容了。
下一篇文章我会讲解DataBinding和ViewData的区别,之后马上会更新ViewModel和LiveData。最后就是我们的MVVM主要架构ViewModel+LiveData+DataBinding双向绑定。坚持就是胜利!!!!!