上一篇文章主要叙述了基本数据类型和引用数据类型在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
- 由于创建的是MVVM的思想,工作分明,也就是各干各的事,谁也不参与谁,而ViewModel又是控制器,所以最好在创建一个ItemViewModel, 专门用于RecyclerView的内部操作,比如onClick事件。
- 有一点需要明确,重写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层,接口回调应该是最好的办法。
到这呢,基本流程基本就完了。
效果图如下:
上传的Demo中有两个包,截图如下: