前言
在上一篇文章簡(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):
可以看到,Android studio給xml文件里添加了data標(biāo)簽,我們可以直接在里面導(dǎo)入我們需要的類(lèi)或者對(duì)象了。
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)算符 --第三女神程憶難