什么是DataBinding
DataBinding是Google官方发布的一个框架,是mvvm在android上的一种实现。 主要应用于数据绑定,用于降低布局和逻辑的耦合性,使代码逻辑更加清晰,可以直接绑定数据到xml中,并实现自动刷新。 databinding能够省去findviewbyId,大量减少activity的代码,数据能够单向或双向绑定到layout文件中,有助于防止内存泄漏,而且能自动进行空检测以避免空指针异常
DataBinding的使用
基础配置
首先,在我们build.gradle文件添加对DataBinding的支持 如下图 然后来到我们需要支持Databinding的xml布局文件中 将光标定位在第一行最开始 然后按下Alt+Enter键 选择Convert to data binding layout 将我们的xml自动转为Databinding的xml 如下图: 这样,基本的配置就完成了
简单使用
下面我们对一个简单的界面做一个数据的绑定 这个界面只有两个TextView 分别显示姓名和年龄 首先,我们定义一个类
class Person {
var name = "张三"
var age = 18
var address = "北京市朝阳区xxx街道xxx号"
}
然后在支持Databinding的xml里的data标签中,导入这个类
<data>
<variable
name="person"
type="com.example.databindingdemo.data.Person" />
</data>
type这里需要用到类的全路径,name代表给这个类取得别名 使用的时候,也很简单 按照以下格式对TextView的文本进行赋值就好了
android:text="@{person.name}"
然后来到我们的MainActivity代码
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val contentView: ActivityMainBinding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val person = Person()
person.address="修改后地址:上海市xxx街道"
contentView.person = person
}
}
注意这边的setContentView使用的是Databinding的,而不是原来的 最后显示界面如下: 如果要针对int类型 可以做这样一个工具类:
class AgeUtils {
companion object {
@JvmStatic
public fun getAge(age: Int): String {
return "年龄:$age"
}
}
}
然后在data标签里导入
<import
type="com.example.databindingdemo.util.AgeUtils"
/>
使用时:
android:text="@{AgeUtils.getAge(person.age)}"
如果要处理Button按钮之类的Onclick方法 也就是事件的处理 也可以先写一个类
class MyOnclickListener(var context: Context) {
public fun buttonOnclick(view: View) {
Toast.makeText(context, "触发按钮点击事件", Toast.LENGTH_SHORT).show()
}
}
然后加上标签
<variable
name="myOnclickListener"
type="com.example.databindingdemo.util.MyOnclickListener" />
使用时:
android:onClick="@{myOnclickListener.buttonOnclick}"
然后在Mainactivity里
contentView.myOnclickListener = MyOnclickListener(this)
这样点击事件就实现了 可以看到 这样做实现了和页面的解耦 Activity里面只需要关注业务即可
二级页面绑定
有时候页面嵌套过多,我们会需要用到二级页面 一般我们会用include标签进行二级页面的嵌套
layout="@layout/sub1"
针对这个sub1的二级xml文件, 如果也需要用到Databinding, 我们也需要用快捷键先转成Databinding的xml格式 如果这个sub1页面也需要用到Person类 也需要在data标签里配置
<data>
<variable
name="person"
type="com.example.databindingdemo.data.Person" />
</data>
然后在主页xml里的include标签里需要添加一行属性
app:类的别名="@{类的别名}"
这个类的别名就是variable标签的name属性 代表把对应的类传递到sub1的xml中 比如这样:
app:person="@{person}"
这样配置后,sub1里面才能正常使用Person类中的属性 注意,如果是标签,则不需要进行这样的传递配置 直接在sub1的xml里面配置import标签就好了 最终是这样:
<data>
<variable
name="idol"
type="com.example.jetpack_databinding.main1.Idol" />
<import
type="com.example.jetpack_databinding.main1.StarUtils"/>
</data>
加载图片
现在来介绍一下如何去用Databinding加载一张图片 首先我们先写好这么一个类
class ImageViewBindingAdapter {
companion object {
@BindingAdapter("image")
@JvmStatic
fun setImage(imageView: ImageView, url: String) {
if (!TextUtils.isEmpty(url)) {
Log.d("ImageViewBindingAdapter", "url:${url}")
} else {
Log.d("ImageViewBindingAdapter", "url为空")
}
}
}
}
注意,这和setImage方法上面带了一个@BindingAdapter的注解 我们在注解里写上自己要写的关键字 来到xml,在data标签里自定义一个variable标签 name随便起,type是字段的类型 这里我们要加载网络图片,就设置type为字符串类型,也就是图片的url
<variable
name="networkImage"
type="String" />
然后在Imageview里这样配置一个属性
app:image="@{networkImage}"
这里的image就是上面的注解 networkImage就是自定义variable标签的name 最后来到Activity页面
class MainActivity3 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main3)
val activityMain3Bind =
DataBindingUtil.setContentView<ActivityMain3Binding>(this, R.layout.activity_main3)
activityMain3Bind.networkImage="图片的url"
}
}
在Activity页面,我们对networkImage进行赋值即可 整个流程,我们注意的就是注解+自定义的variable属性要和Imageview里面的标签对应起来 同样的,如果要加载本地图片 我们也可以自定义一个方法,加上注解,方法参数内传入int类型即可
@BindingAdapter("image")
@JvmStatic
fun setLocalImage(imageView: ImageView, resId: Int) {
imageView.setImageResource(resId)
}
最后,如果我们要动态的判断 有网络时加载网络图片,否则加载本地图片,我们可以这样定义我们的方法
@BindingAdapter(value = ["image", "defaultImageResource"], requireAll = false)
@JvmStatic
fun setImage(imageView: ImageView, url: String?, resId: Int?) {
if (!TextUtils.isEmpty(url)) {
Log.d("ImageViewBindingAdapter", "url:${url}")
Glide.with(imageView.context).load(url).into(imageView)
} else {
Log.d("ImageViewBindingAdapter", "url为空2222")
resId?.let { imageView.setImageResource(it) }
}
}
看到这个方法的注解里多了一个requireAll 属性 true代表Imageview里同时定义了image和defaultImageResource时才会进入这个方法 false代表Imageview里只要定义了image和defaultImageResource其中的一个,就会进入这个方法 这也是方便方法的重载 然后我们定义data标签
<data>
<variable
name="networkImage"
type="String" />
<variable
name="localImage"
type="int" />
</data>
在Imageview里这样写:
app:image="@{networkImage}"
app:defaultImageResource="@{localImage}"
Activity里对两个标签属性进行赋值即可
class MainActivity3 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main3)
val activityMain3Bind =
DataBindingUtil.setContentView<ActivityMain3Binding>(this, R.layout.activity_main3)
activityMain3Bind.networkImage="图片的url"
activityMain3Bind.localImage= R.drawable.img1
}
}
双向绑定
第一种做法
第二种做法
RecyclerView的绑定
DataBinding+ViewModel+LiveData
|