1. 程式人生 > >DataBinding系列:DataBinding的基本用法

DataBinding系列:DataBinding的基本用法

轉載自:https://www.jianshu.com/p/70316eb4e0f8

1.在xml中引入一些基礎變數Variables

data 標籤中可以有任意數量的 variable 標籤。這些變數可以使Java中的任意資料型別,每個 variable 標籤描述了會在 binding 表示式中使用的屬性。

<layout xmlns:android="http://schemas.android.com/apk/res/android">  
    <data>  
        <variable  
            name="str"  
            type="String"/>  
        <variable  
            name="age"  
            type="int" />  
    </data>  

    <LinearLayout  
        android:orientation="vertical"  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content">  
        <TextView  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:text="@{str}"/>
        <TextView  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:text="@{String.valueOf(age)}"/>  
    </LinearLayout>  
</layout>

Data Binding和Java一樣,java.lang包裡的類,我們是可以不用匯入包的,也就是說,java基礎型別都是不用導包的。注意:android:text設定int型別的值一定要轉化為String型別,否則系統會認為是資原始檔id,就會出錯*

2.引入一些高階變數Variables

在上面,我們學會了如何在xml中定義變數,以及在控制元件中使用。同樣,我們可以在data中定義像List、Map,陣列等這樣的集合變數,只是它的實現稍微有點不同,下面就一起來看看是如何定義以及使用的。

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <import type="java.util.List" />
        <import type="java.util.Map" />

        <!--泛型的支援會在編譯時期報紅線,但是是可以直接執行的
       但是需要通過轉義字元才行,如:<號用&lt表示;>號用&gt表示;-->
       <variable
            name="list"
            type="List&lt;String&gt;" />

        <variable
            name="map"
            type="Map&lt;String,Object&gt;" />

        <variable
            name="array"
            type="String[]" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="15dp"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{list[0]}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{list.get(1)}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="@{map[`key0`]}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{map.get(`key1`)}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="@{array[0]}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{array[1]}" />

    </LinearLayout>
</layout>   

 

踩坑:

1、目前遇到as環境中,在xml中Map必須使用泛型,List可以不使用,否則編譯報錯;

2、在xml中使用泛型,不能寫<>,只能使用轉義字元,否則報錯,應如下Map&lt;String,Object&gt;

附:常用的轉義字元

顯示結果 描述 轉義字元 十進位制
  空格 &nbsp; &#160;
< 小於號 &lt; &#60;
> 大於號 &gt; &#62;
& 與號 &amp; &#38;
" 引號 &quot; &#34;
撇號 &apos; &#39;
× 乘號 &times; &#215;
÷ 除號 &divide; &#247;

關於獲取list和map的值,我們有2種寫法,[]或者是get(),如果是list或者陣列,需要設定索引下標,如果是map,還需要為它定義key的變數,官方推薦這種集合框架使用[]的寫法。

注意: android:text=""這裡,我用的是雙引號的寫法,官方還有一種單引號的寫法。

單引號
官方說單引號比雙引號更容易使用

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

雙引號
雙引號當然也是可以的,只是你的key要用``包裹,注意,這個不是單引號,而是Tab鍵上面的那個鍵的那個點。

在Activity中初始化資料,設定這些變數

public class BasicActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityBasicBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_basic);

        List<String> list = new ArrayList<>();
        list.add("list1");
        list.add("list2");
        binding.setList(list);

        HashMap<String, Object> map = new HashMap<>();
        map.put("key0", "map_value0");
        map.put("key1", "map_value1");
        binding.setMap(map);
        
        String[] arrays = {"字串1", "字串2"};
        binding.setArray(arrays);
    }
}

3.xml中引用表示式

舉幾個例子,還有很多,大多數Java表示式都是支援的

android:text="@{String.valueOf(age)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:text='@{`iname:` +user.name}'

此外還支援null合併操作,它的符號是??,意思是:如果左邊的物件非空則選擇左邊,否則選擇右邊,這和Java中的三目運算子是一樣的,可以算是一個簡略吧。

android:text="@{user.displayName ?? user.lastName}"
//等價於
android:text="@{user.displayName != null ? user.displayName : user.lastName}"

支援的表示式語言
表示式語言與 Java 表示式有很多相似之處。下面是相同之處:

  • 數學計算 + - / * %
  • 字串連線 +
  • 邏輯 && ||
  • 二進位制 & | ^
  • 一元 + - ! ~
  • 位移 >> >>> <<
  • 比較 == > < >= <=
  • instanceof
  • 組 ()
  • 字面量 - 字元,字串,數字, null
  • 型別轉換
  • 函式呼叫
  • 欄位存取
  • 陣列存取 []
  • 三元運算子 ?:

不支援的操作符
一些 Java 中的操作符在表示式語法中不能使用。

  • this
  • super
  • new
  • 顯式泛型呼叫 <T>

4.設定別名alias

如果我們import了兩個不同路徑,但名稱相同的類,可以藉助於別名來解決,別名藉助alias欄位來標識,例如:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <import type="com.zx.databindingdemo.bean.UserBean" />
        <!--不同路徑下有2個相同名字的類,那麼給其中一個起一個別名,用alias表示-->
       <import type="com.zx.databindingdemo.bean.user.UserBean" alias="UserBean2"/>

        <variable
            name="user"
            type="UserBean" />
        
        <variable 
            name="user2" 
            type="UserBean2"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="15dp"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="@{`姓名:`+user.name}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="@{`user2:`+user2.content}" />
      
    </LinearLayout>
</layout>   

在Activity中

public class BasicActivity extends AppCompatActivity  {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityBasicBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_basic);

        UserBean userBean = new UserBean(URL_USER_PIC, "張三", 24);
        binding.setUser(userBean);

        com.zx.databindingdemo.bean.user.UserBean userBean2 = new com.zx.databindingdemo.bean.user.UserBean("我是user2");
        binding.setUser2(userBean2);
    }

5.include中的使用

在使用名稱空間的佈局中,變數可以傳遞到任何 include 佈局中。

<?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="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <include layout="@layout/name"
          app:user="@{user}"/>
       <include layout="@layout/contact"
          app:user="@{user}"/>
   </LinearLayout>
</layout>

注意:在name.xml以及contact.xml兩個layout檔案中必需要有user variable

<?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="com.example.User" />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{`我的名字`+user.name}"/>
    </LinearLayout>

</layout>

Data binding不支援直接包含merge 節點。舉個例子, 以下的程式碼不能正常執行 :

<?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="com.example.User"/>
   </data>
   <merge>
       <include layout="@layout/name"
        app:user="@{user}"/>
       <include layout="@layout/contact"
         app:user="@{user}"/>
   </merge>
</layout>

6.事件處理

大家都知道,我們經常需要處理View的點選事件,在xml中android:onClick 可以指定 Activity 中的函式,Data Binding 也允許處理從檢視中傳送的事件。

下面給出幾種實現方式:

  • 佈局中引入OnClickListener的變數
  • 方法呼叫

佈局中引入OnClickListener的變數

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
            name="clickListener"
            type="android.view.View.OnClickListener" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="15dp"
        android:orientation="vertical">

        <Button
            android:id="@+id/click_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{clickListener}"
            android:text="點我" />

        <Button
            android:id="@+id/click2_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{clickListener}"
            android:text="點我2" />

    </LinearLayout>
</layout>   

Activity處理點選事件

public class BasicActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityBasicBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_basic);
        
        binding.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.click_btn) {
            Toast.makeText(this, "點選了1", Toast.LENGTH_SHORT).show();
        } else if (v.getId() == R.id.click2_btn) {
            Toast.makeText(this, "點選了2", Toast.LENGTH_SHORT).show();
        }
    }
}

方法呼叫
相比較於在android:onClick中指定Activity的一個方法,它的優勢在於表示式會在編譯時處理,如果方法不存在或者方法簽名不對,編譯將會報錯。
以下是個例子:

public class OnClickHandler {
    public void onClickFriend(View view) {
        Toast.makeText(view.getContext(), "onClickFriend", Toast.LENGTH_SHORT).show();
    }
}

佈局檔案如下

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
         <variable
            name="handler"
            type="com.zx.databindingdemo.handler.OnClickHandler" />
    </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="@{handler::onClickFriend}"/>
        <!-- 注意:函式名和監聽器物件必須對應 -->
        <!-- 函式呼叫也可以使用 `.` , 如handler.onClickFriend , 不過已棄用 -->
    </LinearLayout>
</layout>

別忘了在Activity設定變數

binding.setHandler(new OnClickHandler());