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