本篇只是对RecycleView中使用ViewModel进行一个记录。确定使用哪种方案,在面临这两种情况可以考虑两种方案:
1、在RecyclView中使用List
2、在RecycleView中使用ViewModel进行填充
(上面对两种例子的代码实际上不能编译通过,只是起到一种辅助说明的意思。表示RecycleView中是使用一个包含很多ViewMode的列表,还是使用一个ViewMode,但是里面包含一个普通的列表)
经过查看多方Android官方提供的示例代码发现官方采用的均是第二种写法,第一种写法从没出现。哪怕是位于androidx.recyclerview.widget
包下面的ListAdapter
中的示例也是没有使用第一种写法。虽然有些人使用第一种方式,不过遵循官方编写风格还是使用第二种比较好
为了能帮助阅读,这里直接将ListAdapter
的源码贴出来,如下:
package androidx.recyclerview.widget;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.List;
/**
* {@link RecyclerView.Adapter RecyclerView.Adapter} base class for presenting List data in a
* {@link RecyclerView}, including computing diffs between Lists on a background thread.
*
* This class is a convenience wrapper around {@link AsyncListDiffer} that implements Adapter common
* default behavior for item access and counting.
*
* While using a LiveData is an easy way to provide data to the adapter, it isn't required
* - you can use {@link #submitList(List)} when new lists are available.
*
* A complete usage pattern with Room would look like this:
*
* {@literal @}Dao
* interface UserDao {
* {@literal @}Query("SELECT * FROM user ORDER BY lastName ASC")
* public abstract LiveData> usersByLastName();
* }
*
* class MyViewModel extends ViewModel {
* public final LiveData> usersList;
* public MyViewModel(UserDao userDao) {
* usersList = userDao.usersByLastName();
* }
* }
*
* class MyActivity extends AppCompatActivity {
* {@literal @}Override
* public void onCreate(Bundle savedState) {
* super.onCreate(savedState);
* MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
* RecyclerView recyclerView = findViewById(R.id.user_list);
* UserAdapter adapter = new UserAdapter();
* viewModel.usersList.observe(this, list -> adapter.submitList(list));
* recyclerView.setAdapter(adapter);
* }
* }
*
* class UserAdapter extends ListAdapter {
* public UserAdapter() {
* super(User.DIFF_CALLBACK);
* }
* {@literal @}Override
* public void onBindViewHolder(UserViewHolder holder, int position) {
* holder.bindTo(getItem(position));
* }
* public static final DiffUtil.ItemCallback DIFF_CALLBACK =
* new DiffUtil.ItemCallback() {
* {@literal @}Override
* public boolean areItemsTheSame(
* {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
* // User properties may have changed if reloaded from the DB, but ID is fixed
* return oldUser.getId() == newUser.getId();
* }
* {@literal @}Override
* public boolean areContentsTheSame(
* {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
* // NOTE: if you use equals, your object must properly override Object#equals()
* // Incorrectly returning false here will result in too many animations.
* return oldUser.equals(newUser);
* }
* }
* }
*
* Advanced users that wish for more control over adapter behavior, or to provide a specific base
* class should refer to {@link AsyncListDiffer}, which provides custom mapping from diff events
* to adapter positions.
*
* @param Type of the Lists this Adapter will receive.
* @param A class that extends ViewHolder that will be used by the adapter.
*/
public abstract class ListAdapter<T, VH extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<VH> {
final AsyncListDiffer<T> mDiffer;
private final AsyncListDiffer.ListListener<T> mListener =
new AsyncListDiffer.ListListener<T>() {
@Override
public void onCurrentListChanged(
@NonNull List<T> previousList, @NonNull List<T> currentList) {
ListAdapter.this.onCurrentListChanged(previousList, currentList);
}
};
@SuppressWarnings("unused")
protected ListAdapter(@NonNull DiffUtil.ItemCallback<T> diffCallback) {
mDiffer = new AsyncListDiffer<>(new AdapterListUpdateCallback(this),
new AsyncDifferConfig.Builder<>(diffCallback).build());
mDiffer.addListListener(mListener);
}
@SuppressWarnings("unused")
protected ListAdapter(@NonNull AsyncDifferConfig<T> config) {
mDiffer = new AsyncListDiffer<>(new AdapterListUpdateCallback(this), config);
mDiffer.addListListener(mListener);
}
/**
* Submits a new list to be diffed, and displayed.
*
* If a list is already being displayed, a diff will be computed on a background thread, which
* will dispatch Adapter.notifyItem events on the main thread.
*
* @param list The new list to be displayed.
*/
public void submitList(@Nullable List<T> list) {
mDiffer.submitList(list);
}
/**
* Set the new list to be displayed.
*
* If a List is already being displayed, a diff will be computed on a background thread, which
* will dispatch Adapter.notifyItem events on the main thread.
*
* The commit callback can be used to know when the List is committed, but note that it
* may not be executed. If List B is submitted immediately after List A, and is
* committed directly, the callback associated with List A will not be run.
*
* @param list The new list to be displayed.
* @param commitCallback Optional runnable that is executed when the List is committed, if
* it is committed.
*/
public void submitList(@Nullable List<T> list, @Nullable final Runnable commitCallback) {
mDiffer.submitList(list, commitCallback);
}
protected T getItem(int position) {
return mDiffer.getCurrentList().get(position);
}
@Override
public int getItemCount() {
return mDiffer.getCurrentList().size();
}
/**
* Get the current List - any diffing to present this list has already been computed and
* dispatched via the ListUpdateCallback.
*
* If a null
List, or no List has been submitted, an empty list will be returned.
*
* The returned list may not be mutated - mutations to content must be done through
* {@link #submitList(List)}.
*
* @return The list currently being displayed.
*
* @see #onCurrentListChanged(List, List)
*/
@NonNull
public List<T> getCurrentList() {
return mDiffer.getCurrentList();
}
/**
* Called when the current List is updated.
*
* If a null
List is passed to {@link #submitList(List)}, or no List has been
* submitted, the current List is represented as an empty List.
*
* @param previousList List that was displayed previously.
* @param currentList new List being displayed, will be empty if {@code null} was passed to
* {@link #submitList(List)}.
*
* @see #getCurrentList()
*/
public void onCurrentListChanged(@NonNull List<T> previousList, @NonNull List<T> currentList) {
}
}
architecture-components-samples
architecture-samples
Android MVVM:RecyclerView
Flexible RecyclerView Adapter with MVVM and Data Binding