MVVM的基本使用

MVVM的基本概念

項目源碼地址: https://github.com/corffen/MVVMDemo

1.什么是MVVM?

它是model-view-viewmodle的縮寫

為何要用MVVM?

需求不斷地提出,應用一天比一天復雜,fragment和activity開始膨脹,逐漸變得難以理解和擴展,這個時候控制器層需要做功能拆分.
MVVM能能夠很好的把控制器里的臃腫代碼放入到布局文件里,很容易地看出哪些是動態(tài)界面,同時抽出部分動態(tài)控制器代碼放入viewModel中.

MVVM的基本使用

1.在build.gradle文件中添加

`

     dataBinding {
        enabled = true
    }

`

2.在布局文件中

將根目錄結構換成layout

如:
`

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
>

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

    <TextView
        android:gravity="center"
        android:text="基本使用"
        android:id="@+id/tv_title"
        android:layout_gravity="center_vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>
</layout>

`

3.在Activity或者Fragment中初始化DataBinding

`

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ActivityBasicBinding activityBasicBinding = DataBindingUtil.setContentView(this, R.layout.activity_basic);
    activityBasicBinding.tvTitle.setText("我不需要findviewbyid");
}

`
運行效果:

[圖片上傳失敗...(image-a9e259-1524712938506)]

可以看到我們并沒有初始化TextView,直接使用DataBinding的打點調(diào)用控件就行了.

上面的ActivityBasicBinding這個東西其實是根據(jù)我們的布局文件名字自動生成的.

這樣我們不需要手動的去寫一大堆的findviewbyid了,當然它的功能遠不止這些.

我們想要的效果是view可以跟數(shù)據(jù)綁定起來,當數(shù)據(jù)變化的時候,view會自己刷新UI

4.view綁定數(shù)據(jù)

假設我們需要的是TextView顯示的內(nèi)容是一個bean的屬性,比如User類中的一個name

那么可以向下面這樣去寫

首先在布局文件中添加
data標簽

`

<data>
    <variable
        name="user"
        type="com.corffen.mvvmdemo.User"/>
</data>

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

    <TextView
        android:id="@+id/tv_view_bind_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:gravity="center"
        android:text="@{user.name}"/>

`

然后使用@{}語法糖去設置屬性

最后在Activity中將我們的bean類設置給DataBinding

`

final ActivityViewBindDataBinding activityViewBindDataBinding = DataBindingUtil.setContentView(this, R.layout
            .activity_view_bind_data);
    mUser = new User("綁定數(shù)據(jù)啊");
    activityViewBindDataBinding.setUser(mUser);

`
運行效果如圖

image

我們并沒有調(diào)用textview的setText函數(shù),就可以直接顯示數(shù)據(jù)了.

現(xiàn)在我們點擊"改變數(shù)據(jù)按鈕",更改bean的屬性
添加如下:
`

  activityViewBindDataBinding.btnChangeData.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            count++;
            mUser.setName("我改變了數(shù)據(jù)" + count);
        }
    });

`

發(fā)現(xiàn)點擊按鈕之后,textview并沒有更改數(shù)據(jù).
這是因為我們在點擊按鈕時,數(shù)據(jù)bean類,已經(jīng)改變了,但是view并不知道,所以
我們在點擊事件里再添加一行代碼
`

mUser.setName("我改變了數(shù)據(jù)" + count);
activityViewBindDataBinding.setUser(mUser);

`

再次運行如圖:

[圖片上傳失敗...(image-14fdc3-1524712938506)]

也就是說我們在點擊事件中,重寫給DataBinding設置了數(shù)據(jù)bean類,當數(shù)據(jù)變化時,需要通知view,view就可以正確的顯示我們想要的數(shù)據(jù)了.

那么這個setUser方法到底干什么了.點擊源碼進去可以看到
`

public void setUser(@Nullable com.corffen.mvvmdemo.User User) {
    this.mUser = User;
    synchronized(this) {
        mDirtyFlags |= 0x1L;
    }
    notifyPropertyChanged(BR.user);
    super.requestRebind();
}

`

調(diào)用了一個
notifyPropertyChanged(BR.user);這樣的方法去刷新數(shù)據(jù).那我們總不能再數(shù)據(jù)每次改變的時候,都去調(diào)用一次 activityViewBindDataBinding.setUser(mUser);這樣的方法,這也太low了.

BaseObservable的使用

首先讓我們的bean類繼承自BaseObservable,然后在get方法上添加一個@Bindable

然后在set方法中調(diào)用notifyChange(); 修改如下:
`

public class User2 extends BaseObservable {
private String name;

public User2(String name) {
    this.name = name;
}

@Bindable
public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
    notifyChange();
}
}

`

布局文件可以不用動,然后我們把點擊事件中的DataBinding.setuser方法屏蔽掉,運行結果:

[圖片上傳失敗...(image-228b6e-1524712938506)]

實現(xiàn)了與上面的一樣的效果.

點擊事件的實現(xiàn)

首先在布局文件中添加
`

  <Button
        android:id="@+id/btn_change_data3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:onClick="@{() ->user3.clickMe()}"
        android:text="@{user3.btnContent}"/>

`

上面text使用了User中btnContent屬性,然后還多了一個onClick
這是什么鬼?
這是給Button設置了一個點擊事件,具體的實現(xiàn)邏輯在User3中,如下:
`

 public void clickMe() {
    count++;
    setBtnContent("點我啊" + count);
}

然后在Activity中給DataBinding設置Data:

 @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ActivityClickBinding activityClickBinding = DataBindingUtil.setContentView(this, R.layout.activity_click);
    activityClickBinding.setUser3(new User3("我是點擊事件", "點我啊"));
}

`
運行效果:

image

可以看到Activity中的代碼基本不用怎么寫了,看起來很清爽,因為業(yè)務邏輯交給布局和model層去處理了,但是有一個問題,就是我們在model層做了邏輯處理,因為view和data交互的時候,data不可避免的要關心顯示問題,這就違背了單一原則,為了解決這個問題,我們想起了一句名言
“計算機科學領域的任何問題都可以通過增加一個間接的中間層來解決”

ViewModel

我們希望的是view作為界面的顯示,而bean類只作為基本的數(shù)據(jù),不去參與具體的邏輯處理.
如下代碼:
`

public class User4 {

private String btnContent;

public String getBtnContent() {
    return btnContent;
}

public void setBtnContent(String btnContent) {
    this.btnContent = btnContent;
}

public User4(String btnContent) {
    this.btnContent = btnContent;
}

}
`

所以我們還需要創(chuàng)建一個中間層來處理相應的業(yè)務邏輯.
`

public class ClickViewModel extends BaseObservable {

private User4 mUser4;
private int count;

public ClickViewModel(User4 user4) {
    mUser4 = user4;
    count = 0;
}

@Bindable
public String getBtnContent() {
    return mUser4.getBtnContent();
}

public void setBtnContent(String content) {
    mUser4.setBtnContent(content);
    notifyChange();
}

public void clickMe() {
    count++;
    setBtnContent("點我啊" + count);
}
}

`

viewModel持有User4類,因為我們需要在數(shù)據(jù)變化的時候通知界面去刷新UI,所以我們的viewModel繼承BaseObservable,并給需要的BtnContent設置了@Bindable

然后看一下布局
`

    <data>
    <variable
        name="clickViewModel"
        type="com.corffen.mvvmdemo.ClickViewModel"/>
</data>

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

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:gravity="center"
        android:text="我是ClickViewModel界面"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:onClick="@{() ->clickViewModel.clickMe()}"
        android:text="@{clickViewModel.btnContent}"/>
</LinearLayout>
</layout>

注意到android:text="@{clickViewModel.btnContent}",而我們的viewModel并沒有提供btnContent這個屬性,實際上他是調(diào)用了getBtnContent()也就是說他是方法的簡寫.另外值得一提的是我們在Activity中給DataBinding設置viewModel時,使用的方法 如下:

   activityClickWithViewModelBinding.setClickViewModel(new ClickViewModel(new User4("點我啊!")));
    activityClickWithViewModelBinding.executePendingBindings();

`
這里的setClickViewModel其實是布局文件中data標簽下的name屬性來的.
在數(shù)據(jù)變化時,調(diào)用notifyChange,在讀取數(shù)據(jù)的方法上加上@Bindable注解.

多個property的ViewModel

在上一個例子當中,我們要的數(shù)據(jù)是User4中的一個屬性,我們做的地方首先是viewModel繼承了BaseObservable,然后提供了get(需要@Bindable注解)和set(需要notifyChange)方法,這樣看起來好像有些麻煩,而且有時候我們自定義的ViewModel類可能繼承了別的類,比如我們在封裝的時候,ViewModel可能已經(jīng)繼承了BaseViewModel,這樣我們就不能再繼承BaseObservable了,這樣數(shù)據(jù)變化了,UI便不能更新了.
所以系統(tǒng)提供了Observable系列的封裝類,供我們?nèi)ナ褂?

比如我們有多個屬性
`

   <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:onClick="@{() ->multiPropertyViewModel.click1()}"
        android:text="@{multiPropertyViewModel.content1}"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:onClick="@{() ->multiPropertyViewModel.click2()}"
        android:text="@{multiPropertyViewModel.content2}"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:onClick="@{() ->multiPropertyViewModel.click3()}"
        android:text="@{multiPropertyViewModel.content3}"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:onClick="@{() ->multiPropertyViewModel.click4()}"
        android:text="@{multiPropertyViewModel.content4 }"/>

我們的viewModel類可以這樣去寫

public class MultiPropertyViewModel {

private int count1 = 0, count2 = 0, count3 = 0, count4 = 0;
private ObservableField<String> mContent1 = new ObservableField<>("第一個按鈕");
private ObservableField<String> mContent2 = new ObservableField<>("第二個按鈕");
private ObservableField<String> mContent3 = new ObservableField<>("第三個按鈕");
private ObservableField<String> mContent4 = new ObservableField<>("第四個按鈕");

public MultiPropertyViewModel() {
}

public ObservableField<String> getContent1() {
    return mContent1;
}

public ObservableField<String> getContent2() {
    return mContent2;
}

public ObservableField<String> getContent3() {
    return mContent3;
}

public ObservableField<String> getContent4() {
    return mContent4;
}

public void click1() {
    count1++;
    mContent1.set("第一個按鈕點擊了" + count1);
}

public void click2() {
    count2++;
    mContent2.set("第二個按鈕點擊了" + count2);
}

public void click3() {
    count3++;
    mContent3.set("第三個按鈕點擊了" + count3);
}

public void click4() {
    count4++;
    mContent4.set("第四個按鈕點擊了" + count4);
}

}

`
不需要繼承自BaseObservable,然后定義4個ObservableField<String>,提供get方法
這里注意一個問題是,get方法一定要返回ObservableField<String>而不是String類的,否則看不到數(shù)據(jù)的更新,然后點擊按鈕的時候,我們更改了數(shù)據(jù),就調(diào)用
ObservableField<String>的set方法,運行效果如下圖:

image

BindingAdapter的使用

經(jīng)常有一個這樣的場景,比如我有一個TextView,用來顯示一段文字,每當數(shù)據(jù)變化的時候,我都需要調(diào)用此控件的setText方法,我有一個ImageView,在不同的狀態(tài)時,需要顯示不同的圖片,就去調(diào)用類似setDrawable這樣的方法,而對于加載網(wǎng)絡圖片,只需要知道url就行了.然后使用開源框架,去加載圖片.
對于這些常見的場景,我們能不能只改變數(shù)據(jù),讓控件自動去正確的顯示我們需要的數(shù)據(jù).

我們先來看一下下面的代碼
@BindingAdapter("imageUrl")
public static void setImageUrl(ImageView imageView, String url) {
Context context = imageView.getContext();
Glide.with(context).load(url).
into(imageView);
}
這是一個使用Glide加載圖片的方法,方法上面有一個注解BindingAdapter,還有一個參數(shù)imageUrl.
這個參數(shù)其實是一個屬性的意思,就是說我們給ImageView設置了一個自定義個屬性imageURL,當我們給ImageView設置這個屬性,并給其值設置一個URL,那么就會調(diào)用這個方法去加載圖片.
在布局文件中我們就可以使用

    <ImageView
        android:id="@+id/iv_net"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:imageUrl="@{viewModel.imageUrl}" />

然后我們只需要關注viewModel.imageUrl這個值的變化就可以更改ImageView顯示的圖片了.上面的app:imageUrl 可以理解為,我們自定義了一個控件ImageView,然后寫了一個imageUrl的自定義屬性,它的值是String類型的,然后給這個屬性設置值時,就去執(zhí)行相應的@BindingAdapter的方法了.

其實我們在使用TextView時,會見到這樣的一個寫法

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{viewModel.textContent}" />

當我們的viewModel.textContent值變化時,就會顯示對應的text.假設我們有一個需求就是讓我們的TextView每次都顯示大寫的內(nèi)容.改怎么做呢?

這里的text,其實是系統(tǒng)提供給我們的一個BindingAdapter寫法.我們可以仿照系統(tǒng)提供的方法,然后自定義一個@BindingAdapter方法

  @BindingAdapter("android:text")
    public static void setText(TextView view, CharSequence text) {
        final CharSequence oldText = view.getText();
        if (text == oldText || (text == null && oldText.length() == 0)) {
            return;
        }
        if (text instanceof Spanned) {
            if (text.equals(oldText)) {
                return; // No change in the spans, so don't set anything.
            }
        } else if (!haveContentsChanged(text, oldText)) {
            return; // No content changes, so don't set anything.
        }
        //下面這句代碼,就是我們加進去的
        CharSequence upperText = ((String) text).toUpperCase();
        view.setText(upperText);
    }

我們在設置內(nèi)容時,它的值就會變成大寫的了.運行效果如圖:

源碼在GitHub中可以查看

RecyclerView的使用

Rv是使用頻率非常高的一個控件了,我們在使用它時,無非是當數(shù)據(jù)源變化時寫Adapter去適配其顯示的方式.對Rv來說也就是說,給我們一個數(shù)據(jù)源,我就知道如何去顯示自己的內(nèi)容了.
所以利用上一節(jié)的內(nèi)容,可以寫以下的一個方法.

  @BindingAdapter("adapter")
    public static void setAdapterForRv(RecyclerView rv, List<String> datas) {
        RvAdatper adapter = (RvAdatper) rv.getAdapter();
        if (adapter != null) {
            adapter.clearItems();
            adapter.addItems(datas);
        }
}

這里說一下這個方法,可以寫很多的重載方法,當給不同的數(shù)據(jù)源時,Rv去尋找對應的方法.比如我還有一個以下的方法

  @BindingAdapter("adapter")
    public static void setAdapterForRv(RecyclerView rv, List<Bean> datas) {
        RvAdatper adapter = (RvAdatper) rv.getAdapter();
        if (adapter != null) {
            adapter.clearItems();
            adapter.addItems(datas);
        }
    }

上面給adapter提供的兩個方法如下:

public void addItems(List<String> datas) {
    this.mDatas.addAll(datas);
    notifyDataSetChanged();
}

public void clearItems() {
    mDatas.clear();
}

對了這種@BindingAdapter的方法一定需要是靜態(tài)的,而且它放在任何類都可以,所以為了統(tǒng)一,我們經(jīng)常會將這樣的方法,統(tǒng)一放在一個類里.比如BindingUtils類.
寫好上面的方法后,在布局中就可以像下面這樣寫

  <android.support.v7.widget.RecyclerView
            android:id="@+id/rv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:adapter="@{viewModel.mStringObservableArrayList}"
            tools:listitem="@layout/item_string_rv">

        </android.support.v7.widget.RecyclerView>

上面的adapter就是我們給Rv自定義的屬性,它的值是一個List<String>,它有點特殊,因為我們需要觀察數(shù)據(jù)變化時去刷新UI,所以我們用了一個ObservableArrayList,這個跟上面講的ObservableField系列沒啥不同,它只是用來存放List類型的數(shù)據(jù)而已.

 tools:listitem="@layout/item_string_rv" 這行代碼是用來告訴IDE,我們在as中預覽到我們的item而已.

所以在寫好常規(guī)的adapter中后,我們只需要關注數(shù)據(jù)源的變化就可以了.

public class RvViewModel extends BaseObservable {

    public final ObservableArrayList<String> mStringObservableArrayList = new
            ObservableArrayList<>();

    private int count = 0;

    public RvViewModel() {

    }

    public void setData() {
        for (int i = 0; i < 9; i++) {
            mStringObservableArrayList.add("我是item" + i);
        }
    }

    public void addData() {
        count++;
        mStringObservableArrayList.add("我是添加的數(shù)據(jù)" + count);
    }

    public void clearData() {
        mStringObservableArrayList.clear();
    }

    public void clearOneData() {
        if (mStringObservableArrayList.size() > 0) {
            mStringObservableArrayList.remove(mStringObservableArrayList.size() - 1);
        }
    }
}

在給Activity中設置Rv的使用如下啊:

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        activityRvBinding = DataBindingUtil.setContentView(this, R.layout
                .activity_rv);
        mRvViewModel = new RvViewModel();
        activityRvBinding.setViewModel(mRvViewModel);
        //給數(shù)據(jù)源初始化數(shù)據(jù)
        mRvViewModel.setData();
        setAdapter();
    }

    private void setAdapter() {
        LinearLayoutManager manager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL,
                false);
   //注意這里并沒有在構造中設置數(shù)據(jù)源
        mRvAdatper = new RvAdatper(new ArrayList<String>());
        activityRvBinding.rv.setLayoutManager(manager);
        activityRvBinding.rv.setAdapter(mRvAdatper);
}

然后運行效果如圖:

LiveData和MultiLiveData

http://www.itdecent.cn/p/13a855ceaf2b
這里有一篇鏈接,講的挺好的.

LiveData 定義

假設有這樣的一個場景,我在Activity中的onCreate方法中執(zhí)行了一段異步加載數(shù)據(jù)的邏輯,
當加載完數(shù)據(jù)之后,需要刷新我的界面.可是我在加載數(shù)據(jù)尚未完成的時候,屏幕旋轉(zhuǎn),導致我們重新加載數(shù)據(jù),異步線程加載的數(shù)據(jù)要在一個已經(jīng)銷毀的控件上顯示會導致錯誤.這樣的解決方式,我們有多種,而我們要講的主題是LiveData.

簡單地說,LiveData是一個數(shù)據(jù)持有類。它具有以下特點:

  • 數(shù)據(jù)可以被觀察者訂閱;

  • 能夠感知組件(Fragment、Activity、Service)的生命周期;

  • 只有在組件出于激活狀態(tài)(STARTED、RESUMED)才會通知觀察者有數(shù)據(jù)更新;

LiveData的優(yōu)點

從LiveData具有的特點,我們就能聯(lián)想到它能夠解決我們遇到的什么問題。LiveData具有以下優(yōu)點:

  • 能夠保證數(shù)據(jù)和UI統(tǒng)一
    這個和LiveData采用了觀察者模式有關,LiveData是被觀察者,當數(shù)據(jù)有變化時會通知觀察者(UI)。

  • 減少內(nèi)存泄漏
    這是因為LiveData能夠感知到組件的生命周期,當組件處于DESTROYED狀態(tài)時,觀察者對象會被清除掉。

  • 當Activity停止時不會引起崩潰
    這是因為組件處于非激活狀態(tài)時,不會收到LiveData中數(shù)據(jù)變化的通知。

  • 不需要額外的手動處理來響應生命周期的變化
    這一點同樣是因為LiveData能夠感知組件的生命周期,所以就完全不需要在代碼中告訴LiveData組件的生命周期狀態(tài)。

  • 組件和數(shù)據(jù)相關的內(nèi)容能實時更新
    組件在前臺的時候能夠?qū)崟r收到數(shù)據(jù)改變的通知,這是可以理解的。當組件從后臺到前臺來時,LiveData能夠?qū)⒆钚碌臄?shù)據(jù)通知組件,這兩點就保證了組件中和數(shù)據(jù)相關的內(nèi)容能夠?qū)崟r更新。

  • 針對configuration change時,不需要額外的處理來保存數(shù)據(jù)
    我們知道,當你把數(shù)據(jù)存儲在組件中時,當configuration change(比如語言、屏幕方向變化)時,組件會被recreate,然而系統(tǒng)并不能保證你的數(shù)據(jù)能夠被恢復的。當我們采用LiveData保存數(shù)據(jù)時,因為數(shù)據(jù)和組件分離了。當組件被recreate,數(shù)據(jù)還是存在LiveData中,并不會被銷毀。

  • 資源共享
    通過繼承LiveData類,然后將該類定義成單例模式,在該類封裝監(jiān)聽一些系統(tǒng)屬性變化,然后通知LiveData的觀察者,這個在繼承LiveData中會看到具體的例子。

3.11.3 LiveData的基本使用

首先在項目工程里添加依賴
`

// view model
implementation "android.arch.lifecycle:extensions:1.1.0"
annotationProcessor "android.arch.lifecycle:compiler:1.1.0"

`

然后在根目錄工程里添加

allprojects {
    repositories {
        google()
//把這行添加進去
        maven { url 'https://maven.google.com' }
        jcenter()
    }
}

最后執(zhí)行使用流程.假設我現(xiàn)在要實現(xiàn)下面這樣的一個需求.
最上面的TextView用于顯示文本,下面的改變數(shù)據(jù)按鈕,點擊時改變文本的顯示.
最下面的列表是RecyclerView,點擊上面的改變數(shù)據(jù)集合,RecyclerView就顯示對應的數(shù)據(jù)源.
當點擊item時,最上面的Textview就顯示點擊的item的內(nèi)容.

具體實現(xiàn)細節(jié)

這里只講列表數(shù)據(jù)的關聯(lián)變化,按鈕點擊的變化比較簡單,可以看看源碼就明白了.

首先我們在ViewModel中定義一個

public final ObservableArrayList<String> mStringObservableArrayList = new
            ObservableArrayList<>();

用于提供列表的數(shù)據(jù).
然后定義一個系統(tǒng)提供的繼承自LiveData的MutableLiveData.
它持有的數(shù)據(jù)類型為List<String>

 private MutableLiveData<List<String>> mSourceDatas;

提供一個get方法

  public MutableLiveData<List<String>> getSoureceDatas() {
        if (mSoureceDatas == null) {
            mSoureceDatas = new MutableLiveData<>();
        }
        return mSoureceDatas;
}

在Activity中將LiveData與我們的組件(Activity)關聯(lián)起來.

  mLiveDataViewModel.getSoureceDatas().observe(this, new Observer<List<String>>() {
            @Override
            public void onChanged(@Nullable List<String> strings) {
                mLiveDataViewModel.addTitleToList(strings);
            }
        });

這個observe方法的第一個參數(shù)this,指的是我們的組件Activity,第二個參數(shù)是可觀察的數(shù)據(jù).
這個方法的意思是當我們觀察的數(shù)據(jù)源改變之后,就會回調(diào)這個方法去通知給LiveData.

然后我們實現(xiàn)addTitleToList這個方法,

  public void addTitleToList(List<String> titles) {
        mStringObservableArrayList.clear();
        mStringObservableArrayList.addAll(titles);
}

其實就是修改我們的列表的數(shù)據(jù)源.
因為進來列表要顯示初始的數(shù)據(jù),所以給mSoureceDatas設置初始數(shù)據(jù)

public void setData() {
    List<String> titls = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        titls.add("我是item" + i);
    }
    mSoureceDatas.setValue(titls);
}

然后給更改數(shù)據(jù)源按鈕提供一個點擊方法.

  public void changeDatas() {
        dataCount++;
        mStringObservableArrayList.clear();
        for (int i = 10 * dataCount; i < 10 + dataCount * 10; i++) {
            mStringObservableArrayList.add("我是改變后的item" + i);
        }
    }

首先清空之前的數(shù)據(jù),,然后添加修改后的數(shù)據(jù).

根據(jù)上面說的,當數(shù)據(jù)源改變的時候,就會通知LiveData,去執(zhí)行observe方法,而Observable系列的數(shù)據(jù)是跟控件綁定的,它會自己刷新UI的.
然后在實現(xiàn)點擊item時,顯示item的值.
首先在Adapter設置一個點擊item的回調(diào)監(jiān)聽,這個就不講了.
然后

   mRvAdatper.setOnclickListener(new RvAdatper.OnItemClickListener() {
            @Override
            public void onItemClick(int position) {
                mLiveDataViewModel.updataContent(position);
            }
        });

當點擊item的時候就去執(zhí)行mLiveDataViewModel.updataContent(position);
它是干嘛的呢?

  public void updataContent(int position) {
        content.set(mStringObservableArrayList.get(position));
    }

content是TextView的數(shù)據(jù)觀察

public final ObservableField<String> content = new ObservableField<>("我是初始值");

當點擊item時,去給content設置item上的內(nèi)容就可以了.

這樣功能就實現(xiàn)了.我們現(xiàn)在來回顧一下,發(fā)現(xiàn),數(shù)據(jù)與UI是完全隔離的,在ViewModel中,只關心數(shù)據(jù)的變化,而UI的各種交互點擊在布局文件中聲明好就行了.這為我們省去了很多”垃圾代碼”.最后使用LiveData又幫我們做了數(shù)據(jù)與組件生命周期的優(yōu)化工作.

這些不就是mvvm的魅力嗎?

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

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

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