本文是https://github.com/LyndonChin/MasteringAndroidDataBinding的学习笔记
DataBinding简要##
准备###
保证Gradle插件版本不低于1.5.0-alpha1
修改对应模块的build.gradle:
dataBinding{
enabled true
}
布局文件###
最外层根节点变为layout
,新增节点data
用来存放页面可能用的数据以及方法。
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
</data>
<!--原先的根节点(Root Element)-->
<LinearLayout>
....
</LinearLayout>
</layout>
数据对象###
需要注意的是在数据对象中必须实现每个属性的get和set方法。
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 firstName;
}
public String getLastName() {
return lastName;
}
}
如果需要双向绑定则需要继承BaseObservable
类,该类实现了监听器的注册机制(未验证
)。
public class ObservableUser extends BaseObservable {
private String firstName;
private String lastName;
@Bindable
public String getFirstName() {
return firstName;
}
@Bindable
public String getLastName() {
return lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
}
BR
是编译阶段生成的一个类,功能与 R.java
类似,用 @Bindable
标记过 getter
方法会在 BR
中生成一个 entry。
通过代码可以看出,当数据发生变化时还是需要手动发出通知。 通过调用 notifyPropertyChanged(BR.firstName)
可以通知系统 BR.firstName
这个 entry
的数据已经发生变化,需要更新 UI。
Variable和import###
这两个标签是在layout资源文件中data标签的下级标签,其中variable标签可以独自使用,其独自使用方法如下:
<data>
<variable name="user" type="com.xxx.xxx.xxx.User" />
</data>
其中name对应的字段会在具体view控件赋值时用到,type字段对应值是具体引用的类的全路径。
variable和import方法联合使用方法如下:
<data>
<import type="com.xxx.xxx.xxx.User" />
<variable name="user" type="User" />
</data>
import使用方法与java类似,当使用了import后,variable的type标签就可以使用类名而不是全路径了
多个路径类名相同的解决方法###
<import type="com.example.home.data.User" />
<import type="com.examle.detail.data.User" alias="DetailUser" />
<variable name="user" type="DetailUser" />
如上面代码所示当引用了不同路径的两个 User
类时,需要给其中一个设置别名alias
,这样在给variable标签中的type字段赋值时就可以避免冲突。
绑定variable####
当设置了variable标签后,框架会自动生成一个继承自ViewDataBinding的类,如果data中有class属性,比如
<data class="com.example.CustomBinding">
</data>
则生成的类名为CustomBinding,否则就根据layout的文件名来生成对应的Binding类,例如R.layout.activity_main
对应生成的类名为ActivityMainBinding
。自动生成的类在build目录下,as中不可见。
具体在Activity中的绑定方法如下
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityBasicBinding binding = DataBindingUtil.setContentView(
this, R.layout.activity_basic);
User user = new User("fei", "Liang");
binding.setUser(user);
}
用DataBindingUtil.setContentView来获取一个ActivityBasicBinding的实例binding来实现绑定,binding通过set方法来设置variable中对应的属性。
除了setContentView方法外,DataBindingUtil还提供了一个静态方法bind(View v)
来实现xml和对应的ViewDataBinding的绑定。该方法可以用于adper和fragment中的xml绑定。
使用variable####
数据与 Variable 绑定之后,xml 的 UI 元素就可以直接使用了。使用方法为:属性="@{[variable.name].[property]}"
例如:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}" />
java类静态方法的使用###
首先定义一个静态方法
public class MyStringUtils {
public static String capitalize(final String word) {
if (word.length() > 1) {
return String.valueOf(word.charAt(0)).toUpperCase() + word.substring(1);
}
return word;
}
}
然后在 xml 的 data
节点中导入:
<import type="com.liangfeizc.databindingsamples.utils.MyStringUtils" />
使用方法与 Java 语法一样:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{MyStringUtils.capitalize(user.firstName)}" />
BindingAdapter###
public class TestUtils{
@BindingAdapter("bind:imageUrl")
public static void imageLoader(ImageView view,String url){
ImageLoader.getInstance().display(url,view)
}
}
<?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">
<data >
<import type="com.vip.vf.android.home.api.model.ActiveModel"></import>
<import type="com.vip.vf.android.common.uitils.TestUtils"></import>
<variable
name="model"
type="ActiveModel"/>
<variable
name="isBottom"
type="boolean"/>
<variable
name="utils"
type="TestUtils"/>
<variable
name="imageUrl"
type="String"/>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/vfWhiteColor">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
app:imageUrl = "@{imageUrl}"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
</layout>
以上代码实现的功能是通过设置xml中的imageUrl属性来调用java文件中TestUtils的方法imageLoader,具体需要加载图片的时候只要调用相应的binding class的setimageUrl方法即可完成,可以有效的避免在Activity代码中多次引用ImageLoader这个方法。
自定义view的属性###
自定义view可以直接通过set方法来链接xml的相应属性。
<com.xxx.xxx.xxx.UserView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/largePadding"
app:onClickListener="@{activity.clickListener}"
app:firstName="@{@string/firstName}"
app:lastName="@{@string/lastName}"
app:age="27" />
在如上xml布局文件中,有两个属性firstName和lastName,只要在Java文件UserView中实现两个set方法,不需要专门写style属性。
public void setFirstName(@NonNull final String firstName){
mFirstName.setText(firstName);
}
public void setLastName(@NonNull final String lastName) {
mLastName.setText(lastName);
}
带 ID 的 View
Data Binding 有效降低了代码的冗余性,甚至完全没有必要再去获取一个 View 实例,但是情况不是绝对的,万一我们真的就需要了呢?不用担心,只要给 View 定义一个 ID,Data Binding 就会为我们生成一个对应的 final
变量。
<TextView
android:id="@+id/firstName"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
上面代码中定义了一个 ID 为 firstName* 的 TextView
,那么它对应的变量就是
public final TextView firstName;
使用时我们直接调用相应的binding.firstName就可以对应到相应的TextView
Null Coalescing 运算符
android:text="@{user.displayName ?? user.lastName}"
就等价于
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
属性值
通过 @{}
可以直接把 Java 中定义的属性值赋值给 xml 属性。
<TextView
android:text="@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
使用资源数据
<TextView
android:padding="@{large? (int)@dimen/largePadding : (int)@dimen/smallPadding}"
android:background="@android:color/black"
android:textColor="@android:color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />