什么是DataBinding
Data Binding,顾名思义,数据绑定,是Google对MVVM在Android上的一种实现,可以直接绑定数据到xml中,并实现自动刷新。现在最新的版本还支持双向绑定,尽管使用场景不是那么多。
Data Binding可以提升开发效率(节省很多以往需要手写的java代码),性能高(甚至超越手写代码),功能强(强大的表达式支持)。
用途
去掉Activities & Fragments内的大部分UI代码(setOnClickListener, setText, findViewById, etc.)
XML变成UI的唯一真实来源
减少定义view id的主要用途(数据绑定直接发生在xml)
优势
- UI代码放到了xml中,布局和数据更紧密
- 性能超过手写代码
- 保证执行在主线程
劣势
- IDE支持还不那么完善(提示、表达式)
- 报错信息不那么直接
- 重构支持不好(xml中进行重构,java代码不会自动修改)
DataBinding的使用
Android Studio 的兼容版本,需要 1.3 及以上的版本.
在Android Studio上使用,需要在module级别的build.gradle上添加对DataBinding的支持:
android {
...
dataBinding {
enabled = true
}
}
-
数据绑定布局文件XML
数据绑定的xml和我们以前经常写的xml稍有不同,从布局的跟标记开始,依次是根布局layout,数据元素data节点,视图元素,例如:
<?xml version="1.0" encoding="utf-8"?>
<layout 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"
>
<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>
数据中的用户变量描述此布局中可能使用的属性:
<variable name="user" type="com.example.User"/>
布局中的表达式使用 "@{}" 语法在属性中写入,在这里,TextView 的文本设置为用户 FirstName 属性:
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"/>
-
数据对象User:
class UserModel {
var lastName: String? = null
var firstName: String? = null
···
}
-
数据绑定
生成实例,会有一定的规则,layout通过文件名生成,View通过id生成,通过dataBinding.setVariable(Variable variable);来实现数据的绑定。
默认情况下,绑定类将根据 layout 文件的名称生成,首字母大写的命名规范,并添加 "Binding" 后缀,上述的布局文件是main_activity.xml,所以生成类是 MainActivityBinding, 该类将布局属性(例如 User 变量)的所有绑定保存到布局视图中,并知道如何为绑定表达式赋值,创建最简单的方法是在 inflating 时绑定,如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
User user = new User("Test", "User");
binding.setUser(user);
}
如果你是在 RecyclerView adapter 中使用 Data Binding 时,你可能使用:
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): AdapterBindingViewHolder<*> {
var binding: ViewDataBinding = DataBindingUtil.inflate(mLayoutInflater, R.layout.book_recycle_item, parent, false)
return AdapterBindingViewHolder(binding)
}
通过自定义类名,这样就可以避开上面的规则
<data class="CustomBinding"></data> //在app_package/databinding下生成CustomBinding;
<data class=".CustomBinding"></data> //在app_package下生成CustomBinding;
<data class="com.example.CustomBinding"></data> // 明确指定包名和类名。
-
事件处理
数据绑定允许你编写表达式处理,如 onClick 事件
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.onClickBindalbeTest()}"
android:text="click bindalbe test"
/>
可以使用多个参数的 Lambda 表达式:
public class Presenter {
public void onSaveClick(View view, Task task){}
}
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />
还可以使用三元表达式:
android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"
-
XML data的使用
可以在数据元素内使用零个或多个导入元素。这些可以参考在你的布局文件的类,就像在 java:
<data>
<import type="android.view.View"/>
</data>
现在,视图可以在绑定表达式中使用:
<TextView
android:text="@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
当有类名冲突时,其中一个类可以定义一个别名为“alias:”,如下:
<import type="android.view.View"/>
<import type="com.example.real.estate.View"
alias="Vista"/>
在表达式中引用静态字段和方法时也可以使用导入类型:
<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"/>
可以在数据元素内使用任意数量的变量元素。每个变量元素描述可以在布局文件中用于绑定表达式中的布局的属性:
<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>
还可以自定义绑定类名
通过调整数据元素的类属性可以将绑定类重命名或放置在不同的包中。例如:
<data class="ContactItem">
...
</data>
这个生成绑定类中的模块封装在数据绑定包 ContactItem,如果类产生在不同的包中的模块封装内,它可能会加 ".",如下:
<data class=".ContactItem">
...
</data>
在这种情况下,ContactItem 直接在模块封装生成,如果提供完整的包,任何包可以使用:
<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}"/>
</LinearLayout>
</layout>
-
表达式
支持的运算符:
<ul>
<li>数学运算符: + - / * %</li>
<li>字符串拼接: +</li>
<li>逻辑运算符: && ||</li>
<li>二进制: & | ^</li>
<li>一元运算符: +</li>
<li>位运算符: >> >>> <<</li>
<li>比较: == > < >= <=</li>
<li>instanceof</li>
<li>()</li>
<li>数据类型: character, String, numeric, null</li>
<li>类型转换(ClassCast)</li>
<li>方法回调(Method calls)</li>
<li>数据属性</li>
<li>数组:[]</li>
<li>三元操作符:?</li>
</ul>
使用如下:
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'
android:text="@{user.displayName ?? user.lastName}"
...
可以使用正常语法的表达式访问资源:
<string name="nameFormat">%s - %s</string>
android:text="@{@string/nameFormat(user.displayName , user.lastName)}"
也可以直接数据处理
android:text="@{Integer.toString(user.age)}"
-
Observable Objects
目前所提供的ObservableField有:
Observable类型 | 对应原类型 |
---|---|
ObservableArrayList | ArrayList |
ObservableArrayMap | ArrayMap |
ObservableBoolean | boolean |
ObservableByte | byte |
ObservableChar | char |
ObservableFloat | float |
ObservableDouble | double |
ObservableLong | long |
ObservableInt | int |
ObservableParcelable<T extends Parcelable> | <T extends Parcelable> |
ObservableField<T> | <T> |
Observable是提供添加移除监听的一个java接口,DataBinding基于此接口提供了一个基础类BaseObserable,我们可以这样使用它,通过Bindale注解绑定一个getter,当data属性发生改变在setter中发出通知,这样就实现了响应
/**
* 继承BaseObservable
* set方法notifyPropertyChanged
* get方法@Bindable
*/
class BindalbeTestModel: BaseObservable() {
var testStr: String? = null
set(value) {
field = value
notifyPropertyChanged(com.ghp.demo.databindingdemoproject.BR.testStr)
}
@Bindable
get() {
return field?:""
}
}
根据google为我们提供了一些Obserable类,还可以这样:
public static class UserModel {
public final ObservableField<String> firstName =
new ObservableField<>();
public final ObservableField<String> lastName =
new ObservableField<>();
public final ObservableInt age = new ObservableInt();
}
看这样的UserModel会不会觉得烦琐,其实还可以这样
class UserModel {
var firstName: String? = null
var userAge: Int = 0
var lastName: String? = null
}
var user = ObservableField<UserModel>()
/**
* ObservableField可以包裹变量非Observable的model
* model初始化后,更改值,必须要notifyChange刷新UI
*/
fun onclickObservableFieldUser() {
user.get().firstName = "ghp"
user.notifyChange()
}
ObservableArrayMap:
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
在xml中使用:
<data>
<import type="android.databinding.ObservableMap"/>
<variable name="user" type="ObservableMap<String, Object>"/>
</data>
…
<TextView
android:text='@{user["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text='@{String.valueOf(1 + (Integer)user["age"])}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
ObservableArrayList:
ObservableArrayList<Object> user = new ObservableArrayList<>();
user.add("Google");
user.add("Inc.");
user.add(17);
xml使用:
<data>
<import type="android.databinding.ObservableList"/>
<import type="com.example.my.app.Fields"/>
<variable name="user" type="ObservableList<Object>"/>
</data>
…
<TextView
android:text='@{user[Fields.LAST_NAME]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
参考:
DataBinding使用全面详解
Android DataBinding 详解
从零开始的Android新项目7 - Data Binding入门篇
网友评论