DataBinding是Google Jetpack组件中的一员,该库使用声明性格式(而非程序化地)将布局中的界面组件绑定到应用中的数据源。
使用该库,借助布局文件中的绑定组件,您可以移除 Activity 中的许多界面框架调用,使其维护起来更简单、方便。
还可以提高应用性能(绑定的时候遍历一遍View,而不是findViewById每次遍历查询),并且有助于防止内存泄漏以及避免发生 Null 指针异常
(该库进行了空指针判断)。
android {
dataBinding{
enabled = true
}
}
新版本需要在gradle.properties配置如下:
android.databinding.enableV2=true
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
data>
<LinearLayout
android:orientation="vertical"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".databinding.TestActivity">
<TextView
android:id="@+id/tv_name"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_marginTop="10dp"
android:id="@+id/tv_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
LinearLayout>
layout>
class TestActivity : AppCompatActivity() {
var binding: ActivityTestDataBindingBinding? = null
var student: Student = Student("小李", 23)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(
this,
R.layout.activity_test_data_binding
)
binding?.tvName?.text = "小张"
binding?.tvAge?.text = 22.toString()
}
}
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="student"
type="com.zgj.demojetpackapp.bean.Student"/>
data>
<LinearLayout
android:orientation="vertical"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".databinding.TestActivity">
<TextView
android:id="@+id/tv_name"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{student.name,default = Jack}"/>
<TextView
android:layout_marginTop="10dp"
android:id="@+id/tv_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{student.age+"",default = 16}'/>
LinearLayout>
layout>
Student为一个简单的数据类,如下:
data class Student (var name:String,var age:Int)
class TestActivity : AppCompatActivity() {
var binding: ActivityTestDataBindingBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_test_data_binding )
}
}
class TestActivity : AppCompatActivity() {
var binding: ActivityTestDataBindingBinding? = null
var student: Student = Student("小李", 23)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(
this,
R.layout.activity_test_data_binding
)
binding?.student = student
//两个等效
//binding?.setVariable(BR.student,student)
}
}
运行效果如图:
如果不设置数据或设置数据为null进去
则显示默认值(但不会报空指针)如图(左):,但不是default的值,default的值只在预览视图展示,方便调整布局及预览,如图(右)
可用:
不可用:
this
super
new
显式泛型调用
如下代码演示(包含方法调用,字符串拼接、三目运算符、运算符%等,目前instanceof还没有调好):
<TextView android:layout_width="wrap_content"
android:text="@{String.valueOf(10%3)}"
android:padding="10dp"
android:visibility="@{1>2?View.GONE:View.VISIBLE}"
android:layout_height="wrap_content"/>
<TextView android:layout_width="wrap_content"
android:text='@{"新年"+"快乐"}'
android:padding="10dp"
android:layout_height="wrap_content"/>
<TextView android:layout_width="wrap_content"
android:text='@{"123456".substring(0,2)}'
android:padding="10dp"
android:layout_height="wrap_content"/>
注意:使用View.GONE的时候要注意早标签内导入类如下,不然会报错:
<data>
...
<import type="android.view.View"/>
data>
<TextView android:layout_width="wrap_content"
android:text='@{student.name??"Tom"}'
android:padding="10dp"
android:layout_height="wrap_content"/>
<TextView android:layout_width="wrap_content"
android:text='@{student.name != null?student.name:"Tom"}'
android:padding="10dp"
android:layout_height="wrap_content"/>
binding?.list = listOf("jack","tom","wang")
<LinearLayout android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="wrap_content">
<TextView android:layout_width="wrap_content"
android:text="@{list[0]}"
android:padding="10dp"
android:layout_height="wrap_content"/>
<TextView android:layout_width="wrap_content"
android:text="@{list[1]}"
android:padding="10dp"
android:layout_height="wrap_content"/>
<TextView android:layout_width="wrap_content"
android:text="@{list[2]}"
android:padding="10dp"
android:layout_height="wrap_content"/>
LinearLayout>
执行效果如下(此处超出下标是不会报错的,但是也不会展示内容):
binding?.map = mutableMapOf(Pair("name1","jack"), Pair("name2","tom"),Pair("name3","wang"))
binding?.name1 = "name1"
binding?.name2 = "name2"
binding?.name33 = "name3"
binding?.name4 = "name4"
xml代码:
<data>
...
<variable name="map" type="Map<String,String>"/>
<variable name="name1" type="String"/>
<variable name="name2" type="String"/>
<variable name="name33" type="String"/>
<variable name="name4" type="String"/>
data>
<LinearLayout android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="wrap_content">
<TextView android:layout_width="wrap_content"
android:text="@{map[name1]}"
android:padding="10dp"
android:layout_height="wrap_content"/>
<TextView android:layout_width="wrap_content"
android:text="@{map[name2]}"
android:padding="10dp"
android:layout_height="wrap_content"/>
<TextView android:layout_width="wrap_content"
android:text="@{map.name33}"
android:padding="10dp"
android:layout_height="wrap_content"/>
<TextView android:layout_width="wrap_content"
android:text="@{map[name4]}"
android:padding="10dp"
android:layout_height="wrap_content"/>
LinearLayout>
效果如下(可以看到第一个第二个展示出来了,但是第三个展示不出来,第四个因为没有对应的key-value所以没有展示):
注意: map.name33 直接将name33作为key来取值 map[name2]是将name2对应代码设置的值作为key来取值,两个是不一样的
val sparse:SparseArray<String> = SparseArray<String>()
sparse.put(1,"jack")
sparse.put(2,"tom")
sparse.put(3,"wang")
binding?.sparse = sparse
xml代码,效果:
<data>
<import type="android.util.SparseArray"/>
<variable name="sparse" type="SparseArray<String>"/>
data>
<LinearLayout android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="wrap_content">
<TextView android:layout_width="wrap_content"
android:text="@{sparse[1]}"
android:padding="10dp"
android:layout_height="wrap_content"/>
<TextView android:layout_width="wrap_content"
android:text="@{sparse[2]}"
android:padding="10dp"
android:layout_height="wrap_content"/>
<TextView android:layout_width="wrap_content"
android:text="@{sparse[3]}"
android:padding="10dp"
android:layout_height="wrap_content"/>
<TextView android:layout_width="wrap_content"
android:text="@{sparse[4]}"
android:padding="10dp"
android:layout_height="wrap_content"/>
LinearLayout>
<TextView
android:layout_marginTop="10dp"
android:id="@+id/tv_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{student.age+""}'/>
<TextView
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{student.age+`反单引号`}"/>
<dimen name="dimen_10">10dpdimen>
<dimen name="dimen_30">30dpdimen>
<string name="str_demo1">字符串Demo:%s 结尾string>
<string name="str_demo2">字符串Demo:我有 %d 个香蕉string>
<string name="str_demo3">字符串Demo:我叫 %s ,今年 %d岁了string>
<string name="str_demo4">字符串Demo:我叫 %1$s ,今年 %2$d岁了string>
<string name="str_demo5">字符串Demo:我有 %d个香蕉 ,%d个苹果string>
xml代码如下:
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="paddingResources"
android:padding="@{student.age > 50?@dimen/dimen_10:@dimen/dimen_30}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{@string/str_demo1(`测试字符串`)}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{@string/str_demo2(10)}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{@string/str_demo3(student.name,student.age)}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{@string/str_demo4(student.name,student.age)}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{@string/str_demo5(10,20)}"/>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="student" type="com.zgj.demojetpackapp.bean.Student"/>
<variable name="person" type="com.zgj.demojetpackapp.bean.Person"/>
data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/dimen_10"
android:text="@{`学生:`+student.name+`,`+student.age}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/dimen_10"
android:text="@{`人员:`+person.name+`,`+person.birthday}"/>
LinearLayout>
layout>
主xml如下:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable name="student" type="com.zgj.demojetpackapp.bean.Student"/>
<variable name="person" type="com.zgj.demojetpackapp.bean.Person"/>
data>
<LinearLayout
android:orientation="vertical"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".databinding.TestActivity">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DemoMain"/>
<include layout="@layout/item_data_binding"
bind:student="@{student}"
bind:person="@{person}"/>
LinearLayout>
layout>
kotlin代码如下:
class TestActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding :ActivityTestDataBinding2Binding= DataBindingUtil.setContentView(this,R.layout.activity_test_data_binding2)
binding.student = Student("小张",23)
binding.person = Person("老王","1980-10-10")
}
}
注意:databinding不支持标签(merge标签写的时候就会直接报红,因为)
写的时候注意格式 bind:参数名(item中的参数名)= 值,同时导入命名空间 xmlns:bind="http://schemas.android.com/apk/res-auto",不
然会报红,但是不影响运行,不写bind:也不影响运行,但是建议按规范写。
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="student"
type="com.zgj.demojetpackapp.bean.Student"/>
<variable name="listener"
type="com.zgj.demojetpackapp.databinding.TestActivity.EventListener"/>
data>
<LinearLayout
android:orientation="vertical"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".databinding.TestActivity">
<TextView
android:id="@+id/tv_name"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_marginTop="10dp"
android:id="@+id/tv_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:padding="10dp"
android:text="点击事件1"
android:onClick="@{listener::onClick}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
LinearLayout>
layout>
2、代用代码如下:
class TestActivity : AppCompatActivity() {
var binding: ActivityTestDataBindingBinding? = null
var student: Student = Student("小李", 23)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(
this,
R.layout.activity_test_data_binding
)
binding?.student = student
//重点 千万不要忘记
binding?.listener = EventListener()
}
inner class EventListener{
fun onClick(view: View){
Toast.makeText(this@TestActivity,"点击按钮",Toast.LENGTH_SHORT).show()
}
}
}
此处注意方法名和入参都必须与监听器一致,如点击事件必须入参View等,如onTextChanged必须如下写法:
这样设置之后名称根据EditText的内容变化,实现监听
fun onTextChanged(s:CharSequence,start:Int,before:Int,count:Int){
Log.d(Common.TAG, s.toString())
binding?.student?.name = s.toString()
binding?.student = student
}
xml代码如下:
<EditText android:layout_width="match_parent"
android:text="@{student.name}"
android:onTextChanged="@{listener::onTextChanged}"
android:layout_height="wrap_content"/>
这个比较自由,方法名、参数都可以任意写,代码如下:
fun customClickListener(student: Student) {
Toast.makeText(this@TestActivity, student.name, Toast.LENGTH_SHORT).show()
}
xml代码如下(传入的参数是lambda表达式方式,此处的调用是用.):
<TextView
android:padding="10dp"
android:text="点击事件2"
android:onClick="@{()->listener.customClickListener(student)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
实现的效果如上图,按钮2的点击事件就是这种写法,只是此处的名字已经变了,所以弹出的是及时变化的名字。
1. variable声明xml中用到的变量,import导入需要用到的类
2. 导入的时候如果类名冲突,则可以使用别名alias来处理
3. 标签的class属性可以自定义生成类的类名
源码链接:源码
1、关于Jetpack中的databinding限于篇幅先介绍到这里,关于其他的内容后续第二篇的时候在介绍:
剩余的内容包含:可观察的字段和类、绑定适配器(配合RecycleView)、双向绑定等。
2、关于上述使用databinding的代码,都是经过验证运行没有问题的,如果有如环境等导致的问题欢迎探讨。
3、databinding的使用感受:
1)有一定的方便,数据绑定也较为安全,空指针等异常都得到了避免。
2)效率也有了提升,不用频繁遍历view,而是将view作为变量维护了起来。
3)对于事件的处理要建一个监听类导入进来,另外参数方面也有限制(方法调用),不是很方便。
4)报错不够精确,xml中的错误有时候能检测到(如类没有导入等),有时候提示很模糊(表达式语言有的错误检测不出来),不知道哪里
出错了,调试起来不是太方便。
5)总体的感觉还是不错的,有效的减少了activity的代码量,xml使用起来也比较明确,整体还是建议大家尝试的,至于项目中是否使用,
那就要在使用之后自己做整体的评估了。
3、自勉,多思考,多实践,不要只看网上的结论,不都是正确的,有时候由于各方面的条件限制得出的特定结论是不通用的。
4、关于文档,不要只停留在看的层面上,如上述的表达式语言的使用,官网上也就简单的几句demo,自己看的时候要学会举一反三,
同时也是查漏补缺的过程。
5、自勉,多接触一些自己没有用到的东西,一方面提升自己的技能,另外一方面也是对自己思维的一个开拓,这是很不错的选择。