上次介绍了来自 recyclerview-v7 中的 AsyncListUtil 类。AsyncListUtil 是异步加载数据的,而这次出场的 SortedList 是用来排序数据的。
SortedList
SortedList 顾名思义 是一个排序的 List。SortedList 是一个保持内部数据顺序的 List,当新的数据添加的时候,会通过回调接口来更新数据变化信息,使用这些信息可以通知 RecyclerView 的 Adapter。
SortedList 具有 Java List 接口中的一些常见方法,比如:add(T item)、addAll(T… items)、clear()、get(int index)、remove(T item)、 size() 等,下面是 SortedList 中的几个特殊的重要方法:
void beginBatchedUpdates ()、endBatchedUpdates () :批量操作数据的开始和结束方法,可以在这两个函数之间批量操作数据,比如在循环中添加删除修改多个数据,在操作结束后统一通过回调函数该通知 Adapter 数据变化,这样可以提高 UI 效率:
Java
mSortedList.beginBatchedUpdates();
try {
mSortedList.add(item1)
mSortedList.add(item2)
mSortedList.remove(item3)
...
} finally {
mSortedList.endBatchedUpdates();
}
1
2
3
4
5
6
7
8
9
10
11
mSortedList.beginBatchedUpdates();
try{
mSortedList.add(item1)
mSortedList.add(item2)
mSortedList.remove(item3)
...
}finally{
mSortedList.endBatchedUpdates();
}
使用 SortedList.BatchedCallback 也可以实现批量操作,如果使用了 SortedList.BatchedCallback 则需要自己在批量操作后手动调用 dispatchLastEvent () 函数。
如果使用了 SortedList.BatchedCallback 则调用 beginBatchedUpdates() 函数将不起作用。
void recalculatePositionOfItemAt (int index) :可以重新计算 index 位置的数据的位置,但是不触发 [ onChanged(int, int)](https://developer.android.google.cn/reference/android/support/v7/util/SortedList.Callback.html#onChanged(int, int)) 函数,触发的是 [onMoved(int, int)](https://developer.android.google.cn/reference/android/support/v7/util/ListUpdateCallback.html#onMoved(int, int)) 函数。
void updateItemAt (int index, T item) 更新 index 位置的数据,根据需要调用 [onMoved(int, int)](https://developer.android.google.cn/reference/android/support/v7/util/ListUpdateCallback.html#onMoved(int, int)) 或者 [ onChanged(int, int)](https://developer.android.google.cn/reference/android/support/v7/util/SortedList.Callback.html#onChanged(int, int)) 函数。
SortedList 是通过其回调接口(Callback) 来判断数据是否相同和通知数据位置变化的。SortedList 有如下几个 Callback:
SortedList.Callback :基本的 Callback,定义了如何比较两个对象是否相同,以及数据变化的通知函数。
SortedListAdapterCallback 继承于 SortedList.Callback 的 类,里面的数据变化通知的接口都用 RecyclerView 的 Adapter 的 notifyXXX 类实现了,比如:
Java
@Override
public void onInserted(int position, int count) {
mAdapter.notifyItemRangeInserted(position, count);
}
1
2
3
4
5
@Override
publicvoidonInserted(intposition,intcount){
mAdapter.notifyItemRangeInserted(position,count);
}
SortedList.BatchedCallback: 批量更新数据的回调接口实现。如果你希望把多次更新调用合并到一次来触发 RecyclerView Adapter 的数据变化通知,则可以使用这个。比如如果使用 SortedList.Callback 触发 N 次 onInserted(index, 1) ,则会触发 N 次 RecyclerView Adapter 的 notifyItemRangeInserted(index, 1) 调用,这样会导致 RecyclerView 更新 N 次,如果使用 BatchedCallback 则可以把这 N 次 notifyItemRangeInserted(index, 1) 变为一次 notifyItemRangeInserted(index, N) 调用,从而提高 UI 渲染效率。(使用 beginBatchedUpdates() 和 Callback 也可以实现同样的效果)
示例代码
SortedList 使用是非常简单的,在 Adapter 中使用 SortedList 来保存数据,并实现 Callback,当数据变化的时候,使用 Adapter 的 notifyXXX 方法来通知 RecyclerView 即可:
Java
private static class SortedListAdapter extends RecyclerView.Adapter {
SortedList mData;
final LayoutInflater mLayoutInflater;
public SortedListAdapter(LayoutInflater layoutInflater, Item... items) {
mLayoutInflater = layoutInflater;
mData = new SortedList(Item.class, new SortedListAdapterCallback(this) {
@Override
public int compare(Item t0, Item t1) {
if (t0.mIsDone != t1.mIsDone) {
return t0.mIsDone ? 1 : -1;
}
int txtComp = t0.mText.compareTo(t1.mText);
if (txtComp != 0) {
return txtComp;
}
if (t0.id < t1.id) {
return -1;
} else if (t0.id > t1.id) {
return 1;
}
return 0;
}
@Override
public boolean areContentsTheSame(Item oldItem,
Item newItem) {
return oldItem.mText.equals(newItem.mText);
}
@Override
public boolean areItemsTheSame(Item item1, Item item2) {
return item1.id == item2.id;
}
});
for (Item item : items) {
mData.add(item);
}
}
public void addItem(Item item) {
mData.add(item);
}
@Override
public TodoViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
return new TodoViewHolder(
mLayoutInflater.inflate(R.layout.sorted_list_item_view, parent, false)) {
@Override
void onDoneChanged(boolean isDone) {
int adapterPosition = getAdapterPosition();
if (adapterPosition == RecyclerView.NO_POSITION) {
return;
}
mBoundItem.mIsDone = isDone;
mData.recalculatePositionOfItemAt(adapterPosition);
}
};
}
@Override
public void onBindViewHolder(TodoViewHolder holder, int position) {
holder.bindTo(mData.get(position));
}
@Override
public int getItemCount() {
return mData.size();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
privatestaticclassSortedListAdapterextendsRecyclerView.Adapter{
SortedListmData;
finalLayoutInflatermLayoutInflater;
publicSortedListAdapter(LayoutInflaterlayoutInflater,Item...items){
mLayoutInflater=layoutInflater;
mData=newSortedList(Item.class,newSortedListAdapterCallback(this){
@Override
publicintcompare(Itemt0,Itemt1){
if(t0.mIsDone!=t1.mIsDone){
returnt0.mIsDone?1:-1;
}
inttxtComp=t0.mText.compareTo(t1.mText);
if(txtComp!=0){
returntxtComp;
}
if(t0.id
return-1;
}elseif(t0.id>t1.id){
return1;
}
return0;
}
@Override
publicbooleanareContentsTheSame(ItemoldItem,
ItemnewItem){
returnoldItem.mText.equals(newItem.mText);
}
@Override
publicbooleanareItemsTheSame(Itemitem1,Itemitem2){
returnitem1.id==item2.id;
}
});
for(Itemitem:items){
mData.add(item);
}
}
publicvoidaddItem(Itemitem){
mData.add(item);
}
@Override
publicTodoViewHolderonCreateViewHolder(finalViewGroupparent,intviewType){
returnnewTodoViewHolder(
mLayoutInflater.inflate(R.layout.sorted_list_item_view,parent,false)){
@Override
voidonDoneChanged(booleanisDone){
intadapterPosition=getAdapterPosition();
if(adapterPosition==RecyclerView.NO_POSITION){
return;
}
mBoundItem.mIsDone=isDone;
mData.recalculatePositionOfItemAt(adapterPosition);
}
};
}
@Override
publicvoidonBindViewHolder(TodoViewHolderholder,intposition){
holder.bindTo(mData.get(position));
}
@Override
publicintgetItemCount(){
returnmData.size();
}
}