• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • mvvm設計模式之Data Binding庫的使用

    標簽: mvvm  data binding  設計模式

    DataBinding是google推出的一個mvvm設計模式的官方類庫,想比與mvp設計模式,該模式提供了view與數據直接綁定的能力,今天這票文章就來教大家如何使用該庫。

    準備條件

    1、Android studio版本大于1.3,并且Gradle 版本大于1.5.0-alpha1

    2、在module模塊的build文件中添加下面的代碼塊,然后同步工程

    android {
        ....
        dataBinding {
            enabled = true
        }
    }
    下面開始正式使用。


    布局文件

           布局文件和以前的布局文件不同的是現在的布局文件需要以layout做根布局,然后里面包含兩個部分,一個是節點<data>另一個就是我們以前的布局文件原封不動的代碼,類似與下面這樣:

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
       <data>
           <variable name="user" type="com.example.User"/>
       </data>
       <LinearLayout
           android:orientation="vertical"
           android:layout_width="match_parent"
           android:layout_height="match_parent">
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.firstName}"/>
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.lastName}"/>
       </LinearLayout>
    </layout>

    這里我們需要注意的就是<data>節點,在這里我們定義了一個<variable>子節點,相當于定義了一個變量,該變量可以是一個基本數據類型比如int float,也可以是一個類,比如上面的代碼。我們可以在這個布局文件中使用這個變量的屬性和方法,使用方式為:@{},在大括號中就可以和使用java代碼的方式一樣去使用屬性和調用方法。

    我們來看下User類的代碼:

    public class User {
       public final String firstName;
       public final String lastName;
       public User(String firstName, String lastName) {
           this.firstName = firstName;
           this.lastName = lastName;
       }
    }

    當然你也可以給屬性定義為私有的,然后通過get方法去獲取,不過那樣性能有所降低。

    到這里我們還只是將數據和view綁定起來,還需要在java代碼中將變量賦值,來看代碼:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
       User user = new User("Test", "User");
       binding.setUser(user);
    }

    其實我們如果在布局文件中使用了databinding,那么其自動會為我們生成一個以布局文件名去掉下劃線加Binding結尾的類,向上面的DataBindingUtil返回的MainActivityBinding類,我們也可以使用這個類來給activity和布局綁定并且給其賦值,比如下面這樣:

    MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());
    為了代碼的風格一致和在某些情況下不清楚返回的類名,我推薦大家使用DataBindingUtil這個類來綁定。



    事件處理


    事件處理有兩種方式,一種是方法關聯(Method References)、一種是監聽綁定(Listener Binding).他們兩者的不同之處是后一個在事件觸發的時候才創建回調方法。

    1、方法關聯的使用

    這種方式與以前在onclick方法中寫方法名然后在activity中定義方法類似,不同之處有兩點:第一就是現在可以給回調方法定義在別的類中;第二就是現在能在編譯階段就檢查出回調方法是否編寫錯誤,避免了以前在運行的時候因為找不到回調方法而奔潰。

    首先在xml文件中這樣寫:

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
    
        <data>
            <variable
                name="clickCallBack"
                type="com.example.leixinxue.databindingdemo.ClickCallBack" />
        </data>
    
        <RelativeLayout
            android:id="@+id/activity_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:onClick="@{clickCallBack.onClick}"
                android:paddingBottom="10dp"
                android:paddingTop="10dp"
                android:text="吐司"
                android:textSize="14dp" />
        </RelativeLayout>
    </layout>

    需要指出的是clickCallBack.onClick也可以使用這種方式:clickCallBack::onClick.然后我們來看看這個類是什么樣的:

    public class ClickCallBack {
        public void onClick(View view) {
            Log.e("kk", "-------->點到");
            Toast.makeText(view.getContext(), "點到我啦!", Toast.LENGTH_SHORT).show();
        }
    }
    

    最后還需要在activity中給該類綁定到xml文件中:

     @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
            binding.setClickCallBack(new ClickCallBack());
        }

    我們來看一下運行效果圖:



    2、綁定監聽的使用

    這種方式是使用lambda表達式去書寫,類似于這樣@{(xx)->xxx.xxx(xx,xx,..)},可以選擇多種參數,也可以一個參數也沒有,除了這里的寫法不一樣外,別的也和上面一樣,需要在activity中設置,創建一個xxx類和其帶N個參數的方法。比如這樣:

    <RelativeLayout
            android:id="@+id/activity_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:onClick="@{()->clickCallBack.onClick()}"
                android:paddingBottom="10dp"
                android:paddingTop="10dp"
                android:text="吐司"
                android:textSize="14dp" />
        </RelativeLayout>

    使用了一個沒有參數的方式,假如我們需要參入被點擊的view到CallBack中,我們需要這樣寫:

     <RelativeLayout
            android:id="@+id/activity_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:onClick="@{(view)->clickCallBack.onClick(view)}"
                android:paddingBottom="10dp"
                android:paddingTop="10dp"
                android:text="吐司"
                android:textSize="14dp" />
        </RelativeLayout>

    這里就需要給onClick使用一個帶View的參數,我們也可以給onClick創建多個參數,比如這樣:

        <RelativeLayout
            android:id="@+id/activity_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:onClick="@{(view)->clickCallBack.onClick(task,extraString,view)}"
                android:paddingBottom="10dp"
                android:paddingTop="10dp"
                android:text="吐司"
                android:textSize="14dp" />
        </RelativeLayout>

    在activity中的代碼:

     @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
            binding.setTask(new Task());
            binding.setExtraString("我是額外的信息");
            binding.setClickCallBack(new ClickCallBack());
        }

    需要注意的一點是這種方式得參數的位置別弄錯了,當然我們這里還可以給onclick一個點擊后的返回值,這和定義一個有返回值的方法是一樣的,需要我們自己返回一個值。



    布局文件的細節


    我們可以使用<import>標簽導入類,然后直接使用類名來訪問類,如果類名一致的情況下我們可以給類名一個別名,比如這樣使用:

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
    
        <data>
    
            <import
                alias="CallBack"
                type="com.example.leixinxue.databindingdemo.ClickCallBack" />
    
            <variable
                name="clickCallBack"
                type="CallBack" />
    
            <variable
                name="task"
                type="com.example.leixinxue.databindingdemo.Task" />
    
            <variable
                name="extraString"
                type="String" />
        </data>
    
        <RelativeLayout
            android:id="@+id/activity_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:onClick="@{(view)->clickCallBack.onClick(task,extraString,view)}"
                android:paddingBottom="10dp"
                android:paddingTop="10dp"
                android:text="吐司"
                android:textSize="14dp" />
        </RelativeLayout>
    </layout>

    這種方式和我們在代碼中使用import導入是一樣的。如果是類中有靜態的變量或者類,我們在導入類后就可以直接使用類名.xxx的方式訪問。

    這里有2點需要注意:

    1、變量的默認值是變量類型的默認值,比如引用類型為null,數字為0.

    2、layout中默認定義了一個context的變量,我們直接可以使用



    使用include標簽的時候可以給定義的變量傳遞到include標簽里面去,不過在include中還是得申明變量,不支持<merge>標簽定義變量,使用示例如下:

    <RelativeLayout
            android:id="@+id/activity_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <include
                layout="@layout/a"
                bind:clickCallBack="@{clickCallBack}" />
        </RelativeLayout>
    </layout>

    bind是一個命名空間。


    下面的運算符可以直接在xml文件中使用:

    • Mathematical + - / * %
    • String concatenation +
    • Logical && ||
    • Binary & | ^
    • Unary + - ! ~
    • Shift >> >>> <<
    • Comparison == > < >= <=
    • instanceof
    • Grouping ()
    • Literals - character, String, numeric, null
    • Cast
    • Method calls
    • Field access
    • Array access []
    • Ternary operator ?:

    使用示例:

    android:text="@{String.valueOf(index + 1)}"
    android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
    android:transitionName='@{"image_" + id}'
    
    

    可是下面的不能使用:

    • this
    • super
    • new
    • Explicit generic invocation


    “??”表示不為null,比如:

    android:text="@{user.displayName ?? user.lastName}"

    和這樣的表達式是一樣的:

    android:text="@{user.displayName != null ? user.displayName : user.lastName}"
    
    

    集合數組的使用方式都是用[index]來訪問,如下:

    <data>
        <import type="android.util.SparseArray"/>
        <import type="java.util.Map"/>
        <import type="java.util.List"/>
        <variable name="list" type="List<String>"/>
        <variable name="sparse" type="SparseArray<String>"/>
        <variable name="map" type="Map<String, String>"/>
        <variable name="index" type="int"/>
        <variable name="key" type="String"/>
    </data>
    …
    android:text="@{list[index]}"
    …
    android:text="@{sparse[index]}"
    …
    android:text="@{map[key]}"
    
    

    資源的引用:

    android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
    資源格式化:

    android:text="@{@string/nameFormat(firstName, lastName)}"
    android:text="@{@plurals/banana(bananaCount)}"



    給數據添加一個監聽,實現數據的改變自動改變UI

    添加監聽有三種,分別是:Observable objects, observable fields, and observable collections.

    object方式是給整個類定義一個監聽,如果類中的屬性改變就會導致UI改變,這種方式需要我們寫代碼觸發更新,比較麻煩,google給我們封裝好了一個BaseObservable,我們只需要繼承它就可以了。下面看一個例子,我們點擊按鈕的時候改變綁定textview上面的變量,然后看其是否改變textview的內容。首先看xml代碼:

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
    
        <data>
    
            <variable
                name="mainActivity"
                type="com.example.leixinxue.databindingdemo.MainActivity" />
    
            <variable
                name="inFoType"
                type="com.example.leixinxue.databindingdemo.InfoType" />
        </data>
    
        <LinearLayout
            android:id="@+id/activity_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
    
            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:onClick="@{mainActivity.onClick}"
                android:text="改變文本內容" />
    
            <TextView
                android:id="@+id/txtv"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="@{inFoType.info}" />
        </LinearLayout>
    </layout>

    然后看我們的activity文件:

    public class MainActivity extends AppCompatActivity {
        ActivityMainBinding binding;
        InfoType infoType = new InfoType();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
            binding.setInFoType(infoType);
            binding.setMainActivity(this);
        }
    
        public void onClick(View view) {
            infoType.setInfo("我是改變后的文本!");
        }
    }
    

    最后來看一下我們的Pojo類:

    public class InfoType extends BaseObservable {
        private String info = "我是默認的文本";
    
        @Bindable
        public String getInfo() {
            return info;
        }
    
    
        public void setInfo(String info) {
            this.info = info;
            notifyPropertyChanged(BR.info);
        }
    }

    我們來看下運行效果:



    我們看到完美的改變了,這種方式代碼量少,使用簡單。


    ObservableFields

    這種方式可以直接定義一個自帶改變通知的屬性,目前有:ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, and ObservableParcelable

    實現和上面一樣的功能的例子,首先是pojo文件:

    public class InfoType {
        public final ObservableField<String> info = new ObservableField<>();
    }

    xml布局文件沒有變化,來看activity的代碼:

     @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
            binding.setInFoType(infoType);
            infoType.info.set("我是默認的文本!");
            binding.setMainActivity(this);
        }
    
        public void onClick(View view) {
            infoType.info.set("我是改變后的文本!");
        }

    運行效果是一樣的,這里就不再貼圖了。


    Observable Collections

    這種方式是定義一個雙向綁定的map,還是上面的例子,我們首先是pojo文件:

    public class InfoType {
        //ObservableArrayMap<String, Object> info = new ObservableArrayMap<>();
       public ObservableArrayList<String> info = new ObservableArrayList<>();
    
    }

    然后是布局文件:

    <layout xmlns:android="http://schemas.android.com/apk/res/android">
    
        <data>
    
            <variable
                name="mainActivity"
                type="com.example.leixinxue.databindingdemo.MainActivity" />
    
            <variable
                name="inFoType"
                type="com.example.leixinxue.databindingdemo.InfoType" />
        </data>
    
        <LinearLayout
            android:id="@+id/activity_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
    
            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:onClick="@{mainActivity.onClick}"
                android:text="改變文本內容" />
    
            <TextView
                android:id="@+id/txtv"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="@{inFoType.info[0]}" />
        </LinearLayout>
    </layout>

    最后是activity文件:

      @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
            binding.setInFoType(infoType);
            infoType.info.add("我是默認的文本!");
            binding.setMainActivity(this);
        }
    
        public void onClick(View view) {
            infoType.info.set(0,"我是改變后的文本!");
        }

    運行效果和之前的類似,這里就不同貼出圖片了。



    總結

              到這里databinding的語法基本都介紹完了,類似于google的中文版教程,如果喜歡看英文版本的可以移步到官網:https://developer.android.com/topic/libraries/data-binding/index.html#data_binding_layout_files。google花大力氣就設計這個庫,想必是以后會成為主流的。即使我們的項目引入了該庫,我們也可以控制其風險,因為我們可以在自己想使用的地方去使用,不想使用了我們完全可以按照原來的方式去寫代碼。有的UI和變量綁定實現變量改變UI自動改變的情況使用這個類庫是非常方便的,其實語法也很簡單,和java代碼和類似,我們可以使用邏輯運算符在xml文件中定義一些依賴可以是開發代碼大大減少,推薦大家使用這個類庫!






    歡迎關注我的微信公眾號“android教科書”,最新最好的文章第一時間送到手!可以掃描下面的二維碼來關注:




















    版權聲明:本文為qq379454816原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/qq379454816/article/details/72235950

    智能推薦

    MVC、MVP的區別和MVVM設計模式及實例

        MVVM簡介                MVC和MVP的區別 可能你使用過 MVP 設計模式來對代碼進行解耦, 但是當前谷歌發布 Data Binding 庫來更加簡化我們的代碼 , 也催生了 MVVM 設計模式在 Android 中的引用 . 在 MVP 中, 我們需要 Mo...

    Android架構設計---關于MVVM模式的探討

    一、MVP模式優缺點 在說MVVM之前,簡單回顧一下MVP分層,MVP總共分成三層:  - a 、View: 視圖層,對應xml文件與Activity/Fragment;  - b 、Presenter: 邏輯控制層,同時持有View和Model對象;  - c 、Model: 實體層,負責獲取實體數據。 MVP模式有其很大的優點 1.解耦合,業務邏輯和視圖分離; 2...

    Android---Data Binding使用入門

    Android---Data Binding使用入門 2016年09月22日 14:41:14 閱讀數:466 一直沒使用過Data Binding,今天就來學習一下,有興趣的朋友可以直接看官網的介紹Data Binding 準備工作 首先要在build.gradle中添加一句 1 2 3 添加的位置是這樣的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 然后準備好...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    猜你喜歡

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    Linux C系統編程-線程互斥鎖(四)

    互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...

    統計學習方法 - 樸素貝葉斯

    引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...

    精品国产乱码久久久久久蜜桃不卡