Android MVVM一步一步來實現(xiàn)

通過本文可以了解到的內(nèi)容:
1.如何使用dataBinding;
2.設置點擊事件(含參數(shù)的);
3.數(shù)據(jù)與UI同步更新;
4.基本的dataBinding語法;
5.自定義注解。

按照官網(wǎng)的文檔一步一步來學習
官方文檔傳送門

簡單開始

第一步,在module的build.gradle文件中添加如下代碼,注意是android節(jié)點下添加:

android {
    ...
    dataBinding {
        enabled = true
    }
}

第二步,增加一個數(shù)據(jù)實體類。

public class UserBean{
    private String firstName;

    public UserBean(String firstName) {
        this.firstName = firstName;
    }
}

第三步,修改layout文件,以layout節(jié)點開始,data節(jié)點包含數(shù)據(jù)實體類,其他就和以前布局文件沒有啥區(qū)別。
給某個textview綁定數(shù)據(jù):android:text="@{user.firstName}",user就是某個實體類在這個文件中的引用名,firstName為這個實體類的某個屬性。

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            // 以下類在本xml文件中的引用名稱
            name="user"
            // 數(shù)據(jù)實體類全名
            type="com.hotelgg.android.hotelggreview.data.UserBean" />
        
        <variable
            name="person"
            type="com.hotelgg.android.hotelggreview.data.PersonBean"/>

        <variable
            name="onclick"
            type="com.hotelgg.android.hotelggreview.handler.ClickHandler"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical">

        <TextView
            android:id="@+id/main_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/red_2"
            android:padding="10dp"
            android:text="@{user.firstName}"
            android:onClick="@{onclick.onClick}"
            android:textColor="@color/white_1" />

        <TextView
            android:id="@+id/person_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/red_2"
            android:layout_marginTop="20dp"
            android:padding="10dp"
            android:text="@{person.name}"
            android:onClick="@{onclick.onClick}"
            android:textColor="@color/white_1" />


    </LinearLayout>

</layout>

第四步,在activity中的使用,ActivityMainBinding的命名規(guī)則是和其綁定的xml文件的命名規(guī)則相關的,例如,activity_main為xml文件的名稱,那么就在其后面增加一個binding,首字母大寫即可。

ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
UserBean userBean = new UserBean("shuai bu shuai");
binding.setUser(userBean);
坑Num.1

注意ActivityMainBinding這個類,編譯器不會自動編譯出來,需要build一下項目這個類才能夠被引用到。

坑Num.2
From the perspective of data binding, these two classes are equivalent. 

這是官網(wǎng)的一段話,我來斷章取義一下,第一句話不知道怎么翻譯,什么鬼從data binding的透明(還能穿透明裝呢)?申明來看?(知道的朋友指點一下)第二句,這兩個類是等同的。然后我就跟著官方文檔一樣就寫了一個數(shù)據(jù)model的構造器。

public class UserBean{
    private String firstName;

    public UserBean(String firstName) {
        this.firstName = firstName;
    }
}

編譯,啪!不出意外,你已經(jīng)得到了ide的一個異常

Error:(31, 29) Could not find accessor com.hotelgg.android.hotelggreview
.data.UserBean.firstName 

應該添加一個get方法的入口,所以不知道官方文檔說的這兩者是等同的是嘛意思!

public class UserBean{
    private String firstName;

    public UserBean(String firstName) {
        this.firstName = firstName;
    }

    public String getFirstName() {
        return firstName;
    }

}

添加點擊事件

第一步,添加一個ClickHandler類,名字隨便取。

public class ClickHandler {
    private BaseActivity context;
    private ActivityMainBinding mDataBinding;

    public ClickHandler(BaseActivity context, ActivityMainBinding mDataBinding) {
        this.context = context;
        this.mDataBinding = mDataBinding;
    }

    public void onClick(View view) {
        if (R.id.main_tv == view.getId()) {
            UserBean bean = mDataBinding.getUser();
            bean.setFirstName("perry");
        } else if (R.id.person_name == view.getId()) {
            //  這里先不用理會
            PersonBean bean = mDataBinding.getPerson();
            bean.name.set("mary");
        }

    }

}

第二步,在activity中添加綁定

binding.setOnclick(new ClickHandler(this, binding));

第三步,對應的xml中添加引用,上面已經(jīng)給出完整代碼。

<data>

        <variable
            name="onclick"
            type="com.hotelgg.android.hotelggreview.handler.ClickHandler"/>
</data>

// textview添加點擊事件
android:onClick="@{onclick.onClick}"

然后,就沒有然后了,可以運行開干了。

點擊事件中傳遞參數(shù)

這種場景主要應用于:一個recyclerview的item在點擊時需要取出當前item的數(shù)據(jù),比如一個當前item的id或者url要傳遞到下一個頁面中去。這個時候從綁定的數(shù)據(jù)中取出這個參數(shù)也有相應的辦法。

<data>

        <import type="com.hotelgg.android.hotelggreview.adapter.UserAdapter.UserAdapterItemClick" />

        <variable
            name="hotelInfo"
            type="com.hotelgg.android.hotelggreview.data.HotelInfoBean" />

        <variable
            name="itemClick"
            type="UserAdapterItemClick" />

    </data>

// 某個控件的點擊方法,其實就是lambda表達式的形式
android:onClick="@{()->itemClick.onClick(hotelInfo)}"

然后在adapter中進行相應的注冊,這里涉及到在recyclerview當中使用,具體的代碼會在后頭貼出。這里只是簡單的演示。

((ItemSingleTextBinding) binding).setItemClick(new UserAdapterItemClick());

public static class UserAdapterItemClick {
    public void onClick(HotelInfoBean bean) {
        LogUtil.e(bean.title.get());
        bean.title.set("change big hotel");
    }
}

這里有一點小小的觀點需要表達一下:我們可以把這里的UI這樣理解,它有自身的數(shù)據(jù)源model,有自身的操作行為handler,而同一個頁面,我更加傾向于讓其使用一個handler,這樣復用性高一些,至于每個控件的數(shù)據(jù)源,我們可以通過傳遞進來的ActivityMainBinding對象進行獲取和改變,UI層面就不需要做除了初始化之外的其他任何工作。所有的邏輯操作全由handler進行。

雖然點擊事件和數(shù)據(jù)綁定都已經(jīng)完成,但是數(shù)據(jù)綁定后,改變數(shù)據(jù)源是不是就能夠直接讓UI發(fā)生相應的更新變化呢?答案是否定的。

數(shù)據(jù)源與UI同步更新

如果想要改變數(shù)據(jù)源后同步更新UI界面,就需要對數(shù)據(jù)源進行一定的修改,我們還是以UserBean為例。

第一種方法
繼承BaseObservable,然后在get方法上加上@Bindable注解,在set方法中加入notifyPropertyChanged(BR.firstName);進行數(shù)據(jù)更新。這個BR有點類似于資源文件引用中的R,也是包名.BR。

public class UserBean extends BaseObservable{
    private String firstName;
    private int num;

    public UserBean(String firstName, int num) {
        this.firstName = firstName;
        this.num = num;
    }

    @Bindable
    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
        notifyPropertyChanged(BR.num);
    }

    @Bindable
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
        notifyPropertyChanged(BR.firstName);
    }
}

第二種方法
用Google給我們提供的Observable...類,支持所有數(shù)據(jù)類型。

public class PersonBean {
    public ObservableField<String> name = new ObservableField<>();
    public ObservableField<String> lastName = new ObservableField<>();
}

// 更新方式
personBean.lastName.set("White");

一些基本的語法

增加默認值
除了指定相應的綁定數(shù)據(jù)對象外,還可以增加默認值,如下:

android:text="@{user.firstName,default=test}"

數(shù)據(jù)類型轉換
類型的轉換,把int型轉化成string類型并進行存儲,其中int值還可以進行相應的計算。

android:text="@{String.valueOf(user.num + 1)}"

使用資源文件屬性
還可以指定資源文件中的屬性,并且直接使用占位符的形式來填充數(shù)據(jù)(這個功能強大)。

android:text="@{@string/name_format(person.name,person.lastName)}"
<resources>
    <string name="name_format">第一個:%s,第二個:%s</string>
</resources>

使用三目運算符

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

不過注意,在導入集合類型時,咱們一般都會使用泛型類,以下泛型類為string類型,因為是基本數(shù)據(jù)類型,所以不用導入。使用泛型類的格式如下:

<data>
    <import type="java.util.List"/>
    <variable name="list" type="List&lt;String&gt;"/>
</data>

自定義注解

這個功能相當之強大,廢話不多說,直接上代碼。先做一個工具類,名字隨便取,如我的AppBindingUtils,具體啥意思,注釋當中已經(jīng)說的很清楚。

public class AppBindingUtils {
    // BindingAdapter注解,自定義注解,可以讓某些控件增加自定義屬性,并且,只要在xml中出現(xiàn)該屬性,
    // 就會調(diào)用該注解注釋的方法。
    // 其中 bind_data 為xml中自定義屬性的屬性名,setDataTest這個名字隨便取。
    // 不讓recyclerView每次都設置adapter,這樣會刷新多余數(shù)據(jù)
    @BindingAdapter("bind_data")
    public static void setDataTest(RecyclerView recyclerView, ObservableArrayList<MultipleListBean> data) {
        if (recyclerView != null && recyclerView.getAdapter() == null) {
            recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext(), LinearLayoutManager.VERTICAL, false));
            recyclerView.setAdapter(new UserAdapter(data));
        }
    }
}

xml布局文件中如下,這樣,當編譯器發(fā)現(xiàn)了xml中有一項自定義注解編注的屬性時,就會帶著這個屬性去找有@BindingAdapter注解編注過并且屬性名為bind_data的方法,并在初始化該控件的時候,調(diào)用這個被注解標注過的方法。

<variable
            name="data"
            type="ObservableArrayList&lt;MultipleListBean&gt;" />

<android.support.v7.widget.RecyclerView
            android:id="@+id/main_recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            bind_data="@{data}" />

在activity當中設置這個數(shù)據(jù)源。

ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
beans = new ObservableArrayList<>();
beans.add(new MultipleListBean(1, bean));
beans.add(new MultipleListBean(1, bean1));
binding.setData(beans);

以上介紹差不多就是dataBinding的一些基礎內(nèi)容的介紹了。歡迎留言討論。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容