Что такое SortedList <T>, работающий с RecyclerView.Adapter?
Вчера была выпущена
Android Support Library 22.1. Многие новые функции были добавлены в библиотеку поддержки v4 и v7, среди которых android.support.v7.util.SortedList<T>
привлекает мое внимание.
Он сказал, что SortedList
- новая структура данных, работает с RecyclerView.Adapter
, поддерживает добавленную/добавленную/перемещенную/измененную анимацию элемента, предоставленную RecyclerView
. Это звучит как List<T>
в ListView
, но кажется более продвинутым и мощным.
Итак, в чем разница между SortedList<T>
и List<T>
? Как я могу использовать его эффективно? Что такое принудительное выполнение SortedList<T>
над List<T>
, если это так? Может ли кто-нибудь опубликовать некоторые его образцы?
Будут оценены любые советы или коды. Спасибо заранее.
Ответы
Ответ 1
SortedList
находится в v7 support library
.
A SortedList
реализация, которая может содержать элементы в порядке, а также уведомлять об изменениях в списке, чтобы он мог быть связан с RecyclerView.Adapter
.
Сохраняет элементы, упорядоченные с использованием метода compare(Object, Object)
и использует двоичный поиск для извлечения элементов. Если критерии сортировки элементы могут измениться, убедитесь, что вы вызываете соответствующие методы во время редактирования чтобы избежать несоответствий данных.
Вы можете контролировать порядок элементов и изменять уведомления через SortedList.Callback
.
Ниже приведен пример использования 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;
}
}
}
Ответ 2
SortedList
обрабатывает связь с адаптером Recycler через Callback
.
Одно отличие между SortedList
и List
показано в вспомогательном методе addAll
в приведенном ниже примере.
public void addAll(List<Page> items) {
mPages.beginBatchedUpdates();
for (Page item : items) {
mPages.add(item);
}
mPages.endBatchedUpdates();
}
- Сохраняет последний добавленный элемент
Скажем, у меня есть 10 кешированных элементов, которые нужно загрузить сразу же после заполнения списка ресайклеров. В то же время я запрашиваю свою сеть для тех же 10 элементов, потому что они могли измениться с тех пор, как я их кешировал. Я могу вызвать тот же метод addAll
, а SortedList
заменит cachedItems на fetchedItems под капотом (всегда держит последний добавленный элемент).
// After creating adapter
myAdapter.addAll(cachedItems)
// Network callback
myAdapter.addAll(fetchedItems)
В регулярном List
у меня будут дубликаты всех моих предметов (размер списка - 20). С помощью SortedList
он заменяет элементы, которые являются одинаковыми, используя обратный вызов areItemsTheSame
.
- Внимательно оценивает, когда обновлять представления
Когда добавлены элементы fetchedItems, onChange
будет вызываться только в том случае, если изменился один или несколько заголовков Page
. Вы можете настроить то, что ищет SortedList
в обратном вызове areContentsTheSame
.
- Его исполнитель
Если вы собираетесь добавить несколько элементов в SortedList, вызов BatchedCallback конвертирует отдельные inInserted (index, 1) вызовы в один onInserted (index, N), если элементы добавляются в последовательные индексы. Это изменение может помочь RecyclerView разрешить изменения намного легче.
Пример
У вас может быть геттер на вашем адаптере для вашего SortedList
, но я просто решил добавить вспомогательные методы к моему адаптеру.
Класс адаптера:
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();
}
}
Класс страницы:
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>
Класс владельца:
public class PageViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public PageViewHolder(View itemView) {
super(itemView);
textView = (TextView)item.findViewById(R.id.text_view);
}
}
Ответ 3
В исходном репозитории библиотеки поддержки есть пример SortedListActivity, который демонстрирует, как использовать SortedList и SortedListAdapterCallback внутри RecyclerView.Adapter. Из корня SDK с установленной библиотекой поддержки она должна быть в extras/android/support/samples/Support7Demos/src/com/example/android/supportv7/util/SortedListActivity.java
(также на github).
Существование этих конкретных образцов упоминается ровно один раз в документации Google, в нижней части страницы, посвященной другой теме, поэтому я не обвиняйте вас в том, что вы его не нашли.
Ответ 4
О реализации SortedList
, она поддерживается массивом <T>
с минимальной емкостью по умолчанию в 10 элементов. Когда массив будет заполнен, массив будет изменен до size() + 10
Исходный код доступен здесь
Из документация
A Сортировка списка, которая может содержать элементы в порядке, а также уведомлять об изменениях в списке, чтобы он мог быть связан с RecyclerView.Adapter.
Он сохраняет упорядоченные элементы, используя метод сравнения (Object, Object) и использует двоичный поиск для извлечения элементов. Если критерии сортировки элементы могут измениться, убедитесь, что вы вызываете соответствующие методы во время редактирования чтобы избежать несоответствий данных.
Вы можете контролировать порядок элементов и изменять уведомления через Параметр SortedList.Callback.
Что касается производительности, они также добавили SortedList.BatchedCallback для выполнения нескольких операций одновременно вместо одного в то время
Репликация обратного вызова, которая может отправлять уведомления о событиях, отправленных SortedList.
Этот класс может быть полезен, если вы хотите выполнять несколько операций на SortedList, но не хотите посылать каждое событие по одному, что может приводят к проблемам с производительностью.
Например, если вы собираетесь добавить несколько элементов в SortedList, Выбранный вызов BatchedCallback индивидуальный onInserted (индекс, 1) звонки в один onInserted (индекс, N), если элементы добавляются в последовательные индексы. Это изменение может помочь RecyclerView значительно изменить изменения легко.
Если последовательные изменения в SortedList не подходят для пакетная обработка, BatchingCallback отправляет их, как только такой случай обнаружено. После того, как ваши изменения в SortedList завершены, вы должны всегда вызывайте dispatchLastEvent(), чтобы очистить все изменения от обратного вызова.