一、作用
保持内部数据有序,可自动去重,在对 SortedList 数据进行增删改查时,能够自动通知 RecyclerView 数据集中的更改
二、概念
1. 构造函数
2. 方法
(1)List 接口中常用方法
虽然 SortedList 和 List 没有直接的继承实现等关系,但这两个里面提供的一些方法是类似的。
(2)特别的方法
void beginBatchUpdates()
void endBatchUpdates()
3. 相关类
(1)abstract class SortedList.Callback
public static abstract class Callback < T2 > implements Comparator < T2 > , ListUpdateCallback {
/**
* Similar to {@link java.util.Comparator#compare(Object, Object)}, 比较两个对象并返回它们的顺序.
*
* @param o1 The first object to compare.
* @param o2 The second object to compare.
*
* @return 负值(o1<o2),0(o1==02),正值(o1>o2)
*/
@Override
abstract public int compare(T2 o1, T2 o2);
/**
* Called by the SortedList when the item at the given position is updated.
* 当指定 position 位置的 Item 更新时,由 SortedList 调用
* @param position The position of the item which has been updated.
* @param count The number of items which has changed.
*/
abstract public void onChanged(int position, int count);
@Override
public void onChanged(int position, int count, Object payload) {
onChanged(position, count);
}
/**
* 由 SortedList 调用,用于检查两个 items 是否有相同的数据。根据该方法的返回值,决定是否要调用{@link #onChanged(int, int)} 。
* <p>
* SortedList 使用该方法检查一致性,而不是 {@link Object#equals(Object)}
* 所以
* 你可根据你的 UI 自己决定如何书写该方法
* <p>
* @param oldItem The previous representation of the object.
* @param newItem The new object that replaces the previous one.
*
* @return True :两个 Item 有相同的内容;false:内容不相同
*/
abstract public boolean areContentsTheSame(T2 oldItem, T2 newItem);
/**
* 由 SortedList 调用,用于检查是否是两个相同的 item,如果你的 item 有唯一的 ID,那么可以使用 id 检查两个 item 是否相同。
* <p>
*
* @param item1 The first item to check.
* @param item2 The second item to check.
*
* @return True :两个 Item 相同;false:不相同
*/
abstract public boolean areItemsTheSame(T2 item1, T2 item2);
/**
* 当 {@link #areItemsTheSame(T2, T2)} returns {@code true} for two items and
* {@link #areContentsTheSame(T2, T2)} returns false for them, {@link Callback} 调用该方法得到 payload 封装 Item 的变化。
* <p>
* For example, if you are using {@link Callback} with
* {@link RecyclerView}, you can return the particular field that
* changed in the item and your
* {@link RecyclerView.ItemAnimator ItemAnimator} can use that
* information to run the correct animation.
* <p>
* 默认实现返回 {@code null}.
*
* @param item1 The first item to check.
* @param item2 The second item to check.
* @return A payload object 代表两个 item 之间的不同
*/
@Nullable
public Object getChangePayload(T2 item1, T2 item2) {
return null;
}
}
(2)static class SortedList.BatchedCallback
/**
* A callback implementation that can batch notify events dispatched by the SortedList.
extends Callback ,可以批量通知 SortedList
* <p>
* This class can be useful if you want to do multiple operations on a SortedList but don't
* want to dispatch each event one by one, which may result in a performance issue.
* <p>
如果你想向 SortedList 中添加多个 item,如果 items 是被连续添加进来的,BatchedCallback 可以将多个<code>onInserted(index, 1)</code>
调用转化成一个 <code>onInserted(index, N)</code> 调用。
* For example, if you are going to add multiple items to a SortedList, BatchedCallback call
* convert individual <code>onInserted(index, 1)</code> calls into one
* <code>onInserted(index, N)</code> if items are added into consecutive indices. This change
* can help RecyclerView resolve changes much more easily.
* <p>
如果连续的改变不适合 batching,BatchedCallback 会立即发送通知。在你编辑完 SortedList 后,要调用
{@link BatchedCallback#dispatchLastEvent()} 去刷新变化,告诉 Callback。
* If consecutive changes in the SortedList are not suitable for batching, BatchingCallback
* dispatches them as soon as such case is detected. After your edits on the SortedList is
* complete, you <b>must</b> always call {@link BatchedCallback#dispatchLastEvent()} to flush
* all changes to the Callback.
*/
public static class BatchedCallback < T2 > extends Callback < T2 > {
final Callback < T2 > mWrappedCallback;
private final BatchingListUpdateCallback mBatchingListUpdateCallback;
/**
* Creates a new BatchedCallback that wraps the provided Callback.
*
* @param wrappedCallback The Callback which should received the data change callbacks.
* Other method calls (e.g. {@link #compare(Object, Object)} from
* the SortedList are directly forwarded to this Callback.
*/
public BatchedCallback(Callback < T2 > wrappedCallback) {
mWrappedCallback = wrappedCallback;
mBatchingListUpdateCallback = new BatchingListUpdateCallback(mWrappedCallback);
}
@Override
public int compare(T2 o1, T2 o2) {
return mWrappedCallback.compare(o1, o2);
}
@Override
public void onInserted(int position, int count) {
mBatchingListUpdateCallback.onInserted(position, count);
}
@Override
public void onRemoved(int position, int count) {
mBatchingListUpdateCallback.onRemoved(position, count);
}
@Override
public void onMoved(int fromPosition, int toPosition) {
mBatchingListUpdateCallback.onMoved(fromPosition, toPosition);
}
@Override
public void onChanged(int position, int count) {
mBatchingListUpdateCallback.onChanged(position, count, null);
}
@Override
public void onChanged(int position, int count, Object payload) {
mBatchingListUpdateCallback.onChanged(position, count, payload);
}
@Override
public boolean areContentsTheSame(T2 oldItem, T2 newItem) {
return mWrappedCallback.areContentsTheSame(oldItem, newItem);
}
@Override
public boolean areItemsTheSame(T2 item1, T2 item2) {
return mWrappedCallback.areItemsTheSame(item1, item2);
}
@Nullable
@Override
public Object getChangePayload(T2 item1, T2 item2) {
return mWrappedCallback.getChangePayload(item1, item2);
}
/**
* This method dispatches any pending event notifications to the wrapped Callback.
* You <b>must</b> always call this method after you are done with editing the SortedList.
*/
public void dispatchLastEvent() {
mBatchingListUpdateCallback.dispatchLastEvent();
}
}
(3)SortedListAdapterCallback
它是 SortedList.Callback 抽象类的一个实现,可以将一个 SortedList 和 RecyclerView.Adapter 绑定。
它的唯一一个构造函数是
SortedListAdapterCallback(Adapter adapter)
有4个抽象方法需具体的使用者实现。
areItemsTheSame(T2 item1,T2 item2)
该方法由 SortedList 调用,以决定两个 Object 是否是相同的 item。
areContentTheSame(T2 oldItem,T2 newItem)
由 SortedList 调用,以决定两个 item 是否有相同的数据。
compare(T2 o1,T2 o2)
得到 o1、o2 的排列规则。
onChange(int position,int count)
由 SortedList 调用,当给定 position 的 item 更新时。
三、使用
1. 创建 Adapter
和常规 Adapter 的写法一样,只是把 Adapter 的数据源替换成
SortedList<>
类型
public class MyAdapter extends RecyclerView.Adapter < MyAdapter.UserViewHolder > {
private Context mContext;
private SortedList < User > mData;
private LayoutInflater mLayoutInflater;
public MyAdapter(Context context, SortedList < User > data) {
this.mContext = context;
this.mData = data;
this.mLayoutInflater = LayoutInflater.from(context);
}
public SortedList < User > getData() {
return mData;
}
public void setData(SortedList < User > data) {
mData = data;
}
@NonNull
@Override
public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = mLayoutInflater.inflate(R.layout.user_item, parent, false);
return new UserViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull UserViewHolder holder, int position, @NonNull List < Object > payloads) {
super.onBindViewHolder(holder, position, payloads);
}
@Override
public int getItemCount() {
return mData == null ? 0 : mData.size();
}
@Override
public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
User user = mData.get(position);
holder.idTv.setText(user.getId() + "");
holder.nameTv.setText(user.getName());
holder.ageTv.setText(user.getAge() + "");
holder.profileTv.setText(user.getProfile());
}
class UserViewHolder extends RecyclerView.ViewHolder {
private TextView nameTv;
private TextView ageTv;
private TextView profileTv;
private TextView idTv;
public UserViewHolder(View view) {
super(view);
idTv = view.findViewById(R.id.id_tv);
nameTv = view.findViewById(R.id.name_tv);
ageTv = view.findViewById(R.id.age_tv);
profileTv = view.findViewById(R.id.profile_tv);
}
}
}
2. 准备 SortedListAdapterCallback 的实现类
public class SortedListCallback extends SortedListAdapterCallback < User > {
public SortedListCallback(RecyclerView.Adapter adapter) {
super(adapter);
}
@Override
public int compare(User o1, User o2) {
return o1.getId() - o2.getId();
}
@Override
public boolean areContentsTheSame(User oldItem, User newItem) {
if (oldItem.getId() != newItem.getId()) {
return false;
}
if (!oldItem.getName().equals(newItem.getName())) {
return false;
}
if (oldItem.getAge() != newItem.getAge()) {
return false;
}
if (!oldItem.getProfile().equals(newItem.getProfile())) {
return false;
}
return true;
}
@Override
public boolean areItemsTheSame(User oldItem, User newItem) {
return oldItem.getId() == newItem.getId();
}
}
areItemTheSame()
该方法中,如果 Item 有可唯一标志的 Id 时,一般用此 id 检查 item 是否相同。
areContentsTheSame()
逐项比较 item 的数据,默认相同,只要有一项不同则返回 false。
3. 创建 SortedList 数据集,并设置给 Adapter
SortedList 的构造函数中需要传入 Callback 实例,
Callback 实例创建时需要传入 Adapter 实例,
所以倒过来就是,先创建 Adapter,然后创建 Callback 实例,最后创建 SortedList 实例再设置给 Adapter,所以 Adapter 创建时传不传入数据源都无所谓,因为后面注定要设置新的 SortedList 的。
private void initViews() {
mRecyclerView = findViewById(R.id.user_rv);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
// 1,创建Adapter
mMyAdapter = new MyAdapter(this, null);
mRecyclerView.setAdapter(mMyAdapter);
// 2, 创建Callback
SortedListCallback callback = new SortedListCallback(mMyAdapter);
// 3,创建SortedList
mData = new SortedList < > (User.class, callback);
mData.add(new User(1, "xiaohong1", 10, "adfada"));
mData.add(new User(2, "xiaohong2", 10, "adfada"));
mData.add(new User(1, "xiaohong3", 10, "adfada"));
mData.add(new User(4, "xiaohong4", 10, "adfada"));
mData.add(new User(5, "xiaohong5", 10, "adfada"));
// 4,为Adapter 设置新的数据源
mMyAdapter.setData(mData);
}
4. 实践定向刷新
有了 SortedList,我们刷新数据时不需要再调用
notifyXX()
相关方法通知 Adapter 更新视图,直接操作数据源 SortedList,对其进行增删改后,数据的修改即可直接反应到视图上。
界面上有一按钮,点击按钮向数据源添加数据项,添加后视图立即更新。
- 向数据源中增加数据
private void initListener() {
mRefreshBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mData.add(new User(10, "dafh", 10, "dafafa"));
mData.add(new User(11, "dafh", 10, "dafafa"));
mData.add(new User(12, "dafh", 10, "dafafa"));
}
});
}
删除数据
修改数据
四、注意
五、评价
SortedLit 的亮点是维护数据集的有序&去重,它不支持部分绑定
参考文献
【Android】你可能不知道的Support(一) 0步自动定向刷新SortedList
Android-developer:SortedListAdapterCallback