本文是https://github.com/LyndonChin/MasteringAndroidDataBinding的学习笔记java
保证Gradle插件版本不低于1.5.0-alpha1android
修改对应模块的build.gradle:git
dataBinding{ enabled true }
最外层根节点变为layout
,新增节点data
用来存放页面可能用的数据以及方法。github
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> </data> <!--原先的根节点(Root Element)--> <LinearLayout> .... </LinearLayout> </layout>
须要注意的是在数据对象中必须实现每一个属性的get和set方法。api
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
类,该类实现了监听器的注册机制(未验证
)。app
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。ide
这两个标签是在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标签后,框架会自动生成一个继承自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 绑定以后,xml 的 UI 元素就能够直接使用了。使用方法为:属性="@{[variable.name].[property]}"
例如:
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.lastName}" />
首先定义一个静态方法
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)}" />
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能够直接经过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); }
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
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" />