Android端MVVM從入門(mén)到實(shí)戰(zhàn)(第二篇) - DataBinding基礎(chǔ)

前言

在上一篇文章簡(jiǎn)單的介紹了MVVM架構(gòu)和Android端實(shí)現(xiàn)MVVM架構(gòu)需要用到的四個(gè)官方組件,但是這些組件還有很多的知識(shí)和技巧需要我們了解,接下來(lái)我們先來(lái)詳細(xì)研究一下DataBinding吧。

參考代碼地址:https://github.com/guoergongzi/GMVVMDemo/tree/main

參考代碼Module:gdatabindingdemo2

1、啟用DataBinding

首先新建一個(gè)項(xiàng)目,在module目錄下的build.gradle文件中添加以下代碼:

android {
        。。。
    // 允許項(xiàng)目使用databinding
    dataBinding {
        enabled = true
    }
}

2、快捷生成DataBinding布局文件

之前介紹了編寫(xiě)能用到DataBinding特性的布局文件的規(guī)則,其實(shí)這個(gè)規(guī)則有一個(gè)快速的生成方式,按鍵盤(pán)option+回車(chē)(windows是Alt + 回車(chē))呼出圖中菜單,點(diǎn)擊Convert to data binding layout選項(xiàng):

Untitled.png

可以看到,Android studio給xml文件里添加了data標(biāo)簽,我們可以直接在里面導(dǎo)入我們需要的類(lèi)或者對(duì)象了。

%E6%88%AA%E5%B1%8F2023-01-29_11.19.49.png

3、獲取DataBinding對(duì)象

注意這里xml文件的文件名是activity_main,對(duì)應(yīng)的Activity文件名為MainActivity。這個(gè)時(shí)候我們?cè)贛ainActivity.java文件中刪掉setContentView方法,改為以下語(yǔ)句:

ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

這里調(diào)用了DataBindingUtil這個(gè)類(lèi)的setContentView方法,這是使用DataBinding框架的必要步驟。同時(shí)使用一個(gè)ActivityMainBinding對(duì)象來(lái)接這個(gè)方法的返回值,這個(gè)ActivityMainBinding類(lèi)是編寫(xiě)完xml文件并保存后自動(dòng)生成的(前提是build.gradle文件中的那段配置要有,且項(xiàng)目中不能有異常影響編譯)。我們可以看到這個(gè)類(lèi)命名的規(guī)律:把xml文件的名稱(chēng)去掉下劃線(xiàn)并改成駝峰式命名,并在最后加一個(gè)Binding。

如果系統(tǒng)自動(dòng)生成的名稱(chēng)不能滿(mǎn)足需要的話(huà),可以在data標(biāo)簽中用class屬性指定一個(gè)名字,像這樣:<data class="TestBinding">。保存后再回到MainActivity時(shí)會(huì)發(fā)現(xiàn)ActivityMainBinding這個(gè)類(lèi)變紅了,改成TestBinding即可。

4、使用DataBinding對(duì)象獲取控件對(duì)象

給TextView設(shè)置一個(gè)id:android:id="@+id/tv_main"并保存,這時(shí)進(jìn)入MainActivity并編寫(xiě)以下代碼:

binding.tvMain.setText("測(cè)試");

運(yùn)行項(xiàng)目,我們會(huì)發(fā)現(xiàn)確實(shí)TextView的內(nèi)容發(fā)生了變化,說(shuō)明這個(gè)binding.tvMain就是id為tv_main的TextView,命名規(guī)則也很好理解:把id的下劃線(xiàn)去掉并且變?yōu)槭鬃帜感?xiě)的駝峰式命名。

我們目前使用ButterKnife來(lái)獲取控件對(duì)象時(shí)常常發(fā)現(xiàn)Android studio會(huì)給出這個(gè)警告:Resource IDs will be non-final by default in Android Gradle Plugin version 8.0, avoid using them as annotation attributes。大概意思是Android Gradle Plugin version 8.0以后資源的id將默認(rèn)不再是常量,使用注解的方式獲取控件可能出現(xiàn)異常,用這里演示的方式獲取控件就不會(huì)有這個(gè)問(wèn)題。

5、給控件綁定內(nèi)容

上一步我們直接給TextView設(shè)置了字符串,這里換一個(gè)更MVVM的實(shí)現(xiàn)方式,首先在xml文件中的data標(biāo)簽中添加一個(gè)variable標(biāo)簽,如下所示:

<data class="TestBinding">
    <variable
        name="textString"
        type="String" />
</data>

搞定之后給TextView添加一個(gè)屬性android:text="@{textString}",之后在MainActivity.java文件中把binding.tvMain.setText("測(cè)試");改為binding.setVariable(BR.*textString*, "測(cè)試");,運(yùn)行代碼,我們會(huì)發(fā)現(xiàn)效果還是一樣,這里把一個(gè)字符串“測(cè)試”以textString為名稱(chēng)注入到xml文件里,并在xml文件里完成了對(duì)TextView內(nèi)容的設(shè)置。

6、給控件綁定數(shù)據(jù)對(duì)象

編寫(xiě)一個(gè)數(shù)據(jù)類(lèi)(省略set和get方法):

public class User {
    private String userName;
    private String avatar;
        。。。
}

這時(shí)在xml文件中的data標(biāo)簽中再加一個(gè)variable標(biāo)簽:

<variable
    name="userInfo"
    type="com.gegz.gdatabindingdemo2.model.UserInfo" />

并新增一個(gè)TextView,給它設(shè)置內(nèi)容android:text="@{userInfo.userName}",之后在MainActivity.java中再給它賦一下值:

UserInfo userInfo = new UserInfo();
userInfo.setUserName("Android小郭");
userInfo.setAvatar("<https://profile.csdnimg.cn/C/B/6/1_wccadab>");
binding.setVariable(BR.userInfo, userInfo);

這時(shí)運(yùn)行項(xiàng)目,我們會(huì)發(fā)現(xiàn)新的TextView也正常顯示了內(nèi)容。

7、使用工具類(lèi)

有時(shí)我們需要把原始數(shù)據(jù)使用工具方法處理一下再顯示,這里我們演示一下在xml文件中使用工具類(lèi)的方式,首先我們要先創(chuàng)建一個(gè)工具類(lèi)Utils:

public class Utils {
    public static String processString(String original) {
        return original.toUpperCase();
    }
}

接下來(lái)我們?cè)赿ata標(biāo)簽中添加一個(gè)import標(biāo)簽來(lái)導(dǎo)入工具類(lèi):

<data class="TestBinding">
      <import type="com.gegz.gdatabindingdemo2.utils.Utils" />
    <variable
        name="userInfo"
        type="com.gegz.gdatabindingdemo2.model.UserInfo" />
</data>

再把TextView的text屬性改成android:text="@{Utils.processString(userInfo.userName)}",運(yùn)行之后我們發(fā)現(xiàn)Android小郭的字母全部變成大寫(xiě)了,說(shuō)明工具類(lèi)起到了作用。

這里注意,java.lang.*包下的所有內(nèi)容都是默認(rèn)導(dǎo)入的,可以直接使用。

8、使用Context屬性

在Utils工具類(lèi)中添加以下方法:

public static String isMainActivity(Context context) {
    if (context instanceof MainActivity) {
        return "是MainActivity";
    }
    return "不是MainActivity";
}

新增一個(gè)TextView,把text屬性設(shè)置成android:text="@{Utils.isMainActivity(context)}",運(yùn)行起來(lái)發(fā)現(xiàn)在MainActivity中這個(gè)工具方法顯示出了“是MainActivity”,說(shuō)明在xml中這個(gè)默認(rèn)存在的參數(shù)context是View所在的context對(duì)象。

9、處理同名工具類(lèi)

新建一個(gè)工具類(lèi)包utils2,在里面再建一個(gè)Utils工具類(lèi),并提供一個(gè)isMainActivity2方法:

public class Utils {
    public static String isMainActivity2(Context context) {
        if (context instanceof MainActivity) {
            return "是MainActivity";
        }
        return "不是MainActivity";
    }
}

在xml文件中把兩個(gè)Utils都導(dǎo)入進(jìn)來(lái):

<import
    type="com.gegz.gdatabindingdemo2.utils.Utils" />
<import
    type="com.gegz.gdatabindingdemo2.utils2.Utils" />

這時(shí)發(fā)現(xiàn)isMainActivity方法報(bào)錯(cuò)了,因?yàn)閡tils2包下的Utils類(lèi)中沒(méi)有這個(gè)方法,這時(shí)我們給兩個(gè)Utils分別添加一個(gè)別名alias屬性:

<import
    alias="Utils"
    type="com.gegz.gdatabindingdemo2.utils.Utils" />
<import
    alias="Utils2"
    type="com.gegz.gdatabindingdemo2.utils2.Utils" />

代碼就恢復(fù)正常了,并且使用Utils2也可以調(diào)用到它的isMainActivity2方法。

10、事件綁定

我們常用的OnClickListener、TextWatcher其實(shí)也是一種對(duì)象,既然是一種對(duì)象,那么它就可以作為DataBinding的setVariable方法的參數(shù),如下我們先在xml文件里添加引用和點(diǎn)擊監(jiān)聽(tīng):

<variable
    name="clickListener"
    type="android.view.View.OnClickListener" />
...
<Button
    android:id="@+id/btn_main_click"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="@{clickListener}"
    android:text="@string/string_main_click"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/layout_main_map_update" />

然后在MainActivity中給DataBinding設(shè)置clickListener:

View.OnClickListener onClickListener = v -> {
    Toast.makeText(MainActivity.this, "測(cè)試點(diǎn)擊事件", Toast.LENGTH_SHORT).show();
};
binding.setVariable(BR.clickListener, onClickListener);

運(yùn)行起來(lái)后點(diǎn)擊按鈕,發(fā)現(xiàn)我們點(diǎn)擊按鈕后Toast能正常顯示。

11、另一種調(diào)用方式

在這一章的內(nèi)容中我們常常用到setVariable方法,這個(gè)方法有另一種寫(xiě)法,我們這個(gè)方法的第一個(gè)參數(shù)是一個(gè)定義在BR中的variableId,我們把這個(gè)id首字母大寫(xiě)并在前方添加set作為方法名,用DataBinding對(duì)象調(diào)用,會(huì)發(fā)現(xiàn)DataBinding中有這個(gè)方法,而且這樣的調(diào)用方式和setVariable是完全一樣的,如下所示:

// binding.setVariable(BR.clickListener, onClickListener);
binding.setClickListener(onClickListener);

可以看到,把BR.clickListener中的clickListener首字母大寫(xiě)并添加set,就是我們下面調(diào)用的setClickListener方法。這個(gè)方法同樣可以設(shè)置對(duì)象,我們可以在這兩個(gè)方法中選擇自己覺(jué)得方便的一種。

12、設(shè)置預(yù)覽值

在我們開(kāi)發(fā)的過(guò)程中,我們希望能在Android Studio中直接看到文字的大小、顏色、位置等,方便我們?nèi)フ{(diào)整,這個(gè)時(shí)候就可以利用DataBinding中的default屬性,它不僅能讓我們直接在Android Studio中看到文字,而且運(yùn)行過(guò)程中并不會(huì)顯示,不會(huì)影響用戶(hù)看到的界面,這里我們就嘗試使用一下default屬性,首先在xml中添加以下內(nèi)容:

<variable
    name="defaultValue"
    type="String" />

...

<TextView
    android:id="@+id/tv_main_default"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="15dp"
    android:text="@{defaultValue,default=默認(rèn)值}" />

然后我們可以在Design窗口中看到這個(gè)TextView的內(nèi)容,而且這個(gè)時(shí)候我們?nèi)ミ\(yùn)行的話(huà)會(huì)看到這個(gè)內(nèi)容并沒(méi)有顯示在界面上。

下章預(yù)告

DataBinding是一個(gè)很大的組件,到這里我們已經(jīng)學(xué)了不少有關(guān)的內(nèi)容,但是還有很多內(nèi)容還沒(méi)有涉及,比如數(shù)據(jù)綁定、運(yùn)算符的使用、自定義屬性等,這些都會(huì)在后續(xù)的文章中慢慢討論。

參考文檔:

CSDN:Android DataBinding 運(yùn)算符、BindingAdapter、 BindingConversion --xiaow

CSDN:Android 安卓DataBinding(九)·運(yùn)算符 --第三女神程憶難

CSDN:Android DataBinding的基本使用 -- 尹中文

CSDN:Android DataBinding 從入門(mén)到進(jìn)階,看這一篇就夠 -- 程序員一東

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容