Android Support Library 22.1 est sorti hier. De nombreuses nouvelles fonctionnalités ont été ajoutées à la bibliothèque de support v4 et à la v7, parmi lesquelles Android.support.v7.util.SortedList<T>
attire mon attention.
On dit que SortedList
est une nouvelle structure de données, fonctionne avec RecyclerView.Adapter
, conserve les animations ajoutées/supprimées/déplacées/modifiées fournies par RecyclerView
. Cela ressemble à un List<T>
dans un ListView
mais semble plus avancé et puissant.
Alors, quelle est la différence entre SortedList<T>
et List<T>
? Comment pourrais-je l'utiliser efficacement? Quelle est l'application de SortedList<T>
plus de List<T>
si c'est le cas? Quelqu'un pourrait-il en poster des échantillons?
Tous les conseils ou codes seront appréciés. Merci d'avance.
SortedList
est dans v7 support library
.
Une implémentation
SortedList
qui peut conserver les éléments dans l’ordre et notifier également les modifications apportées à la liste de sorte qu’elle puisse être liée à unRecyclerView.Adapter
.Il conserve les éléments ordonnés à l'aide de la méthode
compare(Object, Object)
et utilise la recherche binaire pour extraire les éléments. Si les critères de tri de vos éléments peuvent changer, veillez à appeler les méthodes appropriées lors de leur modification pour éviter les incohérences dans les 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 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
gère la communication avec l'adaptateur Recycler via Callback
.
Une différence entre SortedList
et List
apparaît dans la méthode d'assistance addAll
de l'exemple ci-dessous.
public void addAll(List<Page> items) {
mPages.beginBatchedUpdates();
for (Page item : items) {
mPages.add(item);
}
mPages.endBatchedUpdates();
}
Disons que j'ai 10 éléments en cache à charger immédiatement lorsque ma liste de recycleurs est remplie. En même temps, je demande à mon réseau les mêmes 10 éléments, car ils pourraient avoir changé depuis que je les ai mis en cache. Je peux appeler la même méthode addAll
et SortedList
remplacera les éléments cached par fetchedItems sous le capot (conserve toujours le dernier élément ajouté).
// After creating adapter
myAdapter.addAll(cachedItems)
// Network callback
myAdapter.addAll(fetchedItems)
Dans un List
normal, j'aurais des doublons de tous mes éléments (taille de la liste de 20). Avec SortedList
, il remplace les éléments identiques à l'aide de areItemsTheSame
de Callback.
Lorsque les éléments fetchedItems sont ajoutés, onChange
ne sera appelé que si un ou plusieurs titres de Page
ont changé. Vous pouvez personnaliser ce que SortedList
recherche dans le areContentsTheSame
du rappel.
Si vous envisagez d'ajouter plusieurs éléments à une liste SortedList, l'appel de BatchedCallback convertit des appels individuels onInserted (index, 1) en un seul onInserted (index, N) si des éléments sont ajoutés à des index consécutifs. Cette modification peut aider RecyclerView à résoudre les modifications beaucoup plus facilement.
Vous pouvez avoir un getter sur votre adaptateur pour votre SortedList
, mais je viens de décider d'ajouter des méthodes d'assistance à mon adaptateur.
Classe d'adaptateur:
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();
}
}
Classe de page:
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>
Classe Viewholder:
public class PageViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public PageViewHolder(View itemView) {
super(itemView);
textView = (TextView)item.findViewById(R.id.text_view);
}
}
Un exemple de SortedListActivity dans le référentiel source de la bibliothèque de support montre comment utiliser SortedList et SortedListAdapterCallback dans un fichier RecyclerView.Adapter. À partir de la racine du SDK, avec la bibliothèque de support installée, vous devez vous trouver à extras/Android/support/samples/Support7Demos/src/com/example/Android/supportv7/util/SortedListActivity.Java
_ (également sur github ).
L'existence de ces exemples particuliers est mentionnée ne seule 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é.
À propos de SortedList
implémentation, il est soutenu par un tableau de <T>
Avec une capacité minimale par défaut de 10 éléments. Une fois que le tableau est plein, le tableau est redimensionné à size() + 10
Le code source est disponible ici
Une implémentation de liste triée pouvant garder les éléments dans l'ordre et notifier les modifications dans la liste, de sorte qu'elle puisse être liée à RecyclerView.Adapter.
Il conserve les éléments ordonnés à l'aide de la méthode de comparaison (Object, Object) et utilise la recherche binaire pour récupérer des éléments. Si les critères de tri de vos éléments peuvent changer, veillez à appeler les méthodes appropriées lors de leur modification pour éviter les incohérences dans les 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 simultanément, au lieu de une à la fois.
Une implémentation de rappel qui peut notifier par lots les événements envoyés par SortedList.
Cette classe peut être utile si vous souhaitez effectuer plusieurs opérations sur une liste SortedList mais ne souhaitez pas distribuer chaque événement un par un, ce qui peut entraîner des problèmes de performances.
Par exemple, si vous envisagez d'ajouter plusieurs éléments à une liste SortedList, l'appel de BatchedCallback convertit les appels individuels onInserted (index, 1) en un seul onInserted (index, N) si les éléments sont ajoutés à des index consécutifs. Cette modification peut aider RecyclerView à résoudre les modifications beaucoup plus facilement.
Si des modifications consécutives dans SortedList ne conviennent pas à la mise en lot, BatchingCallback les distribue dès que ce cas est détecté. Une fois que vos modifications sur SortedList sont terminées, vous devez toujours appeler dispatchLastEvent () pour annuler toutes les modifications apportées au rappel.