android appcompat android-appcompat android-recyclerview sortedlist

android - ¿Qué es el SortedList<T> trabajando con RecyclerView.Adapter?



appcompat android-appcompat (4)

Android Support Library 22.1 se lanzó ayer. Se agregaron muchas características nuevas a la biblioteca de soporte de v4 y v7, entre las cuales android.support.v7.util.SortedList<T> me llama la atención.

Se dice que SortedList es una nueva estructura de datos, funciona con RecyclerView.Adapter , mantiene el elemento agregado / eliminado / movido / modificado animaciones proporcionadas por RecyclerView . Suena como una List<T> en un ListView pero parece más avanzado y poderoso.

Entonces, ¿cuál es la diferencia entre SortedList<T> y List<T> ? ¿Cómo podría usarlo de manera eficiente? ¿Cuál es la aplicación de SortedList<T> sobre List<T> si es así? ¿Alguien podría publicar algunas muestras?

Cualquier sugerencia o código será apreciado. Gracias por adelantado.


Acerca de la implementación de SortedList , está respaldada por una matriz de <T> con una capacidad mínima predeterminada de 10 elementos. Una vez que la matriz está llena, la matriz cambia su size() + 10 a size() + 10

El código fuente está disponible here

De la developer.android.com/reference/android/support/v7/util/…

Una implementación de lista ordenada que puede mantener los elementos en orden y también notificar los cambios en la lista de modo que pueda vincularse a un RecyclerView.Adapter.

Mantiene los elementos ordenados utilizando el método de comparación (Objeto, Objeto) y utiliza la búsqueda binaria para recuperar elementos. Si los criterios de clasificación de sus elementos pueden cambiar, asegúrese de llamar a los métodos apropiados mientras los edita para evitar inconsistencias de datos.

Puede controlar el orden de los elementos y cambiar las notificaciones a través del parámetro SortedList.Callback.

En cuanto al rendimiento, también agregaron SortedList.BatchedCallback para realizar varias operaciones a la vez en lugar de una a la vez

Una implementación de devolución de llamada que puede notificar por lotes los eventos enviados por SortedList.

Esta clase puede ser útil si desea realizar múltiples operaciones en una SortedList pero no desea distribuir cada evento uno por uno, lo que puede ocasionar un problema de rendimiento.

Por ejemplo, si va a agregar varios elementos a una lista ordenada, la llamada BatchedCallback convierte las llamadas individuales onInserted (índice, 1) en una onInserted (índice, N) si los elementos se agregan en índices consecutivos. Este cambio puede ayudar a RecyclerView a resolver los cambios mucho más fácilmente.

Si los cambios consecutivos en SortedList no son adecuados para el procesamiento por lotes, BatchingCallback los despacha tan pronto como se detecta dicho caso. Después de completar sus ediciones en SortedList, siempre debe llamar a dispatchLastEvent () para eliminar todos los cambios en la devolución de llamada.


Hay un ejemplo de SortedListActivity en el repositorio de origen de la biblioteca de soporte que muestra cómo usar SortedList y SortedListAdapterCallback dentro de un RecyclerView.Adapter. Desde la raíz del SDK, con la biblioteca de soporte instalada, debe estar en extras/android/support/samples/Support7Demos/src/com/example/android/supportv7/util/SortedListActivity.java (también en github ).

La existencia de estas muestras particulares se menciona exactamente una vez en la documentación de Google, en la parte inferior de una página que trata un tema diferente, por lo que no te culpo por no haberlo encontrado.


SortedList está en la v7 support library .

Una implementación de SortedList que puede mantener los elementos en orden y también notificar los cambios en la lista de modo que pueda vincularse a un RecyclerView.Adapter .

Mantiene los elementos ordenados utilizando el método de compare(Object, Object) y utiliza la búsqueda binaria para recuperar elementos. Si los criterios de clasificación de sus elementos pueden cambiar, asegúrese de llamar a los métodos apropiados mientras los edita para evitar inconsistencias de datos.

Puede controlar el orden de los elementos y cambiar las notificaciones a través del parámetro SortedList.Callback .

Aquí debajo hay una muestra del uso de SortedList , creo que es lo que quieres, ¡échale un vistazo y disfruta!

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; } } }


SortedList maneja la comunicación con el adaptador Recycler a través de Callback .

Se SortedList una diferencia entre SortedList y List en el método auxiliar addAll en el ejemplo a continuación.

public void addAll(List<Page> items) { mPages.beginBatchedUpdates(); for (Page item : items) { mPages.add(item); } mPages.endBatchedUpdates(); }

  1. Mantiene el último elemento agregado

Digamos que tengo 10 elementos en caché para cargar de inmediato cuando se llena mi lista de recicladores. Al mismo tiempo, consulto en mi red los mismos 10 elementos porque podrían haber cambiado desde que los almacené en caché. Puedo llamar al mismo método addAll y SortedList reemplazará cachedItems con fetchedItems debajo del capó (siempre mantiene el último elemento agregado).

// After creating adapter myAdapter.addAll(cachedItems) // Network callback myAdapter.addAll(fetchedItems)

En una List normal, tendría duplicados de todos mis artículos (tamaño de lista de 20). Con SortedList reemplaza los elementos que son iguales usando los areItemsTheSame .

  1. Es inteligente sobre cuándo actualizar las Vistas

Cuando se agregan onChange , onChange solo se llamará si uno o más de los títulos de la Page cambiaron. Puede personalizar lo que busca SortedList en areContentsTheSame de areContentsTheSame .

  1. Su rendimiento

Si va a agregar varios elementos a una lista ordenada, la llamada BatchedCallback convierte las llamadas individuales onInserted (index, 1) en una onInserted (index, N) si los elementos se agregan en índices consecutivos. Este cambio puede ayudar a RecyclerView a resolver los cambios mucho más fácilmente.

Muestra

Puede tener un captador en su adaptador para su SortedList , pero decidí agregar métodos auxiliares a mi adaptador.

Clase de adaptador:

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(); } }

Clase de página:

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>

Clase de espectador:

public class PageViewHolder extends RecyclerView.ViewHolder { public TextView textView; public PageViewHolder(View itemView) { super(itemView); textView = (TextView)item.findViewById(R.id.text_view); } }