天天看點

Android Jetpack架構元件之資料綁定(DataBinding)入門

Android Jetpack架構元件之資料綁定(DataBinding)入門

我們絕對不會花費任何多餘的時間和體力在無意義的事情上,因為我們的眼睛永遠隻盯着獵物 ————《狼道》

目錄

前言

一、簡介

二、基本配置

三、簡單使用

四、Demo簡介

五、基礎用法

六、文法

七、插件支援

八、Demo位址

九、參考文檔

十、内容推薦

前言

剛認識DataBinding的時候是在MVVM模式上看到的,之前一直都是在使用MVP模式。之後想換個MVVM模式試試,看是否和别人說的一樣比MVP好用,簡潔。但是剛接觸就遇到一個砍(DataBinding),然後就猶如你看到的一樣,有了這篇文章。這篇文章主要是介紹了DataBinding基礎用法,對DataBinding有一個粗略的了解。也是為我後面學習MVVM模式做個鋪墊吧,文章如有不足,請多多指教。好了,廢話不多說,接招吧!!

Android Jetpack架構元件之資料綁定(DataBinding)入門

一、簡介

早在2015谷歌 I/O大會上,介紹了一個新的架構DataBinding,從名字就可以看出來,這是一個資料綁定庫。借助該庫,您可以使用聲明性格式(而非程式化地)将布局中的界面元件綁定到應用中的資料源

那麼問題來了:

(1)資料綁定是什麼?

将一個使用者界面元素(控件)的屬性綁定到一個類型(對象)執行個體上的某個屬性的方法

簡而言之,把代碼中的資料和xml(UI)綁定起來,雙方都能對資料進行操作,并且在資料發生變化的時候,自動重新整理資料。

(2)資料綁定有什麼用?

資料綁定分為單向綁定和雙向綁定兩種方式

單向綁定就是說資料的流向是單方面的,隻能從代碼流向UI;

雙向綁定的資料流向是雙向的,當業務代碼中的資料改變時,UI上的資料能夠得到重新整理;當使用者通過UI互動編輯了資料時,資料的變化也能自動的更新到業務代碼中的資料上。

而DataBinding就是實作資料綁定的一個架構

(3)DataBinding有什麼用?

  • 把代碼中的資料和xml(UI)綁定起來,雙方都能對資料進行操作,并且在資料發生變化的時候,自動重新整理資料
  • 借助布局檔案中的綁定元件,您可以移除 Activity 中的許多界面架構調用,使其維護起來更簡單、友善。還可以提高應用性能,并且有助于防止記憶體洩漏以及避免空指針異常

(4)優點

  1. 您可以移除 Activity 中的許多界面架構調用,使其維護起來更簡單、友善
  2. 提高應用性能,并且有助于防止記憶體洩漏以及避免空指針異常
  3. 減少大量重複的代碼,去除Activity/Fragment中的UI代碼

了解DataBinding是什麼後,我們就可以開始搬磚了。

Android Jetpack架構元件之資料綁定(DataBinding)入門

二、基本配置

1、環境要求:
  1. 系統版本:Android 2.1(API level 7)及以上
  2. Gradle版本:1.5.0-alpha1及以上
  3. Android Studio版本:1.3及以上

2、app要使用Data Binding,需要添加Data Binding到gradle建構檔案裡

App module - build.gradle

android {
   ...

    dataBinding{
        enabled = true
    }
}
           

這樣就可以在項目中使用

三、簡單使用

步驟:

(1)在布局中最外部套一層layout标簽

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <Button
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/app_name" />

    </android.support.constraint.ConstraintLayout>
</layout>
           

(2)關聯布局

//綁定布局 替代setContentView(R.layout.activity_main);
ActivityMainBinding bind = DataBindingUtil.setContentView(this,R.layout.activity_main);
//給指定id指派
bind.btn.setText("xxxxx");
           

這樣算是綁定完成。是不是比butterknife更簡潔..  當然不隻這些。

這裡以一個Demo,看看DataBinding常用的幾種方式。

四、Demo簡介

1、效果圖:
Android Jetpack架構元件之資料綁定(DataBinding)入門
2、首頁布局activity_main.xml
<?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="click"
            type="android.view.View.OnClickListener"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <EditText
                android:id="@+id/et_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:singleLine="true"
                android:gravity="center"
                android:layout_weight="1"
                android:inputType="text"
                android:hint="聯系名字?" />
            <EditText
                android:id="@+id/et_phone"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:singleLine="true"
                android:gravity="center"
                android:inputType="number"
                android:layout_weight="1"
                android:hint="聯系号碼?"
                />
            <!--點選事件設定-->
            <Button
                android:id="@+id/btn_add"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="@{click}"
                android:text="添加聯系人"
                />
        </LinearLayout>
        <!--聯系人清單-->
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout>
</layout>
           
3、MainActivity.class
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    ActivityMainBinding bind;
    private List<User> list=new ArrayList<>();
    private MyAdapter myAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //關聯布局
        bind= DataBindingUtil.setContentView(this,R.layout.activity_main);
        //設定點選事件
        bind.setClick(this);
        //recycler适配
        LinearLayoutManager manager = new LinearLayoutManager(this);
        bind.recycler.setLayoutManager(manager);
        myAdapter = new MyAdapter(this,list);
        bind.recycler.setAdapter(myAdapter);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_add:
                User user = new User();
                user.setName(bind.etName.getText().toString());
                user.setPhone(bind.etPhone.getText().toString());
                user.setImgUrl("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1545725899676&di=f875404bfb91dc586cf14030097b7a35&imgtype=0&src=http%3A%2F%2Fimg1.gtimg.com%2Fzj%2Fpics%2Fhv1%2F112%2F112%2F2259%2F146920147.jpg");
                list.add(user);
                myAdapter.notifyItemInserted(list.size());
                break;
        }
    }
}
           
4、User.class
public class User {
    private String name;//姓名
    private String phone;//電話
    private String imgUrl;//頭像
    public String getImgUrl() {    return imgUrl;    }

    public void setImgUrl(String imgUrl) {    this.imgUrl = imgUrl;    }

    public String getName() {    return name;    }

    public void setName(String name) {    this.name = name;    }

    public String getPhone() {    return phone;    }

    public void setPhone(String phone) {    this.phone = phone;    }
}
           
5、擴充卡布局adapter_main.xml
<?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>
        <!--定義在布局中使用的資料-->
        <variable
            name="user"
            type="lwb.blcs.databinding.User"/>
        <!--定義點選事件-->
        <variable
            name="click"
            type="lwb.blcs.databinding.MyOnClickListen"/>

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:orientation="horizontal">
        <!--給控件指派單向綁定@{}  雙向綁定@={}-->
        <!-- 給點選事件傳值user.name -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:onClick="@{()->click.onClickName(user.name)}"
            android:layout_weight="1"
            android:text="@={user.name}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:onClick="@{()->click.onClickPhone(user.phone)}"
            android:text="@={user.phone}" />

        <!-- 設定點選事件-->
        <!-- 設定自定義圖檔屬性 imageUrl-->
        <ImageView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_margin="10dp"
            android:layout_weight="1"
            android:onClick="@{click.onClickImage}"
            app:imageUrl="@{user.imgUrl}"
            />
    </LinearLayout>
</layout>
           
6、擴充卡MyAdapter.class
class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    private MainActivity mainActivity;
    private List<User> list;
    private final LayoutInflater inflater;

    public MyAdapter(MainActivity mainActivity, List<User> list) {
        this.mainActivity = mainActivity;
        this.list = list;
        inflater = LayoutInflater.from(mainActivity);
    }
    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        //MyAdapter擴充卡綁定布局
        AdapterMainBinding bind = DataBindingUtil.inflate(inflater, R.layout.adapter_main, viewGroup, false);
        return new MyViewHolder(bind);
    }
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int i) {
        //綁定資料
        holder.bind.setUser(list.get(i));
        //設定點選事件
        holder.bind.setClick(new MyOnClickListen(mainActivity));
    }
    @Override
    public int getItemCount() {
        return list.size();
    }
    class MyViewHolder extends RecyclerView.ViewHolder{
        //ViewHolder 構造函數修改
        AdapterMainBinding bind;
        public MyViewHolder(@NonNull AdapterMainBinding itemView) {
            //itemView.getRoot()擷取View
            super(itemView.getRoot());
            this.bind=itemView;
        }
    }
}
           
7、ImageUtils.class
/**
     * 1.加載圖檔,無需手動調用此方法
     * 2.使用@BindingAdapter注解設定自定義屬性的名稱,imageUrl就是屬性的名稱,
     * 當ImageView中使用imageUrl屬性時,會自動調用loadImage方法,
     */
    @BindingAdapter({"imageUrl"})
    public static void loadImage(ImageView imageView, String url) {
        Glide.with(imageView.getContext())
                .load(url)
                .into(imageView);
    }
           
8、MyOnClickListen.class
public class MyOnClickListen {
    private Activity mainActivity;
    public MyOnClickListen(Activity mainActivity) {
        this.mainActivity = mainActivity;
    }

    //方法引用
    public void onClickName(String name){
        Toast.makeText(mainActivity,name,Toast.LENGTH_SHORT).show();
    }
    //方法引用
    public void onClickPhone(String phone){
        Toast.makeText(mainActivity,phone,Toast.LENGTH_SHORT).show();
    }
    //監聽器綁定
    public void onClickImage(View view){
        Toast.makeText(mainActivity,"圖檔",Toast.LENGTH_SHORT).show();
    }
}
           
9、基礎資訊
//相關依賴
   implementation 'com.android.support:recyclerview-v7:28.0.0'
   implementation 'com.github.bumptech.glide:glide:4.8.0'
    //網絡權限
    <uses-permission android:name="android.permission.INTERNET"/>
           

簡單Demo就完成,省了大量的findViewById(),當然布局的使用也有點不習慣。不過相對代碼簡潔了很多。

不過在寫demo過程中也到過更新不及時,無法等到對象的情況。需Clean Project

Android Jetpack架構元件之資料綁定(DataBinding)入門

五、基礎用法

當然Databinding用法還有很多,上面demo隻是簡單嘗試。更多使用需要檢視官網API

(1)bind UI

<data>
    <variable
        name="user"
        type="lwb.blcs.databinding.User" />
</data>
           
//activity_main布局外層添加layout标簽後自動生成ActivityMainBinding
ActivityMainBinding bind = DataBindingUtil.setContentView(this,R.layout.activity_main);

User user = new User("張si", "12");

//布局variable 定義屬性name 則代碼中可綁定資料

//綁定資料的第一種用法
//bind.setVariable(BR.user,user);
//綁定資料的第二種用法 
bind.setUser(user);
           

(2) bind 點選事件

1.常用方法:
<data>
    <variable
        name="click"
        type="android.view.View.OnClickListener"/>
</data>
           
<Button
    android:id="@+id/btn_add"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="@{click}"
    android:text="添加聯系人"
/>
           
//關聯布局
ActivityMainBinding bind = DataBindingUtil.setContentView(this,R.layout.activity_main);
//綁定點選事件
bind.setClick(this);
           
@Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_add:
                ....
                break;
        }
    }
           
2.方法引用
<data>
    <variable
        name="click"
        type="lwb.blcs.databinding.MainActivity.MyOnClickListen" />
</data>

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="@{click.onClickAge}"
    android:layout_marginTop="20dp"
    android:text="@{user.age}" />

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="@{()->click.onClickText(user)}"
    android:text="@{user.name}" />
           
ActivityMainBinding bind = DataBindingUtil.setContentView(this,R.layout.activity_main);
bind.setClick(new MyOnClickListen());

//點選事件處理
public class MyOnClickListen{
    //方法引用
    public void onClickAge(View view){
        Toast.makeText(MainActivity.this,"點我onClickAge",Toast.LENGTH_SHORT).show();
    }
    //監聽器綁定
    public void onClickText(User user){
        Toast.makeText(MainActivity.this,user.name,Toast.LENGTH_SHORT).show();
    }
}
           

(3) 圖檔顯示

1、BindingAdapter注解設定自定義屬性 
/**

* 1.加載圖檔,無需手動調用此方法

* 2.使用@BindingAdapter注解設定自定義屬性的名稱,imageUrl就是屬性的名稱,

* 當ImageView中使用imageUrl屬性時,會自動調用loadImage方法,

*/

@BindingAdapter({"imageUrl"})
public static void loadImage(ImageView imageView, String url) {
    Glide.with(imageView.getContext()).load(url)
    .error(R.mipmap.error)
    .into(imageView);
}

<!-- 當imageUrl屬性存在時,會自動調用ImageHelper的loadImage方法 -->

<ImageView
    android:layout_width="120dp"
    android:layout_height="120dp"
    android:scaleType="centerCrop"
    app:imageUrl="@{user.picUrl}" />
           

六、文法

(1)variable   |   Import 

在data标簽下可以使用多個import标簽和variable标簽:

  1. variable 标簽是約定資料的引用對象:文法為 <variable name="變量名" type="類型"/>
  2. import  标簽是引入資料類型:文法為 <import  type="類型"/>
<data>
        <import type="android.view.View"/>
        <import type="java.util.List"/>
        <!--這裡小于号和大于号 需要用轉義字元-->
        <variable name="userList" type="List&lt;String&gt;"/>
        <variable name="user" type="lwb.blcs.databinding.User"/>
    </data>

    <TextView
            android:text="@{userList[0]}"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="@{user.isShow ? View.VISIBLE : View.GONE}"/>
           

(2)設定别名alias

路徑不同,但名稱相同的類,可以借助于别名來解決,别名借助alias字段來辨別:文法:<import type="類型" alias="别名">
<import type="lwb.blcs.databinding.User" />
<import type="lwb.blcs.databinding.bean.User" alias="UserBean"/>

<variable
    name="user"
    type="User"/>
<variable
    name="user"
    type="UserBean"/>
           

(3)自定義Binding類名稱

在預設情況下,綁定類根據布局檔案的名稱生成,命名規則:布局名首字母,去掉下劃線,下劃線後的首字母大寫 ,并加上字尾 Binding。該類放在 databinding子產品包下的包中。例:activiy_main.xml   ==>   ActivityMainBinding
我們可以通過調整

data

元素的

class

屬性,将 binding 類進行重命名或放置在不同的包中
//會放在預設生成的databinding包中
<data class="ActMain">
    ......
</data>

//在包名中生成bind類
<data class=".ActMainBind">
    ......
</data>

//為生成binding類指定路徑  使用完整的包名稱
<data class="lwb.blcs.databinding.ActMainBinding"> 
    ......
</data>
           
//預設生成的Binding類
ActivityMainBinding bind= DataBindingUtil.setContentView(this,R.layout.activity_main);

//自定義後生成的Binding類
ActMain bind= DataBindingUtil.setContentView(this,R.layout.activity_main);
           
Android Jetpack架構元件之資料綁定(DataBinding)入門

(4)Observable 

Observable classes provide a way in which data bound UI can be notified of changes.

譯為:提供了一種可以向資料綁定UI通知更改的方法

Data Binding綁定UI後更改資料并不能更新到UI。是以Data Binding提供了Observable來自動更新資料。

Observable分為三類:

  1. Observable objects
  2. Observable fields
  3. Observable collections

主要作用:當資料發生改變的時候,可以及時更新UI。

1、Observable objects

Observable是一個java接口,DataBinding基于此接口提供了一個基礎類BaseObserable

public class User extends BaseObservable{
    private String name;
    @Bindable
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }
}
           

步驟:

(1)繼承BaseObserable

(2)getter添加Bindable注解

(3)setter方法中使用notifyPropertyChanged提醒UI更新資料

2、Observable fields

一個對象包裝器,使其可觀察。可以使用可觀察的字段類自動更新該字段;

public class User {
    public ObservableField<String> name = new ObservableField<>();
}

//使用方法
User user = new User();
user.name.set("更新");
           

當然ObservableField<T>中傳入的泛型可以是Java中的基本類型;還可以使用 ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, ObservableParcelable等具體的類型,效果和ObservableField<T>一樣。

3、Observable collections

顧名思義:可觀察的集合,當資料發生變化時可以及時通知UI更新。

使用方式有兩種:

ObservableArrayList

ObservableArrayMap

<data>
    <import type="android.databinding.ObservableMap" />
    <import type="android.databinding.ObservableList" />
    <variable
        name="map"
        type="ObservableMap<String, String>" />
    <variable
        name="list"
        type="ObservableList<String>" />
</data>

ObservableArrayMap<String, String> map = new ObservableArrayMap<>();

ObservableArrayList<String> list= new ObservableArrayList<>();
           

(5)綁定表達式語言中可以使用以下運算符和關鍵字

算數 + - / * %

字元串拼接 +

邏輯 && ||

位 & | ^

一進制 + - ! ~

移位 >> >>> <<

關系 == > < >= <=

instanceof

組 ( )

字面量 - 字元,字元串,數字,null

方法調用

字段通路

數組通路 [ ]

三元操作 ? :

表達式文法不支援以下操作:

this

super

new

顯式泛型調用

<T>

(6)轉義字元

Android Jetpack架構元件之資料綁定(DataBinding)入門

簡單的用法就寫到這了 就不長篇大論了  能堅持看到這的說明還沒被我催眠0.0

原理這裡也不分析了,太長看起來也累。

有興趣的可以看我寫的《Android ButterKnife入門到放棄》的原理分析,就會發現兩者有點類似

Android Jetpack架構元件之資料綁定(DataBinding)入門

七、插件支援

https://plugins.jetbrains.com/plugin/9271-databinding-support

使布局轉化更加簡單

八、Demo位址

https://github.com/DayorNight/DataBinding

九、參考文檔

https://developer.android.google.cn/topic/libraries/data-binding/?hl=zh_cn

https://www.imooc.com/learn/719

十、内容推薦

《簡書》

《Android ButterKnife入門到放棄》

《Android 10文檔閱讀總結》

《Android 學習資源收集》

《Android 自定義控件基礎》

若您發現文章中存在錯誤或不足的地方,希望您能指出!

Android Jetpack架構元件之資料綁定(DataBinding)入門

繼續閱讀