Google DataBinding的簡單使用

簡介

官方解釋

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 類來使 firstNamelastName 屬性變得可觀察.當變化后,去更新對用綁定的視圖

布局

<?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&lt;String>" />

        <variable
            name="map"
            type="Map&lt;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);

    }
}

其中 &lt; 這個符號不是亂碼出現(xiàn)的.它真的是需要這樣寫.不能寫成 <

參考

官方文檔
Demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容