引入DataBinding
要在當(dāng)前module的build.gradle文件中添加如下代碼
android{
...
dataBinding {
enabled = true
}
...
}
快速使用
第一步 :創(chuàng)建對(duì)象
一個(gè)普通的java對(duì)象即可
public class User {
public String name;
public String phone;
}
第二步 :修改布局
- 規(guī)范布局:在布局文件最外層添加一個(gè) layout 的根標(biāo)簽
- 引入數(shù)據(jù):在 layout 標(biāo)簽下萨咕,添加 data 標(biāo)簽
- 聲明對(duì)象:在 data 標(biāo)簽中添加 variable 的標(biāo)簽所坯,其中 name 表示對(duì)象名蒂窒,type 表示類名(包含包名)
- 關(guān)聯(lián)屬性:通過表達(dá)式 “@{}” 獲取對(duì)象的屬性,并將他們綁定到控件中
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable name="user" type="cn.com.ursus.User"/>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"/>
</RelativeLayout>
</layout>
注意事項(xiàng):
當(dāng) User 中的有公有屬性 name 時(shí)祟身,@{user.name} 相當(dāng)于 user.name
當(dāng) User 中的無公有屬性 name 時(shí)肋坚,@{user.name} 相當(dāng)于 user.getName()給 android.text 綁定屬性的時(shí)候泵喘,注意轉(zhuǎn)成字符串拉队,如果是整型,會(huì)被當(dāng)成資源id處理握牧,可以參考下面的代碼(字符串用雙引號(hào) " " 原先外面那層雙引號(hào)轉(zhuǎn)成單引號(hào)' ')
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{student.age + ""}'/>
第三步 :綁定對(duì)象
- 在 Activity 的 onCreate 方法中容诬,用 DataBindingUtil.setContentView 來替換原來的 setContentView ,得到一個(gè)名為 ActivityMainBinding 對(duì)象。( ActivityMainBinding 對(duì)象是根據(jù)布
局文件自動(dòng)生成的沿腰,名稱來自于布局文件的名稱配合上駝峰規(guī)則览徒。) - 通過剛才生成的 ActivityMainBinding 將和布局綁定的對(duì)象設(shè)置進(jìn)去
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
User user = new User();
user.name = "luffy";
user.phone = "130****5678";
binding.setUser(user);
}
這樣一個(gè)最基本的數(shù)據(jù)綁定就完成了。
Observable
綁定完之后颂龙,肯定希望的是 User 對(duì)象中的屬性值改變之后习蓬,綁定的控件也跟著自動(dòng)刷新,然而并沒有厘托,于是乎友雳,需要對(duì) User 對(duì)象進(jìn)行如下改造。
public class User extends BaseObservable{
private String name;
private String phone;
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
@Bindable
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
notifyPropertyChanged(BR.phone);
}
}
注解 @Bindable 修飾 getName 方法可在 BR 類中自動(dòng)生成一個(gè)對(duì)應(yīng)屬性 name 的整型常量 BR.name铅匹。 使用 notifyPropertyChanged 方法即可刷新綁定改屬性的控件押赊。至于 BR 是什么,可以類比為 Android 中的 R
如果一個(gè)類中只有個(gè)別屬性別綁定到ui包斑,需要即使刷新流礁,而整個(gè)類又不想繼承
BaseObservable ,可以使用 ObservableField , 具體可以參考下面的代碼
public class Student {
public final ObservableField<String> name = new ObservableField<>();
public final ObservableField<String> grade = new ObservableField<>();
public final ObservableInt age = new ObservableInt();
}
...
final Student student = new Student();
student.name.set("Ace");
student.grade.set("grade2");
student.age.set(1);
binding.setStudent(student);
表達(dá)式和事件
前面提過為了避免 android:text 將整型識(shí)別為資源文件罗丰,需要將整型轉(zhuǎn)成字符串神帅。
android:text='@{student.age + ""}'
由此可見在 @{} 中進(jìn)行一些簡(jiǎn)單的表達(dá)式操作。
三目運(yùn)算符 ?:
android:text="@{ user.phone != null ? user.phone : @string/no_phone}"
Null Coalescing Operator ??
這個(gè)不是 java 代碼的語法萌抵,Databinding 自定義的找御,類似于三目運(yùn)算符特殊情況的一種簡(jiǎn)易寫法
android:text="@{ user.phone ?? @string/no_phone}"
這和上面那種寫法是等價(jià)的
使用靜態(tài)屬性和靜態(tài)方法
上面代三目運(yùn)算符的例子元镀,如果我們要控制某個(gè)控件的顯示與否可以這么寫
android:visibility="@{ user.phone != null ? View.GONE : View.VISIBLE}"
這里不可以使用 gone 、 visible , 必須使用 View.GONE 和 View.VISIBLE
可是這個(gè) View 是哪里來的? 我們可以在 data 標(biāo)簽中 import 進(jìn)來
<data>
<import type="android.view.View"/>
<import type="android.text.TextUtils"/>
</data>
import 進(jìn)來之后霎桅,我們就可以也只能使用其中的靜態(tài)屬性和靜態(tài)方法
android:visibility="@{TextUtils.isEmpty(user.phone) ? View.GONE : View.VISIBLE}
注意:
如果兩個(gè) import 進(jìn)來的兩個(gè)類栖疑,類名相同,我們可以給他們?cè)O(shè)置別名
<import alias="MainActivityPresenter"
type="cn.com.ursus.PermissionUtils"/>
<import alias="ActivityPresenter"
type="cn.com.ursus.presenter.PermissionUtils"/>
資源文件
上面的幾個(gè)例子中在 @{} 中用到了 @string 資源文件滔驶,那么可以使用帶占位符的 @string 嗎遇革?當(dāng)然可以
<string name="welcome_name">Welcome,%s</string>
...
android:text="@{@string/welcome_name(user.name)}"
當(dāng)然除了 @string ,@dimen揭糕、@color 等資源文件也肯定是支持的
事件
我們可以在 @{} 中可以用表達(dá)式來響應(yīng)事件,比如最常用的 onClick,我們可以之間在之前的 User 類中編寫相應(yīng)的方法來響應(yīng)萝快,不過此處我重新創(chuàng)建一個(gè)類專門處理響應(yīng)事件。
class Presenter{
public void clickUserName(View v){...}
public void userNameChanged(CharSequence s, int start, int before, int count) {...}
}
...
android:onClick="@{presenter.clickUserName}"
android:onTextChanged="@{presenter.userNameChanged}"
在 Databinding 中只需要響應(yīng) onTextChanged ,無需理會(huì) beforeTextChanged afterTextChanged 著角,然而如果在代碼中實(shí)現(xiàn) onTextChanged 我們一般都會(huì)采用如下的方式揪漩,就會(huì)顯得有些臃腫。
tvName.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
}
});
我們自定義的響應(yīng)方法必須和常規(guī)監(jiān)聽方法保持一致的參數(shù)嗎雇寇?答案是否定的氢拥,我們可以自己定義響應(yīng)方法的參數(shù)蚌铜,不過表達(dá)式和先前略有不同锨侯,類似lambda
public void clickUserName(View v,String username){
...
}
...
android:onClick="@{(v)->presenter.clickUserName(v,user.name)}
還有一些表達(dá)式就不一一列舉了,下面是從官方Data Binding Guide上復(fù)制下來的目前 @{} 支持的表達(dá)式
- Mathematical
+ - / * %
- String concatenation
+
- Logical
&& ||
- Binary
& | ^
- Unary
+ - ! ~
- Shift
>> >>> <<
- Comparison
== > < >= <=
- instanceof
- Grouping
()
- Literals - character, String, numeric,
null
- Cast
- Method calls
- Field access
- Array access []
- Ternary operator
?:
- Null Coalescing Operator
??
自定義屬性
自動(dòng)屬性
我們現(xiàn)在自定義了一個(gè)控件冬殃,其中有一個(gè)如下的 setPhoneNumber 方法
public class MyTextView extends TextView {
...
public void setPhoneNumber(String phone) {
if (!isPhoneNumber(phone)) {
throw new IllegalArgumentException("手機(jī)號(hào)格式不正確");
}
String show = phone.substring(0, phone.length() - (phone.substring(3)).length())
+ "****"
+ phone.substring(7);
setText(show);
}
...
}
神奇的一幕發(fā)生了 囚痴,我們可以直接在布局文件中使用 phoneNumber 的布局屬性,雖然 MyTextView 中并沒有該屬性
<cn.com.ursus.view.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:phoneNumber="@{user.phone}"/>
自定義屬性
現(xiàn)在有個(gè)需求审葬,項(xiàng)目用的 Picasso 圖片框架深滚,我們需要在ImageView中自定義一個(gè)布局屬性,使得我們可以給該屬性設(shè)置一個(gè)網(wǎng)絡(luò)url時(shí)涣觉,自動(dòng)使用 Picasso 圖片框架來加載網(wǎng)絡(luò)圖片痴荐,該如何做?我們只需寫一個(gè)靜態(tài)方法官册,給他打上一個(gè) @BindingAdapter
@BindingAdapter({"image_url"})
public static void setImageUrl(ImageView view, String url){
Picasso.with(MainApplication.getContext())
.load(url)
.placeholder(R.mipmap.ic_launcher)
.into(view);
}
然后就可以在布局中使用該屬性了
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:image_url="@{user.icon}"/>
那么這個(gè) setImageUrl 方法該放在哪個(gè)類里呢生兆?需要將那個(gè)類導(dǎo)入布局么?
其實(shí) setImageUrl 方法可以放在任意類里面膝宁,而且不需要導(dǎo)入布局中鸦难,不過同一個(gè)控件的自定義屬性一般放在一起管理。