簡書地址:www.jianshu.com/p/015ad08c2…
寫在前面
對於android開發者而言,寫冗餘重複的程式碼一直是一件吃力不討好的事情,而資料繫結技術能夠減少大量重複的程式碼,可以說是android開發者的福音。它學習起來十分簡單(相信瞭解過的應該都這麼覺得),但使用起來卻不那麼盡如人意(對不起,binding檔案未找到)。
從16年11月到現在,經過這麼長時間的實踐,除了前4個月在踩坑之外,到現在都沒再遇到DataBinding相關的錯誤,趁年前有些時間,因此總結了一下實際專案中使用DataBinding的一些經驗。
本文重點不在於講解DataBinding語法,這樣的文章夠多了。
如果你對DataBinding稍有興趣,可以看看我以前的文章告別findView和ButterKnife
如果你想學習DataBinding語法,推薦看看泡網的DataBinding專題或者慕課網的視訊
如果你正在使用DataBinding並苦惱於不能稱心如意的使用它,那麼看本文是一個不錯的選擇。
相關程式碼:
DataBinding-AspectJ:github.com/ditclear/Da…
閱讀下文請具備DataBinding相關的基礎知識。
正文
-
首先推薦一款AS外掛DataBinding Support
可以簡化DataBinding的轉換操作並支援和
ViewModel
和與之關聯的layout檔案的跳轉,可以提升開發時的效率,節省時間貼兩張圖看看:
更多功能可檢視此連結:plugins.jetbrains.com/plugin/9271…
-
統一命名的variable
俗話說無法不成章,對於一個團隊而言,不管是大還是小,都需要一套合理的統一的命名規範,既方便相互之間的協作,也減輕了CodeReview時的困難。對實踐了Databinding的團隊格外如此。
bad:
<!--user_activity.xml --> <variable name="uservm" type="io.ditclear.app.viewmodel.UserViewModel"/> <!--student_activity.xml --> <variable name="studentvm" type="io.ditclear.app.viewmodel.StudentViewModel"/> 複製程式碼
better:
<!--user_activity.xml --> <variable name="vm" type="io.ditclear.app.viewmodel.UserViewModel"/> <!--student_activity.xml --> <variable name="vm" type="io.ditclear.app.viewmodel.StudentViewModel"/> 複製程式碼
-
儘可能少的variable和import
也許你是剛接觸DataBinding,按照官網的Guide,很可能會定義一些非必需的variable或者import 一些自定義的靜態類來進行字串處理、View顯示隱藏等等的操作。但是這不是一個好習慣,過多的variable除了會讓你多做幾次無謂的繫結外,資料也將變得難以管理。所以儘可能少的variable和import是一種較好的實踐。
bad:
<data> <import type="com.ditclear.app.util.DateUtil"/> <import type="android.view.View"/> <import type="com.ditclear.app.util.StringUtil"/> <import type="com.ditclear.app.network.model.StudentState"/> <import type="java.math.BigDecimal"/> <import type="com.ditclear.app.presentation.student.StudentActivity"/> <variable name="isShow" type="Boolean"/> <variable name="time" type="java.util.Date"/> <variable name="date" type="java.util.Date"/> <variable name="signTime" type="String"/> <variable name="item" type="com.ditclear.app.network.model.student.StudentItem"/> <variable name="presenter" type="com.ditclear.app.presentation.student.StudentActivity.Presenter"/> </data> 複製程式碼
better:
<data> <variable name="presenter" type="com.ditclear.app.helper.presenter.Presenter"/> <variable name="vm" type="com.ditclear.app.view.student.viewmodel.StudentViewModel"/> </data> 複製程式碼
資料的處理和view的顯示都用ViewModel來處理,比如:
android:text="@{vm.signTime}" android:visibility="@{vm.isShowVisbility}" 複製程式碼
這樣的好處是減輕了layout.xml檔案的複雜程度,xml檔案只用來單純的展示資料, 而複雜的邏輯判斷都放在了ViewModel中,並且為頁面佈局資料提供了唯一的入口,方便檢視和管理資料來源。
-
避免使用複雜的表示式
bad:
在官方的指南里有這樣的寫法:
<data> <import type="com.example.MyStringUtils"/> <variable name="user" type="com.example.User"/> </data> <TextView android:text="@{MyStringUtils.capitalize(user.lastName)}" android:layout_width="wrap_content" android:layout_height="wrap_content"/> 複製程式碼
或者這樣的
android:text="@{String.valueOf(index + 1)}" android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}" android:transitionName='@{"image_" + id}' android:text="@{@string/nameFormat(firstName, lastName)}" android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}" android:text="@{map[`firstName`]}" 複製程式碼
需要注意的是這些都不代表著最佳實踐,只是說明有這樣的功能,支援這樣的用法。
而且它有一個很大的弊端,相信很多剛入門DataBinding的人都遇到過找不到binding檔案的錯,然後被勸退,原因無外乎就是表示式寫錯、variable的類路徑不對或者沒import相應的類(比如View)等等,都與複雜的表示式有關係,而DataBinding報錯也不太智慧,有時不能準確定位,所以建議不要在xml裡進行復雜的資料繫結,這些都儘量放到ViewModel裡進行,xml只繫結簡單的基礎資料型別。
better:
<data> <variable name="vm" type="com.example.UserViewModel"/> </data> <TextView android:text="@{vm.capitalizeLastName}" android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="@{vm.index}" android:visibility="@{vm.showName}" android:transitionName='@{vm.transitionName}'/> 複製程式碼
-
點選事件的命名和處理
先看看DataBinding支援的寫法
android:onClick="@{presenter.onClick()}" //1.方法引用 android:onClick="@{()->presenter.onClick()}" //2.lamda表示式 android:onClick="@{(view)->presenter.onClick(view)}" //3.lamda表示式 android:onClick="@{()->presenter.onClick(item)}"//4.帶引數lamda表示式 android:onClick="@{(view)->presenter.onClick(view, item)}"//5.帶引數lamda表示式 複製程式碼
選擇很多,而且使用方法也是五花八門,有的喜歡直接使用viewmodel裡的方法,有的直接將Activity或者fragment作為handler,還有的可能會在activity/fragment裡寫一個內部類作為presenter,然後由於方法名也可以自定義,所以很可能出現你叫
presenter.save()
另一個叫(v)->handler.onSave(v)
的情況。bad:
<layout> <data> <variable name="vm" type="io.ditclear.app.viewmodel.AnimalViewModel"/> <variable name="handler" type="io.ditclear.app.view.AnimalActivity"/> <variable name="presenter" type="io.ditclear.app.view.AnimalActivity.Presenter" </data> <LinearLayout tools:context="io.ditclear.app.view.AnimalActivity"> <Button android:onClick="@{vm.shoutWhat()}"/> <Button android:onClick="@{(v)->handler.shout(v)}"/> <Button android:onClick="@{()->presenter.onShout()}"/> </LinearLayout> </layout> 複製程式碼
better:
推薦的一種處理方式是使用封裝過的
View.OnClickListener
來統一處理點選事件,包裹一層的目的是為了不依賴於具體實現。public interface Presenter extends View.OnClickListener{ @Override void onClick(View v); } 複製程式碼
xml佈局檔案
<layout> <data> <variable name="vm" type="io.ditclear.app.viewmodel.AnimalViewModel"/> <variable name="presenter" type="io.ditclear.app.helper.Presenter" </data> <LinearLayout tools:context="io.ditclear.app.view.AnimalActivity"> <Button android:id="@+id/save_btn" android:onClick="@{(v)->presenter.onClick(v)}"/> <Button android:id="@+id/delete_btn" android:onClick="@{(v)->presenter.onClick(v)}"/> <Button android:id="@+id/submit_btn" android:onClick="@{(v)->presenter.onClick(v)}"/> </LinearLayout> </layout> 複製程式碼
這裡推薦使用(v)->presenter.onClick(v)的寫法,原因之一是比較直觀一點,其二是需要引數view
接著在activity/fragment中來實現Presenter介面,處理點選事件
public class AnimalActivity extends AppCompatActivity implements Presenter { private AnimalActivityBinding mBinding; private AnimalViewModel mViewModel; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBinding= DataBindingUtil.setContentView(this, R.layout.animal_activity); Animal animal=new Animal("dog",1); mViewModel=new AnimalViewModel(animal); mBinding.setVm(viewModel); mBinding.setPresenter(this); } @SingleClick @Override public void onClick(View v) { //根據id進行區分 switch (v.getId()){ case R.id.save_btn: save(); break; case R.id.delete_btn: delete(); break; case R.id.submit_btn: submit(); break; } } private void submit(){ //呼叫viewModel的方法 mViewModel.submit(); } } 複製程式碼
@SingleClick是一個註解,作為AspectJ的切面,來防止多次點選,需要將view作為引數,詳細可參考文章DataBinding結合AspectJ防止多次點選
如果你使用RxJava和RxLifeCycle來處理資料和管理生命週期,那麼這裡的
submit()
方法將會更加簡單。private void submit(){ //呼叫viewModel的方法 mViewModel.submit() .compose(bindToLifecycle()) .subscribe({ //success },{ //error }) } 複製程式碼
詳細情況可以看這篇文章:Retrofit及RxJava
-
處理RecyclerView 列表項的資料及點選事件
RecyclerView功能極其強大,能做到的事情很多,網上已經出現很多關於多型別RecyclerView的處理方法,在使用DataBinding這一年多時間裡,感受便是使用DataBinding來處理RecyclerView Item再合適不過,充分做到了資料和itemView的完美分離,告別了反覆、冗餘的自定義Adapter,不需要關心太多無意義的事情。
詳情請檢視github地址(kotlin版本):github.com/ditclear/Bi…
一些技巧
-
使用tools來進行預覽
tools可以告訴Android Studio,哪些屬性在執行的時候是被忽略的,只在設計佈局的時候有效。比如我們要讓android:text屬性只在佈局預覽中有效可以這樣
<TextView android:id="@+id/text_main" android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="@style/TextAppearance.Title" android:layout_margin="@dimen/main_margin" android:text="@{vm.title}" tools:text="I am a title" /> 複製程式碼
tools可以覆蓋android的所有標準屬性,將android:換成tools:即可。同時在執行的時候就連tools:本身都是被忽略的,不會被帶進apk中。
-
補全自定義的屬性
比如為ImageView,你定義了一個BindingAdapter
@BindingAdapter("url") public static void bindImgUrl(ImageView imageView,String url){ Glide.with(imageView.getContext()).load(url).into(imageView); } 複製程式碼
實際情況中,ImageView並沒有url這個屬性, 這時可以在attrs.xml檔案中為ImageView新增這一屬性,rebuild一下專案,以後就能自動補全屬性了
<declare-styleable name="ImageView"> <attr name="url" format="string"/> </declare-styleable> 複製程式碼
-
錯誤排查
很多開發者放棄DataBinding原因就在於出錯了不容易排查錯誤。 只顯示出很多XXBinding未找到。 如果有一定使用經驗的就知道只看最後一條報錯資訊就夠了。 這裡介紹一種我經常使用來排查錯誤的方式: 在Android Studio 的terminal 裡執行
./gradlew clean assembleDebug
或者
./gradlew compileDebugJavaWithJavac
因為DataBinding是編譯生成程式碼的,很多錯誤都是xml中表示式寫的有問題導致的,所以執行以上命令容易在terminal中列印出具體錯誤的資訊。這些命令對於需要編譯生成程式碼的框架排查錯誤十分有用,比如Dagger2。
最後
DataBinding使用起來很簡單,但是由於它沒有一個統一的規範和寫法,需要靠開發者自己去摸索和研究才能熟練運用,而這其中又會出現一些小坑,所以可能導致剛學習的人覺得難以駕馭而被迫放棄。但是它卻是一門非常實用的技術,不管你是否使用MVVM架構,單憑它可以減少很多冗餘的程式碼和跟RecyclerView的完美契合的優點就值得去了解和使用它。
關於怎麼較好的實踐,總結一哈:
- 統一命名規範
- xml檔案中避免複雜的表示式
- xml只負責展示文字資料,資料的處理和view的顯示隱藏交給ViewModel去做
- 點選事件封裝一哈,在Activity/Fragment中去處理事件或呼叫ViewModel的方法
在經過一年以上實踐後,總結出了以上的一些避免踩坑的方式和較好的實踐方法,希望對準備學習、正在學習或者正在使用的同學一些幫助。
畢竟對於DataBinding :使用得當,那它就是神兵利器,使用不當,那麼便傷人(Code)傷己。