Android:DataBinding的一二事

iamxiarui_發表於2018-07-02

##寫在前面

最近冷靜了一個星期,學了大名鼎鼎的DataBinding。導師說這跟H5一樣,是未來Android發展的趨勢。看了一下,確實是Google官方跟著MVVM一起推出來的,所以就學了相關的知識,個別也運用到專案中。

不過講心裡話,不知道是不是平時的findViewById用習慣了,還是ButterKnife太好用了,一直覺得用這樣的方式去繫結資料有點渾身不得勁,先學著吧。

所以今天來分享一下學習DataBinding過程。不過不同的是,在這裡我不介紹它的使用規範或者方法,因為目前網上關於DataBinding的優秀文章很多很多,文末也會有相關連結。

只不過由於大部分文章都是針對Gradle1.5版本去寫的,有些方法和注意事項已經不適用了,而且文章幾乎只分開介紹了基本使用,並沒有介紹開發中的一些實際問題,我在學的過程中也踩了不少坑,所以本篇文章主要先介紹一下新版本的不同(坑),然後用一個小的實戰專案來具體展示如何使用DataBinding。

##站在前人的肩膀上,幫後人踩坑

###初始配置

第一次使用的時候需要注意,Gradle版本需要大於1.5。如果大於1.5,只需要在當前 Module 下的 build.gradle 檔案中新增如下程式碼即可

android {
    ...
    dataBinding {
        enabled true
    }
	...
}
複製程式碼

注意,只需要在 android{ } 裡面加省略號之間的三行就行了,其他啥都不用配置。在這裡,我專案用的版本是 Gradle 2.1.3

###使用注意事項

1、 android:text 中只能寫" @{...}"的形式

在其他優秀DataBinding使用教程,我們學會了像下面這樣的使用規範:

<TextView
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:text="@{user.age}" />
複製程式碼

但是千萬不能這樣寫:

<TextView
	...
	android:text="年齡@{user.age}" />

<TextView
	...
	android:text="年齡+@{user.age}" />
複製程式碼

對比一下吧,沒有對比就沒有傷害:

text注意點

當然如果你這樣寫的話:

<TextView
	...
	android:text="@{年齡user.age}" />

<TextView
	...
	android:text="@{年齡+user.age}" />
複製程式碼

壓根編譯都過不去,請珍惜生命,善待自己。

2、注意 android:text 中是 String 型別

假如上面的 user.ageint 型別的話,一定要記得轉成 String 型別,怎麼轉?當然是 String.valueOf() 了:

<TextView
	...
	android:text="@{String.valueOf(user.age)}" />
複製程式碼

3、引用資源Bug已經被解決

在之前所有的文章中,都有提到官方有個bug,說在引用資源時必須加上 int 才能通過編譯。

但是經過我的測試,不加也是可以編譯且正常執行的,可能這個bug已經被解決掉了:

<!--不同間距-->
<TextView
	...
	android:text="@{person.name}"
	android:paddingLeft="@{person.isTrue ? @dimen/largeDP : @dimen/smallDP}"/>

<!--不同字號-->
<TextView
	...
	android:text="@{person.phone}"
	android:textSize="@{large ? @dimen/largeSP : @dimen/smallSP}" />
複製程式碼

從下圖可以看到,第一個行間距不同,第二行字號不同:

資源引用

4、include不能新增在非根結點的ViewGroup中?

同樣的,在之前幾乎所有的教程中,說 include 標籤不能非根結點的 ViewGroup 中,說程式會Crash掉,但是經過我的測試,程式也能正常編譯執行,估計也是一個Bug吧:

<!--根結點-->
<LinearLayout
    ...
    android:orientation="vertical">

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

    <include
        layout="@layout/layout_button"
        bind:btClick="@{btClick}" />

    <!-- 非根節點 ViewGroup 仍然有效-->
    <LinearLayout
        ...
        android:orientation="vertical">

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

        <include
            layout="@layout/layout_button"
            bind:btClick="@{btClick}" />

    </LinearLayout>
</LinearLayout>
複製程式碼

如下圖,佈局是一樣的:

include問題

以上兩個Bug問題是僅在有限測試後得出的結論,如有錯誤請指正,謝謝!

5、使用資源或者屬性時,記得引包

什麼意思呢,比如下面根據 large 的值引用不同資源/屬性:

<!--顯示-->
<TextView
	...
	 android:visibility="@{large ? View.INVISIBLE : View.VISIBLE}"/>

<!--顏色-->
<TextView
	...
	android:textColor="@{large ? Color.RED : Color.YELLOW}"/>
複製程式碼

這個時候,我們用到了 View 包和 Color 包,所以必須在 data 標籤中 import 這兩個包,才能顯示正常:

<import type="android.view.View" />
<import type="android.graphics.Color"/>
複製程式碼

從下圖可以看到第一行沒有顯示,第二行是紅色的:

image

6、自定義繫結類名稱注意包名

一般來說,編譯器會給我們自動生成一個繫結類,類名與佈局檔案的名稱一致。有些時候,我們需要自己自定義:

<import class="**.CustomBinding" />
複製程式碼

需要注意的是,這裡 CustomBinding 之前的 * 號部分必須是專案的包名,那包名需要一直寫到哪呢?

這個沒有具體規定,比如我專案的目錄是這樣的:

|--com
	|--dataBinding
		|--adapter
		|--bean
		|--pojo
		|--ui
		|--utils
複製程式碼

我可以寫 com.databinding.CustomBinding 也可以寫 com.databinding.ui.CustomBinding ,只要在專案包中即可。當然了,不能只寫個 com.CustomBinding ,這是個細節問題,大家注意一下就好。

7、XML檔案中不能包含< >符號

實際上這是個很有趣的問題,開始我還沒有發現,先看下面這段程式碼:

 <!--資料層-->
<data>
    <import type="android.databinding.ObservableMap" />
    <variable
        name="muser"
        type="ObservableMap&lt;String, Object>" />
</data>
複製程式碼

這裡的 variable 型別為一個 ObservableMap ,既然是 Map 當然就有 Key 和 Value ,這裡是 String 型別的 Key ,Object 型別的 Value ,但是注意這裡並不是你看錯了,就是這麼寫的。因為這裡不允許使用 "< >" 這樣的格式,否則會直接報錯。

8、IDE的各種問題

因為DataBinding推出不是很久,用的人不是很多,聽說在早前的AS版本中,IDE是沒有智慧提示的。但是現在的IDE已經對DataBinding有了很好的支援了,但是仍然還是有很多小的問題。

比如在XML檔案的UI層的根結點中,一些常用的屬性沒法提示,連 width 和 height 有時候都打不出來,但是如果真的手打出來的話,仍然是有效的,估計是IDE的問題。

同樣的,有時候在Activity中想要得到繫結類的時候,總是提示沒有這個類,但是繫結命名卻跟XML檔案命名一致的,這個時候我們只要把XML檔名稱隨便重新命名一下就行了,估計也是IDE沒有反應過來。

###常見問題

1、xx missing it

Error:Execution failed for task ':DataBindingDemo:compileDebugJavaWithJavac'.
> java.lang.RuntimeException: Found data binding errors.
****/ data binding error ****msg:Identifiers must have user defined types from the XML file. Color is missing it
file:D:\Android\AndroidProject\Android5.0Demo\DataBindingDemo\src\main\res\layout\activity_resource.xml
loc:120:45 - 120:49
****\ data binding error ****
複製程式碼

這個問題很常見,不過跟我上面說的第五點是一致的,大部分都是因為沒有導包的原因,所以找不到這個 Color 。

解決辦法就是一句話,什麼Missing導什麼。

2、Could not find method XX

這也是個細節性問題了,比如下面這段程式碼(有省略):

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

	...

<!--點選事件-->
    <Button
        ...
        android:onClick="btclick" />
複製程式碼

這裡就是給按鈕定義一個監聽,一般我們寫 android:onClick 的時候直接寫方法名就可以了,但是這裡如果我們直接寫的話,能編譯通過,但是一點選就會Crash,錯誤如下:

java.lang.IllegalStateException: Could not find method btclick(View) in a parent or ancestor Context for android:onClick attribute defined on view class android.support.v7.widget.AppCompatButton

提示說沒有找到方法,解決辦法就是 android:onClick="@{btclick}" ,沒什麼好解釋的,細心細心。

3、NullPoint怎麼辦

這個問題也很有趣,剛接觸DataBinding的時候,我也有相同的疑問,但是實際上,DataBinding不會出現NollPoint,至少在已經繫結的物件上不會出現。

為什麼呢?下面這是Data Binding(資料繫結)使用者指南給出的解釋:

“Data Binding程式碼生成時自動檢查是否為nulls來避免出現null pointer exceptions錯誤。例如,在表示式@{user.name}中,如果user是null,user.name會賦予它的預設值(null)。如果你引用user.age,age是int型別,那麼它的預設值是0。”

可以看到,DataBinding已經給我們處理好了,所以不用擔心這個問題。

4、編譯出錯,控制檯一大串錯誤資訊怎麼辦

在編譯過程中,如果有錯,控制檯會列印一大串問題,這個時候只需要看錯誤資訊的最後一條就行了。

而且一般來說,關鍵性錯誤資訊都是下面這樣的格式:

****/ data binding error ****
msg:...
file:...
****\ data binding error ****
複製程式碼

##舉個例子

好了,大概瞭解了DataBinding的使用方式,現在來用一個小小的例子來說具體如何在專案中使用它。

關於下面的例子很簡單,但是有幾點說明:

  • 只說DataBinding相關部分;
  • 特地選用了比較複雜的RecyclerView;
  • 特地在item中新增圖片和文字,圖片來源為Url;
  • 重點在於RecyclerView的動態繫結。

先來看一下item中的佈局吧,也是精簡了一部分:

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

    <data>
        <variable
            name="news"
            type="com.databinding.bean.NewsBean" />
    </data>

    <android.support.v7.widget.CardView
        ...>

		<ImageView
			.../>

		<TextView
			...
			android:text="@{news.title}"/>

		<TextView
			...
			android:text="@{news.desc}"/>

		<TextView
			...
			android:text="@{news.time}"/>

    </android.support.v7.widget.CardView>
</layout>
複製程式碼

可以看到,這裡只有TextView使用了DataBinding,圖片並沒有設定,讓我們來看看在複雜的佈局中如何使用動態繫結。

因為是一個列表資料,item是複用的,所以沒辦法直接繫結,那麼就給ViewHolder繫結。

直接上原始碼吧,難度不大:

public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.NewsViewHolder> {

    private List<NewsBean> list;
    private Context context;

    public NewsAdapter(Context context, List<NewsBean> list) {
        this.list = list;
        this.context = context;
    }

    @Override
    public NewsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_news, parent, false);
        return new NewsViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(NewsViewHolder holder, int position) {
        //給ViewHolder繫結
        holder.bind(list.get(position));
        //載入圖片
        Picasso.with(context).load(list.get(position).getPicUrl()).into(holder.itemPicImage);
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    public class NewsViewHolder extends RecyclerView.ViewHolder {

        //繫結類
        private ItemNewsBinding inBinding;
        private ImageView itemPicImage;

        public NewsViewHolder(View itemView) {
            super(itemView);
            //為每一個item設定
            inBinding = DataBindingUtil.bind(itemView);
            itemPicImage = (ImageView) itemView.findViewById(R.id.iv_item_pic);
        }

        /**
         * 繫結方法
         *
         * @param news Bean物件
         */
        public void bind(@NonNull NewsBean news) {
            inBinding.setNews(news);
        }
    }
複製程式碼

如果你仔細看了上面的程式碼,你會發現其實只是多了三件事情:

  • ViewHolder中建立繫結類;
  • 利用繫結類設定相關內容;
  • 繫結ViewHolder的時候繫結上相關內容。

就這麼多,來看看效果圖吧:

示例圖

其例項子本身很簡單,但是想說的不是例子本身。從程式碼中我們可以發現,圖片的載入並沒有使用DataBinding,而是用Picasso直接載入。當然也可以使用DataBinding的靜態方法去直接展示,但是感覺有點怪怪的。而且如果這個item的佈局十分複雜,考慮的情況很多的話,這種情況顯然很麻煩很麻煩。何況這裡還沒有考慮點選事件。

所以總的來說就是,不熟悉就慎用慎用。

##總結

因為是剛接觸,對這種資料繫結的方式各種不適應,而且很多方法可能暫時不知道,就像上面複雜的佈局不知道有沒有更簡單的方法去處理。反正給我的感覺就是有點麻煩,也不準備繼續深入學習DataBinding了。

###優點

  1. 節省了給View設定Id的工作;
  2. 節省了findViewById的工作;
  3. 大部分情況都能很快捷的處理;
  4. 沒有空指標的問題;
  5. 還有很多優點待發掘。

###缺點

  1. ButterKnife好像更方便;
  2. 複雜的佈局處理起來很困難;
  3. IDE不夠完善。
  4. 還有很多缺點待發現。

###資源分享

Google官方文件 - 請科學上網

Data Binding(資料繫結)使用者指南 - 田浩浩_DockOne

完全掌握Android DataBinding - 泡在網上的日子

Android資料繫結框架DataBinding,堪稱解決介面邏輯的黑科技 - 非著名程式設計師

###專案原始碼

DataBindingDemo - Github


個人部落格:www.iamxiarui.com 原文連結:http://www.iamxiarui.com/?p=805

相關文章