Quel est le fonctionnement de SortedList avec RecyclerView.Adapter?

Android Support Library 22.1 a été publié hier. De nombreuses nouvelles fonctionnalités ont été ajoutées à la bibliothèque de support v4 et à la version 7, parmi lesquelles android.support.v7.util.SortedList attire mon attention.

On dit que SortedList est une nouvelle structure de données, fonctionne avec RecyclerView.Adapter , maintient l’élément ajouté / supprimé / déplacé / modifié par les animations fournies par RecyclerView . Cela ressemble à une List dans un ListView mais semble plus avancé et puissant.

Alors, quelle est la différence entre SortedList et List ? Comment pourrais-je l’utiliser efficacement? Quelle est l’application de SortedList sur List si c’est le cas? Quelqu’un pourrait-il en poster quelques exemples?

Tout conseil ou code sera apprécié. Merci d’avance.

SortedList gère la communication avec l’adaptateur Recycler via Callback .

Une différence entre SortedList et List est observée dans la méthode d’assistance addAll dans l’exemple ci-dessous.

 public void addAll(List items) { mPages.beginBatchedUpdates(); for (Page item : items) { mPages.add(item); } mPages.endBatchedUpdates(); } 
  1. Garde le dernier élément ajouté

Supposons que 10 éléments en mémoire cache soient chargés immédiatement lorsque ma liste de recycleurs est remplie. Dans le même temps, je demande à mon réseau les 10 mêmes éléments car ils ont pu changer depuis que je les ai mis en cache. Je peux appeler la même méthode addAll et SortedList remplacera les cachedItems par fetchedItems sous le capot (conserve toujours le dernier élément ajouté).

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

Dans une List régulière, j’aurais des doublons de tous mes articles (taille de liste de 20). Avec SortedList il remplace les éléments identiques à l’aide de la fonction de areItemsTheSame .

  1. Son intelligent quant à quand mettre à jour les vues

Lorsque les objects récupérés sont ajoutés, onChange ne sera appelé que si un ou plusieurs des titres de la Page ont été modifiés. Vous pouvez personnaliser ce que SortedList recherche dans le areContentsTheSame de areContentsTheSame .

  1. Son performant

Si vous allez append plusieurs éléments à une liste SortedList, l’appel BatchedCallback convertit les appels individuels onInserted (index, 1) en un onInserted (index, N) si des éléments sont ajoutés dans des index consécutifs. Cette modification peut aider RecyclerView à résoudre les problèmes beaucoup plus facilement.

Échantillon

Vous pouvez avoir un getter sur votre adaptateur pour votre SortedList , mais j’ai juste décidé d’append des méthodes d’aide à mon adaptateur.

Classe d’adaptateur:

  public class MyAdapter extends RecyclerView.Adapter { private SortedList mPages; public MyAdapter() { mPages = new SortedList(Page.class, new SortedList.Callback() { @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 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(); } } 

Classe de page:

 public class Page { private Ssortingng title; private long id; public Ssortingng getTitle() { return title; } public void setTitle(Ssortingng title) { this.title = title; } public long getId() { return id; } public void setId(long id) { this.id = id; } } 

Titulaire xml:

     

Classe de spectateur:

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

SortedList est dans la v7 support library .

Une implémentation SortedList qui peut garder les éléments en ordre et notifier les modifications de la liste de manière à ce qu’elle puisse être liée à RecyclerView.Adapter .

Il conserve les éléments ordonnés à l’aide de la méthode compare(Object, Object) et utilise la recherche binary pour récupérer des éléments. Si les critères de sorting de vos éléments peuvent changer, assurez-vous d’appeler les méthodes appropriées lors de leur modification pour éviter les incohérences de données.

Vous pouvez contrôler l’ordre des éléments et modifier les notifications via le paramètre SortedList.Callback .

Voici ci-dessous un exemple d’utilisation de SortedList , je pense que c’est ce que vous voulez, regardez-le et profitez-en!

 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 Ssortingng text = textView.getText().toSsortingng().sortingm(); if (text.length() > 0) { mAdapter.addItem(new Item(text)); } textView.setText(""); return true; } return false; } }); } 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(); } } 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 { Ssortingng mText; boolean mIsDone = false; final public int id; private static int idCounter = 0; public Item(Ssortingng text) { id = idCounter ++; this.mText = text; } } } 

Il existe un exemple SortedListActivity dans le référentiel source de la bibliothèque de support qui montre comment utiliser SortedList et SortedListAdapterCallback dans un RecyclerView.Adapter. A partir de la racine du SDK, avec la bibliothèque de support installée, il doit s’agir de extras/android/support/samples/Support7Demos/src/com/example/android/supportv7/util/SortedListActivity.java (également sur github ).

L’existence de ces échantillons particuliers est mentionnée exactement une fois dans la documentation de Google, au bas d’une page traitant d’un sujet différent. Je ne vous reproche donc pas de ne pas l’avoir trouvé.

A propos de l’implémentation de SortedList , elle est soutenue par un tableau de avec une capacité SortedList par défaut de 10 éléments. Une fois le tableau plein, le tableau est redimensionné à la size() + 10

Le code source est disponible ici

De la documentation

Une implémentation de liste sortingée qui peut garder les éléments en ordre et notifier les modifications dans la liste de manière à ce qu’elle puisse être liée à un RecyclerView.Adapter.

Il conserve les éléments ordonnés à l’aide de la méthode compare (Object, Object) et utilise la recherche binary pour récupérer des éléments. Si les critères de sorting de vos éléments peuvent changer, assurez-vous d’appeler les méthodes appropriées lors de leur modification pour éviter les incohérences de données.

Vous pouvez contrôler l’ordre des éléments et modifier les notifications via le paramètre SortedList.Callback.

En ce qui concerne les performances, ils ont également ajouté SortedList.BatchedCallback pour effectuer plusieurs opérations en même temps au lieu d’une seule à la fois.

Une implémentation de rappel capable de notifier par lot des événements envoyés par SortedList.

Cette classe peut être utile si vous souhaitez effectuer plusieurs opérations sur une SortedList mais ne souhaitez pas envoyer chaque événement un par un, ce qui peut entraîner un problème de performance.

Par exemple, si vous souhaitez append plusieurs éléments à une liste SortedList, l’appel BatchedCallback convertit les appels individuels onInserted (index, 1) en un onInserted (index, N) si des éléments sont ajoutés dans des index consécutifs. Cette modification peut aider RecyclerView à résoudre les problèmes beaucoup plus facilement.

Si des modifications consécutives dans la liste SortedList ne sont pas adaptées au traitement par lots, BatchingCallback les envoie dès que ce cas est détecté. Une fois vos modifications apscopes à la liste SortedList, vous devez toujours appeler dispatchLastEvent () pour vider toutes les modifications apscopes au rappel.