jetpack-architecture使用

語言: CN / TW / HK

本文已參與「新人創作禮」活動,一起開啟掘金創作之路。

DataBinding

DataBinding顧名思義就是資料繫結,即將資料繫結到UI佈局 另不需再使用findViewById()即可獲取控制元件,非常方便;當然kotlin中獲取控制元件更方便...

使用方式

  1. module的build.gradle中配置如下

    ```gradle android { ...

    dataBinding {
        enabled = true
    }
    

    } ```

  2. 佈局中新增\<layout>根標籤和\<data>標籤

    ```xml <?xml version="1.0" encoding="utf-8"?>

    <data />
    
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/cl_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        ...
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    ```

  3. java中例項化,若佈局為xxx_yyy.xml則對應的Databinding為XxxYyyBinding

    java // activity的onCreate()方法中 DataBindingUtil.setContentView(this, R.layout.activity_test);


    java // fragment的onCreateView()方法中 DataBindingUtil.inflate(Objects.requireNonNull(inflater), fragment_test, container, false);


    java // adapter的onCreateViewHolder()方法中 DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), adapter_test, parent, false);

  4. 程式碼獲取控制元件方式,駝峰式(如控制元件id為bt_close_page則獲取方式為mBinding.btClosePage)

    java mBinding.clRoot.setVisible(View.VISIBLE);

使用DataBinding繫結資料至佈局

繫結自定義的物件資料

  1. 定義資料物件User類

    ```java import androidx.annotation.NonNull;

    public class User {

    private String name;
    private int age;
    
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    
    @NonNull @Override public String toString() {
        return name + " -- " + age;
    }
    

    } ```

  2. 佈局中使用\<variable>宣告user物件

    ```xml <?xml version="1.0" encoding="utf-8"?>

    <data>
        <variable
            name="user"
            type="viewmodel.User" />
    
        ...
    
    </data>
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        ...
    

    ```

  3. 佈局控制元件中使用引進來的user資料

    ```xml

    <CheckBox
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:checked="@{user.age > 17}"
        android:text="年齡大於17嗎" />
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="年齡大於17不顯示該TextView"
        android:visibility="@{user.age > 17 ? View.GONE : View.VISIBLE}" />
    

    ```

    因為使用了系統api View所以需要\<import>宣告

    ```xml ...

    ```

  4. java中建立user例項並使用DataBinding將資料繫結至佈局,並且每次更新user資訊後再使用mBinding.setUser(user)將資料繫結至佈局中,介面會同步更新user資訊

    java User user = new User("ashe", 17); mBinding.setUser(user);

繫結Pair資料

  1. 定義資料物件Pair類,因為是系統api所以不需要定義了

  2. 佈局中使用\<variable>宣告pair物件

    xml <variable name="pair" type="Pair&lt;String, Integer>" />

    要使 XML 不含語法錯誤,必須轉義 < 字元。例如:不要寫成 List\ 形式,而是必須寫成 List\<String>

  3. 佈局控制元件中使用引進來的pair資料

    xml <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@{pair.first + '-' + pair.second}" />

    因為使用了系統api Pair所以需要\<import>宣告

    xml <import type="android.util.Pair" />

  4. java中建立pair例項並使用DataBinding將資料繫結至佈局,並且每次更新pair資訊後再使用mBinding.setPair(pair)將資料繫結至佈局中,介面會同步更新pair資訊

    java Pair pair = new Pair<String, Integer>("akali", 25); mBinding.setPair(pair);

表示式語言

在佈局的表示式中可以使用以下運算子和關鍵字

  • 算術運算子 + - / * %
  • 字串連線運算子 +
  • 邏輯運算子 && ||
  • 二元運算子 & | ^
  • 一元運算子 + - ! ~
  • 移位運算子 >> >>> <<
  • 比較運算子 == > < >= <=(請注意,< 需要轉義為 <)
  • instanceof
  • 分組運算子 ()
  • 字面量運算子 - 字元、字串、數字、null
  • 型別轉換
  • 方法呼叫
  • 欄位訪問
  • 陣列訪問 []
  • 三元運算子 ?:

佈局表示式可避免null指標異常

  如表示式@{user.name}中user為null,name為String,則user.name的值為null

  如表示式@{user.age}中user為null,age為int,則user.age的值為0

null合併運算子 ??

  • 若左邊不為null則使用左邊表示式的值,否則使用右邊表示式的值,下面兩種方式等效

    xml android:text="@{user.displayName ?? user.lastName}"


    xml android:text="@{user.displayName != null ? user.displayName : user.lastName}"

屬性引用

  • 表示式可使用.引用屬性,對於欄位、getter和ObservableField物件使用方式相同

    xml android:text="@{user.lastName}"

集合

  • 可使用 [] 運算子訪問常見集合,例如陣列、列表、稀疏列表和對映,注意 < 需要轉義為 \<

    xml <data> <import type="android.util.SparseArray"/> <import type="java.util.Map"/> <import type="java.util.List"/> <variable name="list" type="List&lt;String>"/> <variable name="sparse" type="SparseArray&lt;String>"/> <variable name="map" type="Map&lt;String, String>"/> <variable name="index" type="int"/> <variable name="key" type="String"/> </data> … android:text="@{list[index]}" … android:text="@{sparse[index]}" … android:text="@{map[key]}" // 上面可下成如下方式 android:text="@{map.key}"

字串字面量

  • 下面兩種方式等效

    xml android:text='@{map["firstName"]}'


    xml android:text="@{map[`firstName`]}"

資源

  • 示例

    xml android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

別名alias

  • 引入重複類名時可以使用別名

    xml <import type="android.view.View"/> <import type="cc.catface.View" alias="cview"/>

包含bind

  • 父佈局

    xml <include layout="@layout/activity_test_vm_i" bind:user="@{user}" />

  • 被\<include>的子佈局

    ```xml <?xml version="1.0" encoding="utf-8"?>

    <data>
    
        <variable
            name="user"
            type="viewmodel.User" />
    </data>
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{user.toString()}" />
    
    </LinearLayout>
    

    ```

  • 不支援被merge的子佈局

    xml <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bind="http://schemas.android.com/apk/res-auto"> <data> <variable name="user" type="com.example.User"/> </data> <merge><!-- Doesn't work --> <include layout="@layout/name" bind:user="@{user}"/> <include layout="@layout/contact" bind:user="@{user}"/> </merge> </layout>

事件處理

  • 定義好的事件處理類

    ```java import android.util.Log; import android.view.View;

    public class Actions {

    public void onClick(View view, User user) {
        Log.d("catface", getClass().getName() + "-->onClick: " + view + " || " + user.toString());
    }
    

    } ```

  • 佈局中呼叫事件處理類

    ```xml <?xml version="1.0" encoding="utf-8"?>

    <data>
        <variable
            name="user"
            type="viewmodel.User" />
    
        <variable
            name="actions"
            type="viewmodel.Actions" />
    
        ...
    </data>
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{v -> actions.onClick(v, user)}"
            android:text="test actions" />
    
        ...
    </LinearLayout>
    

    ```

  • 程式碼中繫結事件處理類至佈局中

    java mBinding.setActions(new Actions());

  • 補充:void可作為符號,示例如下

    xml android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"