上一篇文章主要叙述了基本数据类型和引用数据类型在MVVM中的使用。

Android MVVM框架的认识和使用(一)

这篇文章之所以单独拎出来说,是因为还是比较重要的,这篇文章主要讲述一下,RecyclerView在MVVM中使用。

步骤1:
首先创建我们的数据模型:

package com.example.liumengqiang.mvvmoneproject.second;

/**
 * Created by ${liumengqiang} on 2018/7/2.
 */

public class TestSecondModel {
    private String title;

    public TestSecondModel(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

步骤2:然后创建Fragment和ViewModel,这个和上一篇文章一样的,

@NonNull
    private TestSecondFragment findOrCreateViewSecondFragment() {
        TestSecondFragment tasksFragment =
                (TestSecondFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
        if (tasksFragment == null) {
            // Create the fragment
            tasksFragment = TestSecondFragment.newInstance();
            ActivityUtils.addFragmentToActivity(
                    getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
        }
        return tasksFragment;
    }

    private TestSecondViewModel findOrCreateSecondViewModel() {
        // In a configuration change we might have a ViewModel present. It's retained using the
        // Fragment Manager.
        @SuppressWarnings("unchecked")
        ViewModelHolder<TestSecondViewModel> retainedViewModel =
                (ViewModelHolder<TestSecondViewModel>) getSupportFragmentManager()
                        .findFragmentByTag(TEST_ONE_VIEW_MODEL_TAG);

        if (retainedViewModel != null && retainedViewModel.getViewmodel() != null) {
            // If the model was retained, return it.
            return retainedViewModel.getViewmodel();
        } else {
            // There is no ViewModel yet, create it.
            TestSecondViewModel viewModel = new TestSecondViewModel(getApplicationContext());
            // and bind it to this Activity's lifecycle using the Fragment Manager.
            ActivityUtils.addFragmentToActivity(getSupportFragmentManager(), ViewModelHolder.createContainer(viewModel),
                    TEST_ONE_VIEW_MODEL_TAG);
            return viewModel;
        }
    }

步骤3:然后就是XML了:

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <variable
            name="view"
            type="com.example.liumengqiang.mvvmoneproject.second.TestSecondFragment"/>

        <variable
            name="viewModel"
            type="com.example.liumengqiang.mvvmoneproject.second.TestSecondViewModel"/>
    </data>

    <LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent" android:layout_height="wrap_content">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:recycler_view_data="@{viewModel.list}"/>

    </LinearLayout>
</layout>

可以注意,这里XML的RecyclerView设置的是app,也就是自定义的属性,这个属性是recycler_view_data,这个和跟图片设置数据一样的。

步骤4:接下来是Fragment,因为开发者需要给RecyclerView设置LayoutManager和Adapter,所以,在Fragment通过binding对象获取到RecyclerView对象,然后设置LayoutManager和Adapter。

public class TestSecondFragment extends Fragment{

    private RecyclerView recyclerView;
    private TestSecondViewModel viewModel;

    private TestSecondFragmentBinding bind;

    public static TestSecondFragment newInstance() {

        Bundle args = new Bundle();

        TestSecondFragment fragment = new TestSecondFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.test_second_fragment, null, false);
        bind = TestSecondFragmentBinding.bind(view);

        bind.setViewModel(viewModel);
        bind.setView(this);

        initRecycler();

        viewModel.getDataFromServer();

        return view;
    }

    private void initRecycler() {
        recyclerView = bind.recyclerView;
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        recyclerView.setAdapter(new TestSecondAdapter(new TestSecondItemViewModel(getContext())));
    }

    public void setViewModel(TestSecondViewModel viewModel) {
        this.viewModel = viewModel;
    }
}

步骤5:接下来是ViewModel,
1:在ViewModel是获取网络数据,在此,先模拟一下数据。
2:可以预想到,数据源是一个List集合,由于是观察者,所以应该是ObservableList对象。
3:然后,当数据源改变的时候,需要通知UI层,刷新数据,所以应该有一个notify步骤。
基于上述三点,ViewModel代码如下:

package com.example.liumengqiang.mvvmoneproject.second;

import android.content.Context;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.databinding.ObservableArrayList;
import android.databinding.ObservableList;

import com.example.liumengqiang.mvvmoneproject.BR;
import com.example.liumengqiang.mvvmoneproject.MainActivity;
import com.example.liumengqiang.mvvmoneproject.mvvmutil.ViewModelHolder;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by ${liumengqiang} on 2018/7/2.
 */

public class TestSecondViewModel extends BaseObservable {
    private MainActivity nagivator;

    //观察者
    public static ObservableList<TestSecondModel> list = new ObservableArrayList<>();

    private Context context;

    public TestSecondViewModel(Context context) {
        this.context = context;
    }

    /**
     * 网络获取数据(数据源)
     */
    public void getDataFromServer() {
        List<TestSecondModel> testSecondModels = new ArrayList<>();
        for(int i = 0; i < 50; i++) {
            TestSecondModel testSecondModel = new TestSecondModel("title" + i);
            testSecondModels.add(testSecondModel);
        }

        list.addAll(testSecondModels);
        notifyPropertyChanged(BR.empty);
    }

    //刷新RecyclerView的数据
    @Bindable
    public boolean isEmpty() {
        return list.isEmpty();
    }

    public void setNagivator(MainActivity nagivator) {
        this.nagivator = nagivator;
    }
}

步骤6:接下来就是Adapter

  1. 由于创建的是MVVM的思想,工作分明,也就是各干各的事,谁也不参与谁,而ViewModel又是控制器,所以最好在创建一个ItemViewModel, 专门用于RecyclerView的内部操作,比如onClick事件。
  2. 有一点需要明确,重写Adapter时,一定要明确:onCreateViewHolder方法是创建View的,而onBindViewHolder方法 是绑定数据的

明确了上述两点,下面的代码就OK了:

package com.example.liumengqiang.mvvmoneproject.second;

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.example.liumengqiang.mvvmoneproject.BR;
import com.example.liumengqiang.mvvmoneproject.R;
import com.example.liumengqiang.mvvmoneproject.databinding.TestSecondItemBinding;

import java.util.List;

/**
 * Created by ${liumengqiang} on 2018/7/2.
 */

public class TestSecondAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<TestSecondModel> testSecondModelList;

    private TestSecondItemViewModel testSecondItemViewModel;
    public TestSecondAdapter (TestSecondItemViewModel viewModel) {
        this.testSecondItemViewModel = viewModel;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.test_second_item, parent, false);
        TestSecondItemBinding binding = TestSecondItemBinding.bind(view);
        //给XML设置ViewModel.  需要注意的是,setView(View view)  也就是在XMLz
        binding.setVariable(BR.viewModelViewModel, testSecondItemViewModel);
        return new ItemViewHolder(view, binding);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        //给XMl 设置数据源obj
        ((ItemViewHolder)holder).binding.setVariable(BR.obj, testSecondModelList.get(position));
    }

    @Override
    public int getItemCount() {
        return testSecondModelList.size();
    }

    public void updateData(List<TestSecondModel> items) {
        testSecondModelList = items;
        notifyDataSetChanged();
    }

    public class ItemViewHolder extends RecyclerView.ViewHolder {

        public TestSecondItemBinding binding;

        public ItemViewHolder(View itemView, TestSecondItemBinding binding) {
            super(itemView);
            this.binding = binding;
        }
    }
}

步骤7:然后呢就是创建的给Adapter创建的单个的ItemViewModel,

package com.example.liumengqiang.mvvmoneproject.second;

import android.content.Context;
import android.databinding.BaseObservable;
import android.databinding.ObservableField;
import android.widget.Toast;

/**
 * Created by ${liumengqiang} on 2018/7/2.
 */

public class TestSecondItemViewModel extends BaseObservable {

    private Context context;
    public TestSecondItemViewModel(Context context) {
        this.context = context;
    }

    /**
     * 设置的点击事件
     * @param model
     */
    public void onItemClick(TestSecondModel model) {
        Toast.makeText(context, "" + model.getTitle(), Toast.LENGTH_SHORT).show();
    }
}

步骤8:Adapter有了,那么接下来设置数据并且刷新数据了,可以看到RecyclerView声明的XML中,app:recycler_view_data的声明,那么就需要在新建一个Bindings类来给Adapter设置数据,代码如下:

package com.example.liumengqiang.mvvmoneproject.second;

import android.databinding.BindingAdapter;
import android.support.v7.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by ${liumengqiang} on 2018/7/2.
 */

public class TestSecondBinding {
    @BindingAdapter("recycler_view_data")
    public static void setRecyclerViewData(RecyclerView recyclerView, List<TestSecondModel> modelList) {
        TestSecondAdapter adapter = (TestSecondAdapter) recyclerView.getAdapter();
        if(adapter != null) {
            adapter.updateData(modelList);
        }
    }
}

步骤9:接下来就是这个Item的XML文件了:

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <variable
            name="viewModelViewModel"
            type="com.example.liumengqiang.mvvmoneproject.second.TestSecondItemViewModel"/>
        <variable
            name="obj"
            type="com.example.liumengqiang.mvvmoneproject.second.TestSecondModel"/>
    </data>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent" android:layout_height="wrap_content"
        android:onClick="@{() -> viewModelViewModel.onItemClick(obj)}">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:textColor="@color/colorAccent"
            android:text="@{obj.getTitle()}"/>
    </LinearLayout>
</layout>

包含了点击事件和给TextView设置数据。至于这个点击事件传递到UI层,接口回调应该是最好的办法。

到这呢,基本流程基本就完了。

效果图如下:

android mpass框架 androidmvvm框架例子_android

上传的Demo中有两个包,截图如下:

android mpass框架 androidmvvm框架例子_java_02