DataBinding實用指南

簡書地址:www.jianshu.com/p/015ad08c2…html

寫在前面

對於android開發者而言,寫冗餘重複的代碼一直是一件吃力不討好的事情,而數據綁定技術可以減小大量重複的代碼,能夠說是android開發者的福音。它學習起來十分簡單(相信瞭解過的應該都這麼以爲),但使用起來卻不那麼盡如人意(對不起,binding文件未找到)。java

從16年11月到如今,通過這麼長時間的實踐,除了前4個月在踩坑以外,到如今都沒再遇到DataBinding相關的錯誤,趁年前有些時間,所以總結了一下實際項目中使用DataBinding的一些經驗。android

本文重點不在於講解DataBinding語法,這樣的文章夠多了。git

若是你對DataBinding稍有興趣,能夠看看我之前的文章告別findView和ButterKnife
若是你想學習DataBinding語法,推薦看看泡網的DataBinding專題或者慕課網的視頻

若是你正在使用DataBinding並苦惱於不能趁心如意的使用它,那麼看本文是一個不錯的選擇。github

相關代碼:

完整示例:github.com/ditclear/Pa…api

DataBinding-AspectJ:github.com/ditclear/Da…markdown

閱讀下文請具有DataBinding相關的基礎知識。架構

正文

  • 首先推薦一款AS插件DataBinding Support

    能夠簡化DataBinding的轉換操做並支持和ViewModel和與之關聯的layout文件的跳轉,能夠提高開發時的效率,節省時間app

    貼兩張圖看看:框架

更多功能可查看此連接: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>
    複製代碼

    attr

  • 錯誤排查

不少開發者放棄DataBinding緣由就在於出錯了不容易排查錯誤。 只顯示出不少XXBinding未找到。 若是有必定使用經驗的就知道只看最後一條報錯信息就夠了。 這裏介紹一種我常用來排查錯誤的方式: 在Android Studio 的terminal 裏運行

./gradlew clean assembleDebug

或者

./gradlew compileDebugJavaWithJavac

由於DataBinding是編譯生成代碼的,不少錯誤都是xml中表達式寫的有問題致使的,因此運行以上命令容易在terminal中打印出具體錯誤的信息。這些命令對於須要編譯生成代碼的框架排查錯誤十分有用,好比Dagger2。

./gradlew compileDebugJavaWithJavac

最後

DataBinding使用起來很簡單,可是因爲它沒有一個統一的規範和寫法,須要靠開發者本身去摸索和研究才能熟練運用,而這其中又會出現一些小坑,因此可能致使剛學習的人以爲難以駕馭而被迫放棄。可是它倒是一門很是實用的技術,無論你是否使用MVVM架構,單憑它能夠減小不少冗餘的代碼和跟RecyclerView的完美契合的優勢就值得去了解和使用它。

關於怎麼較好的實踐,總結一哈:

  • 統一命名規範
  • xml文件中避免複雜的表達式
  • xml只負責展現文本數據,數據的處理和view的顯示隱藏交給ViewModel去作
  • 點擊事件封裝一哈,在Activity/Fragment中去處理事件或調用ViewModel的方法

在通過一年以上實踐後,總結出了以上的一些避免踩坑的方式和較好的實踐方法,但願對準備學習、正在學習或者正在使用的同窗一些幫助。

畢竟對於DataBinding :使用得當,那它就是神兵利器,使用不當,那麼便傷人(Code)傷己。

相關文章
相關標籤/搜索