web-dev-qa-db-fra.com

Qu'est-ce que SortedList <T> fonctionne avec RecyclerView.Adapter?

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.

69
SilentKnight

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 à un RecyclerView.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;
        }
    }
}
14
SilentKnight

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();
    }
  1. Conserve le dernier élément ajouté

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.

  1. C'est intelligent quand mettre à jour les vues

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.

  1. Son performant

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.

Échantillon

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);
    }
}
94
Amozoss

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é.

5
moskvax

À 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

De documentation

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.

2
Axxiss