First up the source code link: github.com/whenSunSet/…
RecycleView is Google’s alternative to ListView. It has a high degree of decoupling, which makes many developers abandon the previous ListView. Then how to achieve RecycleView under MVVM architecture? How to implement the refresh of single item and the automatic refresh of adding or subtracting items? Today I’m going to show you a convenient and highly decoupled solution.
1. Learn a few tool classes
Let’s take a look at a couple of the utility classes THAT I’ve made, that you can reuse all the time. Why introduce them, of course, is to let everyone better understand the internal mechanism, in the event of a pit will not be unable to find a solution, ha ha (of course, I have used so long has not found a pit)!
- 1.LayoutManager, this class is used to provide the type of layout, you see it!
public class LayoutManager {
protected LayoutManager() { } public interface LayoutManagerFactory { RecyclerView.LayoutManager create(RecyclerView recyclerView); } public static LayoutManagerFactory linear() { return new LayoutManagerFactory() { @Override public RecyclerView.LayoutManager create(RecyclerView recyclerView) { return new LinearLayoutManager(recyclerView.getContext()); }}; } public static LayoutManagerFactory linear(@Orientation final int orientation, final boolean reverseLayout) { return new LayoutManagerFactory() { @Override public RecyclerView.LayoutManager create(RecyclerView recyclerView) { return new LinearLayoutManager(recyclerView.getContext(), orientation, reverseLayout); }}; } public static LayoutManagerFactory grid(final int spanCount) { return new LayoutManagerFactory() { @Override public RecyclerView.LayoutManager create(RecyclerView recyclerView) { return new GridLayoutManager(recyclerView.getContext(), spanCount); }}; } public static LayoutManagerFactory grid(final int spanCount, @Orientation final int orientation, final boolean reverseLayout) { return new LayoutManagerFactory() { @Override public RecyclerView.LayoutManager create(RecyclerView recyclerView) { return new GridLayoutManager(recyclerView.getContext(), spanCount, orientation, reverseLayout); }}; } public static LayoutManagerFactory staggeredGrid(final int spanCount, @Orientation final int orientation) { return new LayoutManagerFactory() { @Override public RecyclerView.LayoutManager create(RecyclerView recyclerView) { return new StaggeredGridLayoutManager(spanCount, orientation); }}; } @IntDef({LinearLayoutManager.HORIZONTAL, LinearLayoutManager.VERTICAL}) @Retention(RetentionPolicy.SOURCE) public @interface Orientation { }Copy the code
}
-
ItemViewArg: This class is used to provide the ID of the XML for each Item interface and the ID of the bound instance in the BR
public class ItemViewArg<T> { public static <T> ItemViewArg<T> of(ItemView itemView) { return new ItemViewArg<>(itemView); } public static <T> ItemViewArg<T> of(ItemViewSelector<T> selector) { return new ItemViewArg<>(selector); } private final ItemView itemView; private final ItemViewSelector<T> selector; private ItemViewArg(ItemView itemView) { this.itemView = itemView; this.selector = new ItemViewSelector<T>() { @Override public void select(ItemView itemView, int position, T item) { } @Override public int viewTypeCount() { return 1; }}; } private ItemViewArg(ItemViewSelector<T> selector) { this.itemView = new ItemView(); this.selector = selector; } public void select(int position, T item) { selector.select(itemView, position, item); } public int bindingVariable() { return itemView.bindingVariable(); } public int layoutRes() { return itemView.layoutRes(); } public int viewTypeCount() { return selector.viewTypeCount(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() ! = o.getClass()) return false; ItemViewArg<? > that = (ItemViewArg<? >) o; if (! itemView.equals(that.itemView)) return false; return selector == that.selector; } @Override public int hashCode() { int result = itemView.hashCode(); result = 31 * result + selector.hashCode(); return result; } public interface ItemViewSelector<T> { void select(ItemView itemView, int position, T item); int viewTypeCount(); } public static class ItemView { public static final int BINDING_VARIABLE_NONE = 0; private int bindingVariable; @LayoutRes private int layoutRes; public static ItemView of(int bindingVariable, @LayoutRes int layoutRes) { return new ItemView().setBindingVariable(bindingVariable).setLayoutRes(layoutRes); } public ItemView set(int bindingVariable, @LayoutRes int layoutRes) { this.bindingVariable = bindingVariable; this.layoutRes = layoutRes; return this; } public ItemView setBindingVariable(int bindingVariable) { this.bindingVariable = bindingVariable; return this; } public ItemView setLayoutRes(@LayoutRes int layoutRes) { this.layoutRes = layoutRes; return this; } public int bindingVariable() { return bindingVariable; } @LayoutRes public int layoutRes() { return layoutRes; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() ! = o.getClass()) return false; ItemView itemView = (ItemView) o; if (bindingVariable ! = itemView.bindingVariable) return false; return layoutRes == itemView.layoutRes; } @Override public int hashCode() { int result = bindingVariable; result = 31 * result + layoutRes; return result; }}}Copy the code
This class has an internal interface and an internal interface, which I will introduce in the future.
- 1. The inner class ItemView has two int fields. BindingVariable stores the ID of the instance generated in BR, and layoutRes stores the ID of the required XML file. The other methods are for these two fields, relatively simple, you should see the understanding. ItemViewSelector<T> = ItemViewSelector<T> = ItemViewSelector<T> = ItemViewSelector<T> = ItemViewSelector<T> = ItemViewSelector<T> = ItemViewSelector<T> - 1. Select (ItemView ItemView, int Position, T item) : Select (ItemView ItemView, int Position, T item) : Select (ItemView ItemView, int Position, T item) : Select (ItemView ItemView, int Position, T item). Therefore, we can select the layout of the item by judging the type of layout in the item and then calling the of or set method of itemView. ViewTypeCount () : ItemView** - 2. ViewTypeCount () : ItemView** - 2. ViewTypeCount () : ItemView** 2. ItemViewArg<T> : This class contains an ItemView and an ItemViewSelector<T>. When we pass in one instance, the other will be generated automatically. - One of the most important methods is select(int position, T item). Keep this method in mind, and we'll talk about it later when we put the whole process together.Copy the code
-
BindingAdapters: If you have used databinding, you know that if you want to define a field that can be used in XML, you need to construct a method. This class is the method that RecycleView uses to construct fields in XML.
public class BindingAdapters { @SuppressWarnings("unchecked") @BindingAdapter(value = {"itemView", "items", "itemIds","itemAnimator","itemDecor"}, requireAll = false) public static <T> void setAdapter(final RecyclerView recyclerView, ItemViewArg<T> arg, final List<T> items, BindingRecyclerViewAdapter.ItemIds<T> itemIds,RecyclerView.ItemAnimator animator,RecyclerView.ItemDecoration decor) { if (arg == null) { throw new IllegalArgumentException("itemView must not be null"); } BindingRecyclerViewAdapter<T> adapter = new BindingRecyclerViewAdapter<>(arg); if (items! =null)adapter.setItems(items); if (itemIds! =null)adapter.setItemIds(itemIds); if (animator! =null)recyclerView.setItemAnimator(animator); if (decor! =null)recyclerView.addItemDecoration(decor); recyclerView.setAdapter(adapter); } @BindingAdapter("layoutManager") public static void setLayoutManager(RecyclerView recyclerView, LayoutManager.LayoutManagerFactory layoutManagerFactory) { recyclerView.setLayoutManager(layoutManagerFactory.create(recyclerView)); } @BindingConversion public static ItemViewArg toItemViewArg(ItemViewArg.ItemView itemView) { return ItemViewArg.of(itemView); } @BindingConversion public static ItemViewArg toItemViewArg(ItemViewArg.ItemViewSelector<? > selector) { return ItemViewArg.of(selector); }}Copy the code
SetAdapter () : Arg is a field that must be set in XML. Other fields are optional. Arg is an instance that provides the interface ID for item. – 2. Items is an instance that provides data for the list. – 3. Animator is an instance that animates additions and deletions of items. – 4. Decor is an instance that provides dividing lines for lists. – 5. Of course, you can also set up more initializations for RecycleView. – 2.setLayoutManager() : We provided the LayoutManager class to generate various list arrangements. This method constructs the field function of the field in the XML. – 3. Two toItemViewArg() are converters that convert ItemView and ItemViewSelector<> toItemViewArg(), So you can also fill in instances of the itemView and ItemViewSelector<> types in the itemView field in your XML.
- 4. BindingRecyclerViewAdapter: this class is to write of adapter, we usually write RecycleView with after this class, you need not to touch the adapter, basically all logic can be found in the ViewModel of the item. Let’s take a closer look at this class.
public class BindingRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final Object DATA_INVALIDATION = new Object();
@NonNull
private final ItemViewArg<T> itemViewArg;//item界面的layout,通过databing设置
private final WeakReferenceOnListChangedCallback<T> callback = new WeakReferenceOnListChangedCallback<>(this);//数据list变化的时候的回调,设置在数据list中,如果list是ObservableList
private List<T> items;//数据的list
private LayoutInflater inflater;//初始化item 界面;
// Currently attached recyclerview, we don't have to listen to notifications if null.
@Nullable
private RecyclerView recyclerView;
public BindingRecyclerViewAdapter(@NonNull ItemViewArg<T> arg) {
this.itemViewArg = arg;
}
@Override
public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int layoutId) {
if (inflater == null) {
inflater = LayoutInflater.from(viewGroup.getContext());
}
ViewDataBinding binding = DataBindingUtil.inflate(inflater, layoutId, viewGroup, false);
final RecyclerView.ViewHolder holder=new BindingViewHolder(binding);
binding.addOnRebindCallback(new OnRebindCallback() {
@Override
public boolean onPreBind(ViewDataBinding binding) {
return recyclerView != null && recyclerView.isComputingLayout();
}
@Override
public void onCanceled(ViewDataBinding binding) {
if (recyclerView == null || recyclerView.isComputingLayout()) {
return;
}
int position = holder.getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
notifyItemChanged(position, DATA_INVALIDATION);
}
}
});
return holder;
}
@Override
public final void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
T item = items.get(position);
ViewDataBinding binding = DataBindingUtil.getBinding(viewHolder.itemView);
if (itemViewArg.bindingVariable() != ItemViewArg.ItemView.BINDING_VARIABLE_NONE) {
boolean result = binding.setVariable(itemViewArg.bindingVariable(), item);
if (!result) {
Utils.throwMissingVariable(binding, itemViewArg.bindingVariable(), itemViewArg.layoutRes());
}
binding.executePendingBindings();
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {
if (isForDataBinding(payloads)) {
ViewDataBinding binding = DataBindingUtil.getBinding(holder.itemView);
binding.executePendingBindings();
} else {
super.onBindViewHolder(holder, position, payloads);
}
}
public void setItems(@Nullable List<T> items) {
if (recyclerView != null) {
if (this.items instanceof ObservableList) {
((ObservableList<T>) this.items).removeOnListChangedCallback(callback);
}
if (items instanceof ObservableList) {
((ObservableList<T>) items).addOnListChangedCallback(callback);
}
}
this.items = items;
notifyDataSetChanged();
}
@Override
public int getItemCount() {
return items == null ? 0 : items.size();
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
if (this.recyclerView == null && items != null && items instanceof ObservableList) {
((ObservableList<T>) items).addOnListChangedCallback(callback);
}
this.recyclerView = recyclerView;
}
@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
if (this.recyclerView != null && items != null && items instanceof ObservableList) {
((ObservableList<T>) items).removeOnListChangedCallback(callback);
}
this.recyclerView = null;
}
@Override
public int getItemViewType(int position) {
itemViewArg.select(position, items.get(position));
return itemViewArg.layoutRes();
}
@Override
public long getItemId(int position) {
return itemViewArg.layoutRes();
}
private boolean isForDataBinding(List<Object> payloads) {
if (payloads == null || payloads.size() == 0) {
return false;
}
for (int i = 0; i < payloads.size(); i++) {
Object obj = payloads.get(i);
if (obj != DATA_INVALIDATION) {
return false;
}
}
return true;
}
private static class BindingViewHolder extends RecyclerView.ViewHolder {
public BindingViewHolder(ViewDataBinding binding) {
super(binding.getRoot());
}
}
private static class WeakReferenceOnListChangedCallback<T> extends ObservableList.OnListChangedCallback<ObservableList<T>> {
final WeakReference<BindingRecyclerViewAdapter<T>> adapterRef;
WeakReferenceOnListChangedCallback(BindingRecyclerViewAdapter<T> adapter) {
this.adapterRef = new WeakReference<>(adapter);
}
@Override
public void onChanged(ObservableList sender) {
BindingRecyclerViewAdapter<T> adapter = adapterRef.get();
if (adapter == null) {
return;
}
Utils.ensureChangeOnMainThread();
adapter.notifyDataSetChanged();
}
@Override
public void onItemRangeChanged(ObservableList sender, final int positionStart, final int itemCount) {
BindingRecyclerViewAdapter<T> adapter = adapterRef.get();
if (adapter == null) {
return;
}
Utils.ensureChangeOnMainThread();
adapter.notifyItemRangeChanged(positionStart, itemCount);
}
@Override
public void onItemRangeInserted(ObservableList sender, final int positionStart, final int itemCount) {
BindingRecyclerViewAdapter<T> adapter = adapterRef.get();
if (adapter == null) {
return;
}
Utils.ensureChangeOnMainThread();
adapter.notifyItemRangeInserted(positionStart, itemCount);
}
@Override
public void onItemRangeMoved(ObservableList sender, final int fromPosition, final int toPosition, final int itemCount) {
BindingRecyclerViewAdapter<T> adapter = adapterRef.get();
if (adapter == null) {
return;
}
Utils.ensureChangeOnMainThread();
for (int i = 0; i < itemCount; i++) {
adapter.notifyItemMoved(fromPosition + i, toPosition + i);
}
}
@Override
public void onItemRangeRemoved(ObservableList sender, final int positionStart, final int itemCount) {
BindingRecyclerViewAdapter<T> adapter = adapterRef.get();
if (adapter == null) {
return;
}
Utils.ensureChangeOnMainThread();
adapter.notifyItemRangeRemoved(positionStart, itemCount);
}
}
Copy the code
}
This class consists of two inner classes and itself
- 1. Internal class BindingViewHolder, which integrates RecyclerView.ViewHolder simply provides a ViewHolder.
- 2. WeakReferenceOnListChangedCallback, this class is a weak reference List of listeners, we will pass in a List entity in the adapter as a data source, used databinding classmates know, there’s a ObsrvableList. So when we pass in a List of this type, the Adapter will pass this listener into an ObsrvableList, and once we add or delete the data source, the listener’s method will be called to refresh our List. We can see several of these methods, onItemRangeChanged, onItemRangeInserted, onItemRangeMoved, and onItemRangeRemoved. Several of the corresponding flush methods of the Adapter are called in.
- 3. Finally look at the Adapter:
- 1. Take a look at an internal example:
-
- ItemViewArg: This class is used to provide interface parameters for the item that are passed when the adapter is created.
- 2. WeakReferenceOnListChangedCallback callback: this is what we said before the data source of the listener, more change, occurs when the data source is called the adapter’s corresponding methods to refresh operation interface.
- 3.List items: This is the data source. In general, we pass an ObservableList or its subclass. This ensures that the interface is refreshed automatically when data sources are added or deleted.
-
- LayoutInflater inflater: It’s used to initialize XML files.
- RecyclerView RecyclerView: I don’t need to say that.
-
- 2. Let’s look at the method again. Let’s do it in order:
- 1.onCreateViewHolder(ViewGroup viewGroup, int layoutId)
This function is the first function called when the RecycleView initializes each item, and it does several things.- Lines 1.1 through 3 initialize the inflater.
- Line 2.4 creates the ViewDataBinding for this item.
- Line 3.5 creates a viewHolder for this item. From the previous explanation, we can see that the viewHoder is just the rootView that stores the ViewDataBinding.
- 4. The following code sets up listeners for databinding’s binding process, The listener onPreBind(ViewDataBinding Binding) and onCanceled(ViewDataBinding Binding) are called before and after each binding, respectively. Check out my previous blog that parsed the databinding source code for details of the process
- OnBindViewHolder (recyclerView. ViewHolder, int position); Let’s explain them all.
- 1.onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads)
- 1. You can see that this method has an if selection statement. If true, this means that the item has already been initialized, so the code just needs to find the corresponding ViewDataBinding and perform its binding.
- 2. If false, the item has not been initialized, and the superclass method is called, which in turn calls another overloaded method, which we’ll look at next.
- 2.onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position)
- Line 1.1 gets the data.
- Line 2.2 finds the ViewDataBinding we created in onCreateViewHolder().
- 3. The logic after that is to set the data for ViewDataBinding.
- 3.In fact, the above two methods are the whole process of RecycleView to initialize an item. Next, we will talk about other auxiliary methods.
-
- SetItems (@nullable List items) is used to initialize Adapter adapters in BindingAdapters. In this case, check whether the List data source type is ObservableList. If so, set the listener we mentioned before, after setting our RecycleView can automatically add and delete.
- 2.getItemViewType(int position)
- 1. This function provides a layoutId for the onCreateViewHolder() method
- 2. As you can see from ItemViewArg, the last thing we call is the Select () method of ItemViewSelector
- 3. If we had built ItemViewArg and passed in an ItemView, we would have returned the layoutId stored in the ItemView
- 4. If the constructor passed in an ItemViewSelector that the developer implemented, the constructor will return an ItemView instance that the developer returned.
-
- 1.Activity is the View layer, which is responsible for implementing operations on the interface. The operation of the interface mainly has two aspects, one is the animation of View, one is the monitoring of View events on the interface, and the change of data.
- 1. Animate the View: Most animations require a reference to the View, so to retrieve a reference to the view, you can set the VIEW ID in XML and retrieve the reference to the view in viewDatabinding.
- 2. View event monitoring and data changes on the interface, such as TextView, Button, EditText, etc. The usual approach is to set up listeners for these views in the Activity and then introduce them in the XML using the variable tag. The problem is that once you have a lot of listeners, you end up with a lot of template code in the XML and Activity files. So to solve this problem I introduced the concept of UiViewModel: implement an inner class in the Activity, implement all the listeners inside that listener, and finally introduce UiViewModel into an XML file in the Activity.
- 2. Do we need to pass the Context into the ViewModel? I think so, for two reasons:
- 1. In many cases, using the Activity directly in the ViewModel is much more intuitive and logical than going around for a long time to call a method in the Activity. I have tried not using the Context in the ViewModel. It turns out that there’s a lot of weird code in the Activity and ViewModel, which I’ll cover in more detail later in the MVVM architecture.
- 2. The main purpose of not passing an Activity in the ViewModel is to decouple it, and once it becomes a problem, it can be easily solved. We can create a BaseActivity and pass in just BaseAcvitity in all viewModels, and 95% of the operations that need to use an Activity in the ViewModel don’t need to know the actual type of Activity. So that’s a good solution to the problem.
-
1. The XML code
<? The XML version = "1.0" encoding = "utf-8"? > <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bind="http://schemas.android.com/apk/res-auto"> <data class="com.example.administrator.mvvmrecycleview.ActivityMainBinding"> <variable name="viewModel" type="com.example.administrator.mvvmrecycleview.MainActivityViewModel"/> <variable name="uiViewModel" type="com.example.administrator.mvvmrecycleview.MainActivity.MainActivityUIViewModel"/> <import type="com.example.administrator.mvvmrecycleview.recycleViewUtil.LayoutManager"/> <import type="android.view.View"/> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" Android :layout_height="wrap_content"> <Button android:onClick="@{uiviewModel.add}" Android :text=" increment" android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content"/> <Button Android :onClick="@{uiviewModel.delete}" Android :text=" delete "Android :layout_weight="1" Android :layout_width="0dp" android:layout_width="0dp" <Button android:onClick="@{uiviewModel.change}" Android :text=" edit" android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content"/> </LinearLayout> <EditText Android :onTextChanged="@{uiViewModel.position}" Android :inputType="number" Android :hint=" Enter the position of the item to be changed" android:layout_width="match_parent" android:layout_height="wrap_content"/> <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/my_recycle_view" bind:layoutManager="@{LayoutManager.linear()}" bind:itemView="@{viewModel.mViewSelector}" bind:items="@{viewModel.dataItems}"> </android.support.v7.widget.RecyclerView> </LinearLayout> </layout>Copy the code
2. Construct a simple RecycleView under MVVM
We have finished the introduction of the tool classes we will use next. After the above tool classes, there is basically no need to change the code, so after removing the tool classes, our automatic increase, deletion and change of RecycleView will only be business code.
Before I go into the actual code, I’ll give you a few thoughts about the architecture of MVVM directly, so you can see what I can do next.
Ok, so without further ado let’s look at the logic of the code.
The above code is very simple and has the following main points.
- 1. Use the variable tag to introduce two instances of ViewModel and UiViewModel. - 2. Set 3 buttons and 1 EditText. - 3. I used the items and itemView fields defined in BindingAdapters to set initialization variables for RecycleView.Copy the code
-
2. Look again at the code in MainActivity
public class MainActivity extends AppCompatActivity { private com.example.administrator.mvvmrecycleview.ActivityMainBinding mActivityMainBinding; private MainActivityViewModel mMainActivityViewModel; private MainActivityUIViewModel mMainActivityUIViewModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); mMainActivityViewModel = new MainActivityViewModel(this); mMainActivityUIViewModel = new MainActivityUIViewModel(this); mActivityMainBinding.setViewModel(mMainActivityViewModel); mActivityMainBinding.setUiViewModel(mMainActivityUIViewModel); } public static class MainActivityUIViewModel { private MainActivity mMainActivity; private MainActivityViewModel mMainActivityViewModel; public MainActivityUIViewModel(MainActivity mMainActivity) { this.mMainActivity = mMainActivity; this.mMainActivityViewModel=mMainActivity.mMainActivityViewModel; } public TextViewBindingAdapter.OnTextChanged position=new TextViewBindingAdapter.OnTextChanged() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (! TextUtils.isEmpty(s.toString()))mMainActivityViewModel.positionStr=s.toString(); }}; public View.OnClickListener add=new View.OnClickListener() { @Override public void onClick(View v) { mMainActivityViewModel.add(); }}; public View.OnClickListener delete=new View.OnClickListener() { @Override public void onClick(View v) { mMainActivityViewModel.delete(); }}; public View.OnClickListener change=new View.OnClickListener() { @Override public void onClick(View v) { mMainActivityViewModel.change(); }}; }}Copy the code
The code in MainActivity is also simple – 1. Get the ViewDatabinding in the Activity and set the ViewModel and UiViewModel. – 2. Set various listeners in the inner class UiViewModel. – 3. It is noteworthy that: I’ve set up listeners in UiViewModel, but if it involves data storage and business logic, it needs to be done in ViewModel, so I’ve passed in a reference to the specific MainActivity in UiViewModel, Since UiViewModel is just a collection of listeners in an Activity, it’s not really a ViewModel, so this doesn’t violate my previous point. Also, although this may seem a bit convoluted, the resulting code is much better organized.
-
3. Let’s look at our most important class ViewModel, this class is a set of our business logic for a RecycleView class, more important, but after our front tool class packaging becomes very clear.
public class MainActivityViewModel { public MainActivity baseActivity; public ItemViewArg.ItemViewSelector<MainActivityItemViewModel> mViewSelector; public final ObservableList<MainActivityItemViewModel> dataItems= new ObservableArrayList<>(); public String positionStr; public MainActivityViewModel(MainActivity b) { baseActivity=b; mViewSelector=new ItemViewArg.ItemViewSelector<MainActivityItemViewModel>() { @Override public void select(ItemViewArg.ItemView itemView, int position, MainActivityItemViewModel item) { itemView.set(com.example.administrator.mvvmrecycleview.BR.viewModel,item.type==1? R.layout.item_one:R.layout.item_two); } @Override public int viewTypeCount() { return 2; }}; for (int i = 0; i < 20; i++) { if (i%2==1){ dataItems.add(new MainActivityItemViewModel(b,1,String .valueOf(i))); }else { dataItems.add(new MainActivityItemViewModel(b,2,String .valueOf(i))); } } } public void add(){ if (TextUtils.isEmpty(positionStr)||dataItems.size()<Integer.parseInt(positionStr))return; DataItems. Add (Integer) parseInt (positionStr), new MainActivityItemViewModel (baseActivity, 1, "increase")); } public void delete(){ if (TextUtils.isEmpty(positionStr)||dataItems.size()<=Integer.parseInt(positionStr))return; dataItems.remove(Integer.parseInt(positionStr)); } public void change(){ if (TextUtils.isEmpty(positionStr)||dataItems.size()<=Integer.parseInt(positionStr))return; MainActivityItemViewModel mainActivityItemViewModel = dataItems.get(Integer.parseInt(positionStr)); MainActivityItemViewModel. Text. The set (" modify "); } public static class MainActivityItemViewModel{ public MainActivity mBaseActivity; public int type; public final ObservableField<String> text=new ObservableField<>(); public MainActivityItemViewModel(MainActivity baseActivity, int type, String text) { mBaseActivity = baseActivity; this.type = type; this.text.set(text); } public View.OnClickListener click=new View.OnClickListener() { @Override public void onClick(View v) { Toast.maketext (mBaseActivity, (" you click "+text.get()), toast.length_short).show(); }}; }}Copy the code
The RecycleView ViewModel consists of itself and an internal class, ItemViewModel, which is the ViewModel of each item in RecycleView. It is worth noting that, When a RecycleView has a lot of content, we can split the ViewModel, that is, ItemViewModel into a new class. This is also easy to do late in a project, but most of the logic for a project is in the ItemViewModel, so I used the inner class approach.
- 1. The ViewModel has several member variables. - 1.MainActivity: This is recommended for your project to pass in BaseActivity as I mentioned earlier for decoupling. - 2. ItemViewArg. ItemViewSelector < MainActivityItemViewModel > : This interface is the utility class I described earlier, and its internal method of selecting layout needs to be implemented by the developer, * * we can see this interface is a generic ItemViewModel * * - 3. ObservableList < MainActivityItemViewModel > : this is our data source, the generic ItemViewModel. - 2. I can be seen in the ViewModel initialized in the constructor of the ItemViewArg. ItemViewSelector and ObservableList. - 1. ItemViewArg. There are two methods need to be rewritten - ItemViewSelector 1. Select (ItemViewArg. ItemView ItemView, int the position, MainActivityItemViewModel item) : * * general situation, we will place the item in the ItemViewModel exactly which interface to use the tag * *, so here we can use the item to choose layoutId this parameter. - 2. ViewTypeCount () : This method returns how many layouts are currently available. - 2. I use static data to initialize the ObservableList, where you use network data in your project. The ItemViewModel is then constructed from the network data. - 3. The following methods are add, delete, and update. These methods are called in the listener in the UiViewModel, and we can see that I'm just adding and deleting data in the ObservableList based on position. - 1. Type: determines what type of layout an item is. - 2.ObservableField< String > : Data of the item.Copy the code
The above is all the code of simple RecycleView automatic increase, deletion and change. It can be said that in addition to the tool class, almost all the code is business logic, and it is very simple to realize. If you don’t want to know the details, you can directly copy several tool classes into your project. This is the second wave of the prelude to the MVVM architecture, you can take a look at the databinding source code analysis I sent before, full believe that dry products we will definitely like.
No angst peddling, no clickbait. Share some interesting things about the world. Topics include but are not limited to: science fiction, science, technology, the Internet, programmers, computer programming. The following is my wechat public number: Interesting things in the world, dry goods waiting for you to see.
- 1.onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads)
- 1.onCreateViewHolder(ViewGroup viewGroup, int layoutId)
- 1. Take a look at an internal example: