数据绑定(data Binding)库提供了灵活性和广泛的兼容性-这是一个支持库,所以你可以在所有的Android平台版本上甚至于是Android 2.1(API等级7+)。但是他需要Android Studio的版本至少在1.3Bate之上甚至更高。
设置工作环境:
更新你的gradle配置文件:
dependencies {
classpath "com.android.tools.build:gradle:1.3.0-beta1"
classpath "com.android.databinding:dataBinder:1.0-rc0"
}
}
然后确保jcenter是在库列表中。
allprojects {
repositories {
jcenter()
}
}
每一个module如果涉及到要使用的地方都要添加代码如下:
apply plugin: ‘com.android.application'
apply plugin: 'com.android.databinding'
data binding 的布局文件
data binding的布局文件和普通的布局文件略有不同,区别在最开始的根节点是layout下面紧跟的是data 以及view 节点,例子如下
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="user" type="com.example.User"/>
</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:text="@{user.firstName}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"/>
</LinearLayout>
</layout>
用户变量中的data描述了可以在本布局中所使用的属性。
<variable name="user" type="com.example.User"/>布局中的表达式都写在
@ {}
语法里。在这里,TextView的文本设置为用户的FirstName属性:
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}"/>
Data 对象
public class User { public final String firstName; public final String lastName; public User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } }这种类型的对象数据是绝对不会改变的。它的对象数据初始化之后就再也不会改变了。此外,也可以使用一个JavaBeans对象:
public class User { private final String firstName; private final String lastName; public User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return this.firstName; } public String getLastName() { return this.lastName; } }从数据绑定的角度来看,这两个类是等价的。表达式
@ {} user.firstName
用于TextView中的
android:text
属性将访问
firstName
的前级和
getFirstName()
方法。
数据绑定
一般情况下,一个绑定类将基于布局文件的名称来生成,将其转换为电脑指令并且绑定它。上述布局文件是activity_main.xml
所以生成的类是 ActivityMainBinding
。此类包含从布局属性的所有绑定(如用户
变量)来布局的建议,并知道如何创建绑定分配的绑定表达式.最简单的方式是当他做反射的时候对他做代码绑定:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity); User user = new User("Test", "User"); binding.setUser(user); }接下来你就可以运行你的APP看他的UI,另外你还可以通过以下方式得到一个view:
MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());如果你想在listview或者recyclerciew的adapter里面实现数据绑定,你最好是使用以下方式:
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false); //or ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
layout文件的细节描述
Import(导入)
你可以在data节点下面使用Import子节点,这可以允许你更加方便简单的在你的layout文件里面使用你的控件引用:
<data> <import type="android.view.View"/> </data>
现在,View就可以在你的视图表达式里面使用了。
<TextView android:text="@{user.lastName}" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>当你导入的View名称有重复的冲突时,你可以使用alins节点。
<import type="android.view.View"/> <import type="com.example.real.estate.View" alias="Vista"/>现在,在你的布局文件中,vista 就可以被用来引用 com.example.real.estate.view, 而且view 也可以被用来引用 android.view.view。导入的类型可以被当做引用类型变量和表达式使用
<data> <import type="com.example.User"/> <import type="java.util.List"/> <variable name="user" type="User"/> <variable name="userList" type="List<User>"/> </data>
PS:Android studio 对于自动补全导入这个功能还没有完全支持,但是你的应用程序还是能够完全正常的编译,而且你也能够使用完全规范的变量命名来解决IDE的工作问题。
<TextView android:text="@{((User)(user.connection)).lastName}" android:layout_width="wrap_content" android:layout_height="wrap_content"/>在表达式里面导入的类型也能够引用静态域和方法。就像Java里面自动导入java.lang.*一样
<data> <import type="com.example.MyStringUtils"/> <variable name="user" type="com.example.User"/> </data> … <TextView android:text="@{MyStringUtils.capitalize(user.lastName)}" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
变量
大部分的Variable节点都被包含在Data节点里面,每一个Variable节点在layout布局文件里面都被描述为设置在layout里面的用于绑定表达式的一个属性。
<data> <import type="android.graphics.drawable.Drawable"/> <variable name="user" type="com.example.User"/> <variable name="image" type="Drawable"/> <variable name="note" type="String"/> </data>变量是在编译的时候被检查的,所以,如果一个变量实现了Observable对象或者他就是一个Observable对象的集合那么就可以在type里面体现反射出来,如果变量是一个基类或者是一个接口那么他就不能实现Observable接口,那么这个变量也就不能被观察到。
当我们有多个不同的布局文件去适配不同的配置的时候(比如风景和人物),变量就必须被混合起来,我们必须保证不同的布局文件之间变量的定义不能有冲突。
生成的绑定类将会有setter和getter方法来对每一个变量进行描述。变量在被赋值之前(调用setter方法之前)都会使用JAVA自带默认值,比如int类型0,boolean类型false,引用类型null。
自定义绑定类名
默认的来说,一个绑定类名的生成是基于对应的布局文件,然后首字母大写,去掉所有的下划线以及后续的大写,以binding作为后缀结尾,这个类将会出现在你的模块下的绑定包下面,比如:一个contact_item.xmlcom.example.my.app 生成的绑定文件名就会是ContactItemBinding,如果模块包名是com.example.my.app,那么他出现的位置就是在com.example.my.app.databinding。
可以通过调整data节点下面的class属性来重命名类名或者改变包的位置
<data class="ContactItem">
...
</data>
如果生成的类应该生成在不同的模块包,那么就可以通过加前缀“.”的方式来区分
<data class=".ContactItem">
...
</data>
在这种情况下,如果所有的包下面的类都想使用,那就可以用补全包名的方式来实现
<data class="com.example.ContactItem"> ... </data>包含
变量可能会从一个使用应用程序命名空间和变量名称写在属性里面的嵌套布局传递到一个被包含的布局来绑定:
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bind="http://schemas.android.com/apk/res-auto"> <data> <variable name="user" type="com.example.User"/> </data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <include layout="@layout/name" bind:user="@{user}"/> <include layout="@layout/contact" bind:user="@{user}"/> </LinearLayout> </layout>
在这里,name.xml 和
contact.xml
两个文件里面只能有一个user变量。
表达式语言
基本特性
这里的EL有点类似JAVA的表达式,以下是相同点:
算术运算 + - / * %
字符串连接 +
逻辑表达 && ||
多目符 &
|
^
单目符 + - ! ~
位移符 >> >>> <<
比较运算符 == > < >= <=
好吧 基本都是一样的。。。(不做翻译了)
举个栗子!
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'
缺少的运算符:
this,super,new。
空连接运算符
空连接运算符(??
)的作用是 如果运算符左边不为空则使用左边的表达式,如果左边为空则使用右边的表达式:
android:text="@{user.displayName ?? user.lastName}"
可以等价于条件运算符
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
属性引用
当一个表达式引用了一个类的属性之后,他仍然使用相同的格式在 字段,getters 和 observableFields。
android:text="@{user.lastName}"
避免空指针异常
生成数据绑定代码的时候会自动检测空指针避免出现空指针异常。比如,在表达式@{user.name}里,如果user是null的话 ,user.name将会默认的被分配为null值,如果要引用user.age 而且 age的类型是Int的 那默认值则是0。
集合
常用的集合:arrays, lists, sparse lists, and maps,为了方便使用都可以用[ ] 访问。
<data> <import type="android.util.SparseArray"/> <import type="java.util.Map"/> <import type="java.util.List"/> <variable name="list" type="List<String>"/> <variable name="sparse" type="SparseArray<String>"/> <variable name="map" type="Map<String, String>"/> <variable name="index" type="int"/> <variable name="key" type="String"/> </data> … android:text="@{list[index]}" … android:text="@{sparse[index]}" … android:text="@{map[key]}"
字符串
当使用单引号包含属性值的时候,那在表达式里面就可以容易地使用双引号:
android:text='@{map["firstName"]}'
也可以使用双引号包含属性值,当你这么做的时候,字符串就需要被单引号包围,或者是被
"包围。
android:text="@{map[`firstName`}"
android:text="@{map["firstName"]}"
资源
在正常的语法里面,也可以允许资源作为表达式的一部分:
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
格式化字符串和复数形式都可以通过提供的参数来判断:
android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"
当复数有很多个参数的时候,全部的参数都可以被通过。
Have an orange Have %d oranges android:text="@{@plurals/orange(orangeCount, orangeCount)}"有一些资源需要显性形式来判断
Type | Normal Reference | Expression Reference |
---|---|---|
String[] | @array | @stringArray |
int[] | @array | @intArray |
TypedArray | @array | @typedArray |
Animator | @animator | @animator |
StateListAnimator | @animator | @stateListAnimator |
color int | @color | @color |
ColorStateList | @color | @colorStateList |