webentwicklung-frage-antwort-db.com.de

Was ist die SortedList <T>, die mit RecyclerView.Adapter arbeitet?

Android Support Library 22.1 wurde gestern veröffentlicht. Viele neue Funktionen wurden in die v4-Unterstützungsbibliothek und in v7 aufgenommen, darunter Android.support.v7.util.SortedList<T> lenkt meine Aufmerksamkeit.

Es wird gesagt, dass SortedList eine neue Datenstruktur ist, die mit RecyclerView.Adapter, behält das von RecyclerView hinzugefügte/gelöschte/verschobene/geänderte Element bei. Es klingt wie ein List<T> in einem ListView aber scheint fortgeschrittener und mächtiger zu sein.

Also, was ist der Unterschied zwischen SortedList<T> und List<T>? Wie könnte ich es effizient nutzen? Was ist die Durchsetzung von SortedList<T> Über List<T> wenn es so ist? Könnte jemand ein paar Beispiele davon posten?

Irgendwelche Tipps oder Codes werden geschätzt. Danke im Voraus.

69
SilentKnight

SortedList ist in v7 support library.

Eine SortedList -Implementierung, mit der Elemente in Ordnung gehalten und Änderungen in der Liste gemeldet werden können, sodass sie an einen RecyclerView.Adapter Gebunden werden können.

Es behält Elemente bei, die mit der compare(Object, Object) -Methode sortiert wurden, und verwendet die binäre Suche, um Elemente abzurufen. Wenn sich die Sortierkriterien Ihrer Artikel möglicherweise ändern, stellen Sie sicher, dass Sie beim Bearbeiten die entsprechenden Methoden aufrufen, um Dateninkonsistenzen zu vermeiden.

Sie können die Reihenfolge der Elemente steuern und Benachrichtigungen über den Parameter SortedList.Callback Ändern.

Hier ist ein Anwendungsbeispiel für SortedList. Ich denke, es ist das, was Sie wollen. Schauen Sie es sich an und genießen Sie es!

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 verwaltet die Kommunikation zum Recycler-Adapter über Callback.

Ein Unterschied zwischen SortedList und List ist in der Hilfsmethode addAll im folgenden Beispiel zu sehen.

public void addAll(List<Page> items) {
        mPages.beginBatchedUpdates();
        for (Page item : items) {
            mPages.add(item);
        }
        mPages.endBatchedUpdates();
    }
  1. Behält den zuletzt hinzugefügten Gegenstand

Angenommen, ich habe 10 zwischengespeicherte Elemente, die sofort geladen werden können, wenn meine Recycler-Liste gefüllt ist. Gleichzeitig frage ich mein Netzwerk nach denselben 10 Elementen ab, da sie sich seit dem Zwischenspeichern möglicherweise geändert haben. Ich kann dieselbe addAll -Methode aufrufen und SortedList ersetzt die cachedItems durch fetchedItems unter der Haube (behält immer das zuletzt hinzugefügte Element bei).

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

In einem normalen List hätte ich Duplikate aller meiner Artikel (Listengröße 20). Mit SortedList werden Elemente ersetzt, die mit dem areItemsTheSame des Rückrufs identisch sind.

  1. Es ist klug, wann die Ansichten aktualisiert werden sollen

Wenn die fetchedItems hinzugefügt werden, wird onChange nur aufgerufen, wenn einer oder mehrere der Titel von Page geändert wurden. Sie können anpassen, wonach SortedList im areContentsTheSame des Rückrufs sucht.

  1. Es ist performant

Wenn Sie einer SortedList mehrere Elemente hinzufügen möchten, konvertiert der BatchedCallback-Aufruf einzelne onInserted-Aufrufe (Index, 1) in einen onInserted-Aufruf (Index, N), wenn Elemente aufeinanderfolgenden Indizes hinzugefügt werden. Diese Änderung kann dazu beitragen, dass RecyclerView Änderungen leichter auflöst.

Probe

Sie können einen Getter auf Ihrem Adapter für Ihr SortedList haben, aber ich habe gerade beschlossen, meinem Adapter Hilfsmethoden hinzuzufügen.

Adapterklasse:

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

Seitenklasse:

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>

Viewholder-Klasse:

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

Es gibt ein Beispiel für SortedListActivity im Quellrepository der Unterstützungsbibliothek, das die Verwendung von SortedList und SortedListAdapterCallback in einem RecyclerView.Adapter demonstriert. Im Stammverzeichnis des SDK sollte die Unterstützungsbibliothek unter extras/Android/support/samples/Support7Demos/src/com/example/Android/supportv7/util/SortedListActivity.Java Gespeichert sein (ebenfalls unter github ).

Die Existenz dieser speziellen Beispiele wird genau einmal in der Dokumentation von Google am Ende einer Seite erwähnt, die sich mit einem anderen Thema befasst. Ich beschuldige Sie also nicht, es nicht gefunden zu haben.

5
moskvax

Bei der Implementierung von SortedList wird es durch ein Array von <T> Mit einer Standardkapazität von mindestens 10 Elementen unterstützt. Sobald das Array voll ist, wird die Größe des Arrays auf size() + 10 geändert.

Der Quellcode ist verfügbar hier

Von Dokumentation

Eine sortierte Listenimplementierung, mit der Elemente in Ordnung gehalten und Änderungen in der Liste gemeldet werden können, sodass sie an einen RecyclerView.Adapter gebunden werden können.

Es behält Elemente bei, die mithilfe der Vergleichsmethode (Object, Object) sortiert wurden, und verwendet die binäre Suche, um Elemente abzurufen. Wenn sich die Sortierkriterien Ihrer Artikel möglicherweise ändern, stellen Sie sicher, dass Sie beim Bearbeiten die entsprechenden Methoden aufrufen, um Dateninkonsistenzen zu vermeiden.

Sie können die Reihenfolge der Elemente steuern und Benachrichtigungen über den Parameter SortedList.Callback ändern.

In Bezug auf die Leistung fügten sie auch SortedList.BatchedCallback hinzu, um mehrere Vorgänge gleichzeitig anstelle von jeweils einem durchzuführen

Eine Callback-Implementierung, die Ereignisse, die von der SortedList ausgelöst werden, im Batch benachrichtigen kann.

Diese Klasse kann nützlich sein, wenn Sie mehrere Vorgänge in einer SortedList ausführen, aber nicht jedes Ereignis einzeln auslösen möchten, was zu einem Leistungsproblem führen kann.

Wenn Sie beispielsweise einer SortedList mehrere Elemente hinzufügen möchten, konvertiert der BatchedCallback-Aufruf einzelne onInserted (index, 1) -Aufrufe in einen onInserted (index, N) -Aufruf, wenn Elemente aufeinanderfolgenden Indizes hinzugefügt werden. Diese Änderung kann dazu beitragen, dass RecyclerView Änderungen leichter auflöst.

Wenn aufeinanderfolgende Änderungen in der SortedList nicht für die Stapelverarbeitung geeignet sind, werden sie von BatchingCallback abgesetzt, sobald ein solcher Fall erkannt wird. Nachdem Sie die SortedList bearbeitet haben, müssen Sie immer dispatchLastEvent () aufrufen, um alle Änderungen am Callback zu löschen.

2
Axxiss