什么是 SortedList<T> 与 RecyclerView.Adapter 一起工作?
What is the SortedList<T> working with RecyclerView.Adapter?
Android 支持库 22.1 已于昨天发布。 v4和v7的支持库中增加了很多新特性,其中android.support.v7.util.SortedList<T>
引起了我的注意。
据说,SortedList
是一种新的数据结构,与RecyclerView.Adapter
一起工作,维护RecyclerView
提供的项目added/deleted/moved/changed动画。它听起来像 List<T>
中的 ListView
,但看起来更高级、更强大。
那么,SortedList<T>
和List<T>
有什么区别呢?我怎样才能有效地使用它?如果是这样,SortedList<T>
对 List<T>
的强制执行是什么?有人可以 post 一些样本吗?
任何提示或代码将不胜感激。提前致谢。
关于 SortedList
实施,它由 <T>
数组支持,默认最小容量为 10 项。一旦数组已满,数组的大小将调整为 size() + 10
源代码可用here
A Sorted list implementation that can keep items in order and also
notify for changes in the list such that it can be bound to a
RecyclerView.Adapter.
It keeps items ordered using the compare(Object, Object) method and
uses binary search to retrieve items. If the sorting criteria of your
items may change, make sure you call appropriate methods while editing
them to avoid data inconsistencies.
You can control the order of items and change notifications via the
SortedList.Callback parameter.
关于性能,他们还添加了 SortedList.BatchedCallback 以一次执行多个操作而不是一次执行一个操作
A callback implementation that can batch notify events dispatched by
the SortedList.
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.
For example, if you are going to add multiple items to a SortedList,
BatchedCallback call convert individual onInserted(index, 1) calls
into one onInserted(index, N) if items are added into consecutive
indices. This change can help RecyclerView resolve changes much more
easily.
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 must
always call dispatchLastEvent() to flush all changes to the Callback.
SortedList
通过 Callback
处理与 Recycler 适配器的通信。
SortedList
和 List
之间的一个区别体现在下面示例中的 addAll
辅助方法中。
public void addAll(List<Page> items) {
mPages.beginBatchedUpdates();
for (Page item : items) {
mPages.add(item);
}
mPages.endBatchedUpdates();
}
- 保留最后添加的项目
假设我有 10 个缓存项目要在我的回收站列表被填充时立即加载。同时,我在我的网络中查询相同的 10 个项目,因为它们在我缓存后可能已经发生变化。我可以调用相同的 addAll
方法,并且 SortedList
将在后台用 fetchedItems 替换 cachedItems(始终保留最后添加的项目)。
// After creating adapter
myAdapter.addAll(cachedItems)
// Network callback
myAdapter.addAll(fetchedItems)
在常规 List
中,我会复制所有项目(列表大小为 20)。使用 SortedList
它会替换使用回调的 areItemsTheSame
.
相同的项目
- 何时更新视图很聪明
添加 fetchedItems 后,仅当 Page
的一个或多个标题发生更改时才会调用 onChange
。您可以自定义 SortedList
在回调的 areContentsTheSame
中查找的内容。
- 其性能
If you are going to add multiple items to a SortedList, BatchedCallback call convert individual onInserted(index, 1) calls into one onInserted(index, N) if items are added into consecutive indices. This change can help RecyclerView resolve changes much more easily.
示例
您可以在适配器上为 SortedList
添加一个 getter,但我只是决定向我的适配器添加辅助方法。
适配器Class:
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private SortedList<Page> mPages;
public MyAdapter() {
mPages = new SortedList<Page>(Page.class, new SortedList.Callback<Page>() {
@Override
public int compare(Page o1, Page o2) {
return o1.getTitle().compareTo(o2.getTitle());
}
@Override
public void onInserted(int position, int count) {
notifyItemRangeInserted(position, count);
}
@Override
public void onRemoved(int position, int count) {
notifyItemRangeRemoved(position, count);
}
@Override
public void onMoved(int fromPosition, int toPosition) {
notifyItemMoved(fromPosition, toPosition);
}
@Override
public void onChanged(int position, int count) {
notifyItemRangeChanged(position, count);
}
@Override
public boolean areContentsTheSame(Page oldItem, Page newItem) {
// return whether the items' visual representations are the same or not.
return oldItem.getTitle().equals(newItem.getTitle());
}
@Override
public boolean areItemsTheSame(Page item1, Page item2) {
return item1.getId() == item2.getId();
}
});
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.viewholder_page, parent, false);
return new PageViewHolder(view);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
PageViewHolder pageViewHolder = (PageViewHolder) holder;
Page page = mPages.get(position);
pageViewHolder.textView.setText(page.getTitle());
}
@Override
public int getItemCount() {
return mPages.size();
}
// region PageList Helpers
public Page get(int position) {
return mPages.get(position);
}
public int add(Page item) {
return mPages.add(item);
}
public int indexOf(Page item) {
return mPages.indexOf(item);
}
public void updateItemAt(int index, Page item) {
mPages.updateItemAt(index, item);
}
public void addAll(List<Page> items) {
mPages.beginBatchedUpdates();
for (Page item : items) {
mPages.add(item);
}
mPages.endBatchedUpdates();
}
public void addAll(Page[] items) {
addAll(Arrays.asList(items));
}
public boolean remove(Page item) {
return mPages.remove(item);
}
public Page removeItemAt(int index) {
return mPages.removeItemAt(index);
}
public void clear() {
mPages.beginBatchedUpdates();
//remove items at end, to avoid unnecessary array shifting
while (mPages.size() > 0) {
mPages.removeItemAt(mPages.size() - 1);
}
mPages.endBatchedUpdates();
}
}
第class页:
public class Page {
private String title;
private long id;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
Viewholder xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/text_view"
style="@style/TextStyle.Primary.SingleLine"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Viewholder class:
public class PageViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public PageViewHolder(View itemView) {
super(itemView);
textView = (TextView)item.findViewById(R.id.text_view);
}
}
支持库源存储库中有一个示例 SortedListActivity,它演示了如何在 RecyclerView.Adapter 中使用 SortedList 和 SortedListAdapterCallback。从 SDK 的根目录开始,安装支持库后,它应该位于 extras/android/support/samples/Support7Demos/src/com/example/android/supportv7/util/SortedListActivity.java
(也在 github)。
exactly once 在 Google 的文档中提到了这些特定样本的存在,在处理不同主题的页面底部,所以我不怪你没有找到它。
SortedList
在 v7 support library
。
A SortedList
implementation that can keep items in order and also
notify for changes in the list such that it can be bound to a
RecyclerView.Adapter
.
It keeps items ordered using the compare(Object, Object)
method and
uses binary search to retrieve items. If the sorting criteria of your
items may change, make sure you call appropriate methods while editing
them to avoid data inconsistencies.
You can control the order of items and change notifications via the
SortedList.Callback
parameter.
下面是SortedList
的使用示例,我想这就是你想要的,看看吧,享受吧!
public class SortedListActivity extends ActionBarActivity {
private RecyclerView mRecyclerView;
private LinearLayoutManager mLinearLayoutManager;
private SortedListAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sorted_list_activity);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
mLinearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
mAdapter = new SortedListAdapter(getLayoutInflater(),
new Item("buy milk"), new Item("wash the car"),
new Item("wash the dishes"));
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setHasFixedSize(true);
final EditText newItemTextView = (EditText) findViewById(R.id.new_item_text_view);
newItemTextView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
if (id == EditorInfo.IME_ACTION_DONE &&
(keyEvent == null || keyEvent.getAction() == KeyEvent.ACTION_DOWN)) {
final String text = textView.getText().toString().trim();
if (text.length() > 0) {
mAdapter.addItem(new Item(text));
}
textView.setText("");
return true;
}
return false;
}
});
}
private static class SortedListAdapter extends RecyclerView.Adapter<TodoViewHolder> {
SortedList<Item> mData;
final LayoutInflater mLayoutInflater;
public SortedListAdapter(LayoutInflater layoutInflater, Item... items) {
mLayoutInflater = layoutInflater;
mData = new SortedList<Item>(Item.class, new SortedListAdapterCallback<Item>(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();
}
}
abstract private static class TodoViewHolder extends RecyclerView.ViewHolder {
final CheckBox mCheckBox;
Item mBoundItem;
public TodoViewHolder(View itemView) {
super(itemView);
mCheckBox = (CheckBox) itemView;
mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (mBoundItem != null && isChecked != mBoundItem.mIsDone) {
onDoneChanged(isChecked);
}
}
});
}
public void bindTo(Item item) {
mBoundItem = item;
mCheckBox.setText(item.mText);
mCheckBox.setChecked(item.mIsDone);
}
abstract void onDoneChanged(boolean isChecked);
}
private static class Item {
String mText;
boolean mIsDone = false;
final public int id;
private static int idCounter = 0;
public Item(String text) {
id = idCounter ++;
this.mText = text;
}
}
}
Android 支持库 22.1 已于昨天发布。 v4和v7的支持库中增加了很多新特性,其中android.support.v7.util.SortedList<T>
引起了我的注意。
据说,SortedList
是一种新的数据结构,与RecyclerView.Adapter
一起工作,维护RecyclerView
提供的项目added/deleted/moved/changed动画。它听起来像 List<T>
中的 ListView
,但看起来更高级、更强大。
那么,SortedList<T>
和List<T>
有什么区别呢?我怎样才能有效地使用它?如果是这样,SortedList<T>
对 List<T>
的强制执行是什么?有人可以 post 一些样本吗?
任何提示或代码将不胜感激。提前致谢。
关于 SortedList
实施,它由 <T>
数组支持,默认最小容量为 10 项。一旦数组已满,数组的大小将调整为 size() + 10
源代码可用here
A Sorted list implementation that can keep items in order and also notify for changes in the list such that it can be bound to a RecyclerView.Adapter.
It keeps items ordered using the compare(Object, Object) method and uses binary search to retrieve items. If the sorting criteria of your items may change, make sure you call appropriate methods while editing them to avoid data inconsistencies.
You can control the order of items and change notifications via the SortedList.Callback parameter.
关于性能,他们还添加了 SortedList.BatchedCallback 以一次执行多个操作而不是一次执行一个操作
A callback implementation that can batch notify events dispatched by the SortedList.
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.
For example, if you are going to add multiple items to a SortedList, BatchedCallback call convert individual onInserted(index, 1) calls into one onInserted(index, N) if items are added into consecutive indices. This change can help RecyclerView resolve changes much more easily.
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 must always call dispatchLastEvent() to flush all changes to the Callback.
SortedList
通过 Callback
处理与 Recycler 适配器的通信。
SortedList
和 List
之间的一个区别体现在下面示例中的 addAll
辅助方法中。
public void addAll(List<Page> items) {
mPages.beginBatchedUpdates();
for (Page item : items) {
mPages.add(item);
}
mPages.endBatchedUpdates();
}
- 保留最后添加的项目
假设我有 10 个缓存项目要在我的回收站列表被填充时立即加载。同时,我在我的网络中查询相同的 10 个项目,因为它们在我缓存后可能已经发生变化。我可以调用相同的 addAll
方法,并且 SortedList
将在后台用 fetchedItems 替换 cachedItems(始终保留最后添加的项目)。
// After creating adapter
myAdapter.addAll(cachedItems)
// Network callback
myAdapter.addAll(fetchedItems)
在常规 List
中,我会复制所有项目(列表大小为 20)。使用 SortedList
它会替换使用回调的 areItemsTheSame
.
- 何时更新视图很聪明
添加 fetchedItems 后,仅当 Page
的一个或多个标题发生更改时才会调用 onChange
。您可以自定义 SortedList
在回调的 areContentsTheSame
中查找的内容。
- 其性能
If you are going to add multiple items to a SortedList, BatchedCallback call convert individual onInserted(index, 1) calls into one onInserted(index, N) if items are added into consecutive indices. This change can help RecyclerView resolve changes much more easily.
示例
您可以在适配器上为 SortedList
添加一个 getter,但我只是决定向我的适配器添加辅助方法。
适配器Class:
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private SortedList<Page> mPages;
public MyAdapter() {
mPages = new SortedList<Page>(Page.class, new SortedList.Callback<Page>() {
@Override
public int compare(Page o1, Page o2) {
return o1.getTitle().compareTo(o2.getTitle());
}
@Override
public void onInserted(int position, int count) {
notifyItemRangeInserted(position, count);
}
@Override
public void onRemoved(int position, int count) {
notifyItemRangeRemoved(position, count);
}
@Override
public void onMoved(int fromPosition, int toPosition) {
notifyItemMoved(fromPosition, toPosition);
}
@Override
public void onChanged(int position, int count) {
notifyItemRangeChanged(position, count);
}
@Override
public boolean areContentsTheSame(Page oldItem, Page newItem) {
// return whether the items' visual representations are the same or not.
return oldItem.getTitle().equals(newItem.getTitle());
}
@Override
public boolean areItemsTheSame(Page item1, Page item2) {
return item1.getId() == item2.getId();
}
});
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.viewholder_page, parent, false);
return new PageViewHolder(view);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
PageViewHolder pageViewHolder = (PageViewHolder) holder;
Page page = mPages.get(position);
pageViewHolder.textView.setText(page.getTitle());
}
@Override
public int getItemCount() {
return mPages.size();
}
// region PageList Helpers
public Page get(int position) {
return mPages.get(position);
}
public int add(Page item) {
return mPages.add(item);
}
public int indexOf(Page item) {
return mPages.indexOf(item);
}
public void updateItemAt(int index, Page item) {
mPages.updateItemAt(index, item);
}
public void addAll(List<Page> items) {
mPages.beginBatchedUpdates();
for (Page item : items) {
mPages.add(item);
}
mPages.endBatchedUpdates();
}
public void addAll(Page[] items) {
addAll(Arrays.asList(items));
}
public boolean remove(Page item) {
return mPages.remove(item);
}
public Page removeItemAt(int index) {
return mPages.removeItemAt(index);
}
public void clear() {
mPages.beginBatchedUpdates();
//remove items at end, to avoid unnecessary array shifting
while (mPages.size() > 0) {
mPages.removeItemAt(mPages.size() - 1);
}
mPages.endBatchedUpdates();
}
}
第class页:
public class Page {
private String title;
private long id;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
Viewholder xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/text_view"
style="@style/TextStyle.Primary.SingleLine"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Viewholder class:
public class PageViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public PageViewHolder(View itemView) {
super(itemView);
textView = (TextView)item.findViewById(R.id.text_view);
}
}
支持库源存储库中有一个示例 SortedListActivity,它演示了如何在 RecyclerView.Adapter 中使用 SortedList 和 SortedListAdapterCallback。从 SDK 的根目录开始,安装支持库后,它应该位于 extras/android/support/samples/Support7Demos/src/com/example/android/supportv7/util/SortedListActivity.java
(也在 github)。
exactly once 在 Google 的文档中提到了这些特定样本的存在,在处理不同主题的页面底部,所以我不怪你没有找到它。
SortedList
在 v7 support library
。
A
SortedList
implementation that can keep items in order and also notify for changes in the list such that it can be bound to aRecyclerView.Adapter
.It keeps items ordered using the
compare(Object, Object)
method and uses binary search to retrieve items. If the sorting criteria of your items may change, make sure you call appropriate methods while editing them to avoid data inconsistencies.You can control the order of items and change notifications via the
SortedList.Callback
parameter.
下面是SortedList
的使用示例,我想这就是你想要的,看看吧,享受吧!
public class SortedListActivity extends ActionBarActivity {
private RecyclerView mRecyclerView;
private LinearLayoutManager mLinearLayoutManager;
private SortedListAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sorted_list_activity);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
mLinearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
mAdapter = new SortedListAdapter(getLayoutInflater(),
new Item("buy milk"), new Item("wash the car"),
new Item("wash the dishes"));
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setHasFixedSize(true);
final EditText newItemTextView = (EditText) findViewById(R.id.new_item_text_view);
newItemTextView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
if (id == EditorInfo.IME_ACTION_DONE &&
(keyEvent == null || keyEvent.getAction() == KeyEvent.ACTION_DOWN)) {
final String text = textView.getText().toString().trim();
if (text.length() > 0) {
mAdapter.addItem(new Item(text));
}
textView.setText("");
return true;
}
return false;
}
});
}
private static class SortedListAdapter extends RecyclerView.Adapter<TodoViewHolder> {
SortedList<Item> mData;
final LayoutInflater mLayoutInflater;
public SortedListAdapter(LayoutInflater layoutInflater, Item... items) {
mLayoutInflater = layoutInflater;
mData = new SortedList<Item>(Item.class, new SortedListAdapterCallback<Item>(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();
}
}
abstract private static class TodoViewHolder extends RecyclerView.ViewHolder {
final CheckBox mCheckBox;
Item mBoundItem;
public TodoViewHolder(View itemView) {
super(itemView);
mCheckBox = (CheckBox) itemView;
mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (mBoundItem != null && isChecked != mBoundItem.mIsDone) {
onDoneChanged(isChecked);
}
}
});
}
public void bindTo(Item item) {
mBoundItem = item;
mCheckBox.setText(item.mText);
mCheckBox.setChecked(item.mIsDone);
}
abstract void onDoneChanged(boolean isChecked);
}
private static class Item {
String mText;
boolean mIsDone = false;
final public int id;
private static int idCounter = 0;
public Item(String text) {
id = idCounter ++;
this.mText = text;
}
}
}