本文是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" />