簡介
官方解釋
Android data binding operates primarily at compile time, processing expressions found in the layout file and generating code in the application. This package contains common code that will be included with the application, just like support library components.
Demo解決了什么問題
-
對象對視圖的綁定(只修改對象的值,綁定的視圖隨之改變) -
對象和視圖的雙向綁定(除了上面一點特性,還能通過修改視圖,從而改變對象的值) - 結(jié)合
ListView去更新列表視圖的數(shù)據(jù)(之前項目有一個需求,點進去查看動態(tài)詳情,評論后返回動態(tài)列表,動態(tài)列表顯示的評論數(shù)量對應增加)
環(huán)境
開發(fā)環(huán)境
- APP運行環(huán)境: API 7+即可
- 開發(fā)IDE: Android Studio
環(huán)境配置
確保jcenter在repositories列表
allprojects {
repositories {
jcenter()
}
}
開啟DataBinding支持
android {
...
dataBinding{
enabled = true;
}
}
例子
對象綁定到視圖
Model定義
public class User {
public ObservableField<String> firstName = new ObservableField<>();
public ObservableField<String> lastName = new ObservableField<>();
public User() {
}
@Override
public String toString() {
return firstName.get() + " " + lastName.get();
}
}
使用
ObservableField類來使firstName和lastName屬性變得可觀察.當變化后,去更新對用綁定的視圖
布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="demo.august1996.top.databingdingusage.bean.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/firstNameET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.firstName}"
android:textAlignment="center" />
<EditText
android:id="@+id/lastNameET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.lastName}"
android:textAlignment="center" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="test"
android:text="測試(修改數(shù)據(jù)后顯示)" />
</LinearLayout>
</layout>
布局與以前不一樣的是,使用
layout作為root標簽.
引入<data>標簽,其中
-
variable為綁定的對象 -
user為綁定對象映射過來的名稱(類似于形參) -
type為綁定對象的類型
接著就是變量解析的語法了
@{變量名.成員屬性}
Activity:
package demo.august1996.top.databingdingusage.activity;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
import demo.august1996.top.databingdingusage.R;
import demo.august1996.top.databingdingusage.bean.User;
import demo.august1996.top.databingdingusage.databinding.ActivityDataBind2ViewBinding;
public class DataBind2ViewActivity extends AppCompatActivity {
private User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_data_bind2_view);
user = new User();
user.firstName.set("Yu");
user.lastName.set("jianbin");
ActivityDataBind2ViewBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_data_bind2_view);
binding.setUser(user);
}
public void test(View v) {
user.firstName.set("August");
user.lastName.set("1996");
Toast.makeText(this, user.toString(), Toast.LENGTH_SHORT).show();
}
}
其中
XXXBinding這個類是Android Studio幫我們生成的.命名規(guī)范是參照布局文件名字:把下劃線命名改成駝峰命名,例如activity_data_bind2_view.xml變成了ActivityDataBind2ViewBinding.
通過
DataBindingUtil.setContentView我們獲取了對應的Binding對象,其中setUser方法就是我們在布局文件里面寫了user變量,然后系統(tǒng)自動生成的.
現(xiàn)在Activity全局變量user就已經(jīng)和布局文件里面的user關(guān)聯(lián)上了
所以通過
test函數(shù)修改了user對象的值.對應的兩個EditText控件也進行了更新
對象與視圖的雙向綁定
對于上面的例子,也只能作為
單向綁定.我們還需要做一個改變視圖里面的內(nèi)容.然后去更新對象的值.
思路
其實很簡單,我們只需要給EditText添加內(nèi)容修改監(jiān)聽.然后再把
user對象更新就可以了。
新建UserWatcher類
package demo.august1996.top.databingdingusage.watcher;
import android.text.Editable;
import android.text.TextWatcher;
import demo.august1996.top.databingdingusage.bean.User;
/**
* Created by August on 16/6/17.
*/
public class UserWatcher {
private User user;
public UserWatcher(User user) {
this.user = user;
}
public TextWatcher firstNameWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
user.firstName.set(editable.toString());
}
};
public TextWatcher lastNameWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
user.lastName.set(editable.toString());
}
};
}
沒什么,就只在編輯結(jié)束后設(shè)置
user對應屬性的值
布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="demo.august1996.top.databingdingusage.bean.User" />
<variable
name="watcher"
type="demo.august1996.top.databingdingusage.watcher.UserWatcher" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/firstNameET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:addTextChangedListener="@{watcher.firstNameWatcher}"
android:text="@{user.firstName}"
android:textAlignment="center" />
<EditText
android:id="@+id/lastNameET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:addTextChangedListener="@{watcher.lastNameWatcher}"
android:text="@{user.lastName}"
android:textAlignment="center" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="test"
android:text="測試(嘗試修改上面數(shù)據(jù),顯示數(shù)據(jù)后修改數(shù)據(jù))" />
</LinearLayout>
</layout>
這次我們引入了
watcher這個變量和它的類型.
然后我們給EditText都增加了對應的android:addTextChangedListener=時間監(jiān)聽.通過這樣,我們就可以把EditText的監(jiān)聽和對應的Watcher進行關(guān)聯(lián)
Activity代碼:
package demo.august1996.top.databingdingusage.activity;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
import demo.august1996.top.databingdingusage.R;
import demo.august1996.top.databingdingusage.bean.User;
import demo.august1996.top.databingdingusage.databinding.ActivityViewBind2DataBinding;
import demo.august1996.top.databingdingusage.watcher.UserWatcher;
public class ViewBind2DataAndDataBind2ViewActivity extends AppCompatActivity {
private User user;
private UserWatcher watcher;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_bind2_data);
user = new User();
user.firstName.set("Yu");
user.lastName.set("jianbin");
watcher = new UserWatcher(user);
ActivityViewBind2DataBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_view_bind2_data);
binding.setUser(user);
binding.setWatcher(watcher);
}
public void test(View v) {
Toast.makeText(this, user.toString(), Toast.LENGTH_SHORT).show();
user.firstName.set("August");
user.lastName.set("1996");
}
}
我們通過
test方法先把原來的user對象的值顯示出來.
如果我們修改EditText的值,我們發(fā)現(xiàn)user對象的值已經(jīng)可以同步了
然后再去修改user對象的值,EditText的值也同步了.
結(jié)合ListView使用
個人覺得最大的用處還是結(jié)合ListView去使用這個特性
item_user子布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="stu"
type="demo.august1996.top.databingdingusage.bean.Student" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/firstNameET"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="firstName"
android:text="@{stu.firstName}"
android:textAlignment="center" />
<EditText
android:id="@+id/lastNameET"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="lastName"
android:text="@{stu.lastName}"
android:textAlignment="center" />
</LinearLayout>
<Button
android:id="@+id/testBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="修改對象" />
</LinearLayout>
</layout>
可以看到跟前面的主布局是差不多的
Model定義
package demo.august1996.top.databingdingusage.bean;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import demo.august1996.top.databingdingusage.BR;
/**
* Created by August on 16/6/17.
*/
public class Student extends BaseObservable {
private String firstName;
private String lastName;
public Student(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = 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);
}
}
這里的模型定義時需要繼承
BaseObservable,并且所有需要綁定的對象的getter方法都需要使用@Bindable注解,來表示該屬性是需要綁定的
BR.class也是Android Studio幫我們生成的,類似R.class.里面是一些我們需要綁定的屬性的信息
當屬性更新時,我們必須使用notifyPropertyChanged方法去提醒屬性被更新,請求綁定視圖同步.
所以我們必須在setter方法里面更新完對象值后調(diào)用notifyPropertyChanged方法
Adapter的編寫
package demo.august1996.top.databingdingusage.adapter;
import android.databinding.DataBindingUtil;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.ArrayList;
import demo.august1996.top.databingdingusage.BR;
import demo.august1996.top.databingdingusage.R;
import demo.august1996.top.databingdingusage.bean.Student;
import demo.august1996.top.databingdingusage.databinding.ItemUserBinding;
/**
* Created by August on 16/6/17.
*/
public class UserAdapter extends BaseAdapter {
private ArrayList<Student> mDatas;
public UserAdapter(ArrayList<Student> mDatas) {
this.mDatas = mDatas;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public Object getItem(int i) {
return mDatas.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(final int i, View convertView, ViewGroup viewGroup) {
ItemUserBinding binding;
if (convertView == null) {
binding = DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.item_user, viewGroup, false);
convertView = binding.getRoot();
convertView.setTag(binding);
} else {
binding = (ItemUserBinding) convertView.getTag();
}
binding.setVariable(BR.stu, mDatas.get(i));
binding.executePendingBindings();
convertView.findViewById(R.id.testBtn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mDatas.get(i).setFirstName(mDatas.get(i).getFirstName() + i);
mDatas.get(i).setLastName(mDatas.get(i).getLastName() + i);
}
});
return convertView;
}
}
與一般Adapter不同的地方主要是在于
getView方法.
通過DataBindingUtil.inflate()方法我們拿到一個Binding對象.
然后ListView顯示的子視圖view通過binding.getRoot()來獲得
通過binding.setVariable();去設(shè)置子布局的變量
最后使用executePendingBindings方法來通知子視圖更新
其中按鈕的監(jiān)聽就是修改對象的值了,然后就有視圖同步對象的效果了
主布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/mListView"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</RelativeLayout>
超簡單...
Activity代碼
package demo.august1996.top.databingdingusage.activity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ListView;
import java.util.ArrayList;
import demo.august1996.top.databingdingusage.R;
import demo.august1996.top.databingdingusage.adapter.UserAdapter;
import demo.august1996.top.databingdingusage.bean.Student;
public class ListViewActivity extends AppCompatActivity {
private ListView mListView;
private ArrayList<Student> mData;
private UserAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view);
mListView = (ListView) findViewById(R.id.mListView);
mData = new ArrayList<>();
for (int i = 0; i < 10; i++) {
mData.add(new Student("王" + i, "" + i));
}
mAdapter = new UserAdapter(mData);
mListView.setAdapter(mAdapter);
}
}
超簡單...
import關(guān)鍵字
這個直接上例子會比較好理解
布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="java.util.List" />
<import type="java.util.Map" />
<import type="java.lang.String" />
<variable
name="list"
type="List<String>" />
<variable
name="map"
type="Map<String,String>" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{list[0]}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{list[1]}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{map[`0`]}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text='@{map["1"]}' />
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@{@drawable/img}" />
</LinearLayout>
</layout>
Activity代碼
package demo.august1996.top.databingdingusage.activity;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.HashMap;
import demo.august1996.top.databingdingusage.R;
import demo.august1996.top.databingdingusage.databinding.ActivityImportViewBinding;
public class ImportViewActivity extends AppCompatActivity {
private ArrayList<String> mList;
private HashMap<String, String> mMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_import_view);
mList = new ArrayList<>();
mMap = new HashMap<>();
for (int i = 0; i < 2; i++) {
mList.add("列表" + i);
mMap.put(String.valueOf(i), "集合" + i);
}
ActivityImportViewBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_import_view);
binding.setList(mList);
binding.setMap(mMap);
}
}
其中
<這個符號不是亂碼出現(xiàn)的.它真的是需要這樣寫.不能寫成<