Was ist die Absicht der Methoden getItem und getItemId in der Android-Klasse BaseAdapter?

155

Ich bin gespannt auf den Zweck der Methoden getItemund getItemIdauf die Klasse Adapter im Android SDK.

Aus der Beschreibung geht hervor, dass getItemdie zugrunde liegenden Daten zurückgegeben werden sollten. Wenn ich also eine Reihe von Namen habe ["cat","dog","red"]und damit einen Adapter erstelle a, a.getItem(1)sollte dann "dog" zurückgegeben werden, richtig? Was soll a.getItemId(1)zurückkehren?

Wenn Sie diese Methoden in der Praxis angewendet haben, können Sie ein Beispiel geben?

oneporter
quelle
16
+1 Ausgezeichnete Frage. Ich möchte darauf hinweisen, dass getItemId()in ArrayAdapter()immer -1mitassert false : "TODO"; return -1;
rds

Antworten:

86

Ich sehe diese Methoden als einen saubereren Ansatz für den Zugriff auf die Daten meiner Liste. Anstatt über so etwas wie direkt auf mein Adapterobjekt zuzugreifen, kann ich den Adapter myListData.get(position)einfach so nennen adapter.get(position).

Das gilt auch für getItemId. Normalerweise würde ich diese Methode verwenden, wenn ich eine Aufgabe basierend auf der eindeutigen ID eines Objekts in der Liste ausführen möchte. Dies ist besonders nützlich, wenn Sie mit einer Datenbank arbeiten. Die Rückgabe idkönnte ein Verweis auf ein Objekt in der Datenbank sein, an dem ich dann verschiedene Operationen ausführen könnte (Aktualisieren / Löschen / usw.).

Anstatt also vom Rohdatenobjekt aus auf die ID zuzugreifen, wie myListData.get(position).getId()Sie es verwenden können adapter.getItemId(position).

Ein Beispiel dafür, wo ich das Gefühl hatte, diese Methoden verwenden zu müssen, war ein Projekt mit dem SeparatedListViewAdapter . Dieser Adapter kann mehrere verschiedene Arten von Adaptern enthalten, die jeweils Daten eines anderen Typs darstellen (normalerweise). Beim Aufrufen getItem(position)von SeparatedListViewAdapterkann das zurückgegebene Objekt unterschiedlich sein, je nachdem, in welchem ​​"Abschnitt" Sie es senden.

Zum Beispiel, wenn Sie hatte zwei Abschnitte in der Liste (Obst und Süßigkeiten): Wenn Sie verwendet getItem(position)und positionpassiert auf ein Element in der sein Obst Abschnitt, würden Sie ein anderes Objekt erhalten , als wenn Sie aufgefordert , getItem(position)mit positionHinweis auf ein Element in der Süßigkeit Sektion. Sie können dann einen konstanten ID-Wert zurückgeben, getItemId(position)der angibt, welche Art von Daten getItem(position)zurückgegeben werden, oder verwenden instanceof, um zu bestimmen, über welches Objekt Sie verfügen.

Abgesehen von dem, was ich erwähnt habe, hatte ich nie das Gefühl, dass ich diese Methoden wirklich anwenden musste

James
quelle
7
Wird getItemId für nicht SQL-bezogene Adapter noch einen Zweck haben? Wenn ja, was sollte zurückgegeben werden? Position?
Android-Entwickler
1
Der Zweck oder die Verwendung der Methode liegt hauptsächlich beim Entwickler und ist nicht an eine datenbankgesteuerte App gebunden. Nutzen Sie es zu Ihrem Vorteil, um klaren / lesbaren / wiederverwendbaren Code zu erstellen.
James
1
Ja ich glaube. getView, getCount, getViewTypeCountIst, usw. speziell für richtig, um Ihr Listview - Benutzeroberfläche verwendet. Die anderen Funktionen helfen einfach dabei, andere Funktionen zu implementieren, z. B. das Ausführen weiterer Aktionen beim Klicken auf ein Element usw., obwohl ich häufig getItemInsidegetView
James
1
@NicolasZozol Sicher - es ist sicher, es nicht zu implementieren getItemId, einfach zurückzugeben 0Loder nulles nirgendwo zu verwenden. Ich sehe keinen offensichtlichen Grund, warum eine UUID wertvoller wäre als nur ein longWert für die ID. Getrennter Modus? Was ist das?
James
1
@binnyb: Nicolas meinte, dass es mit UUIDs auch ohne Netzwerkverbindung möglich ist, gültige eindeutige IDs (z. B. auf Ihrem Mobilgerät) zu erstellen.
Levite
32

Nun, es scheint, dass diese Frage einfacher und unkomplizierter beantwortet werden könnte ... :-)

Einfach ausgedrückt, mit Android können Sie longjedem ListViewArtikel einen anhängen , so einfach ist das. Wenn das System Sie über die Benutzerauswahl benachrichtigt, erhalten Sie drei identifizierende Variablen, die Ihnen mitteilen, was ausgewählt wurde:

  • ein Verweis auf die Ansicht selbst,
  • seine numerische Position in der Liste,
  • Dies haben longSie an die einzelnen Elemente angehängt.

Es liegt an Ihnen, zu entscheiden, welche dieser drei für Sie in Ihrem speziellen Fall am einfachsten zu handhaben ist, aber Sie haben immer alle drei zur Auswahl. Stellen Sie sich dies longals ein Tag vor, das automatisch an das Element angehängt wird, nur dass es noch einfacher und leichter auszulesen ist.

Das Missverständnis darüber, was es normalerweise tut, beruht auf einer einfachen Konvention. Alle Adapter müssen eine getItemId()auch dann angeben, wenn sie diese dritte Identifikation nicht tatsächlich verwenden. Konventionell kehren diese Adapter (einschließlich vieler in Beispielen im SDK oder im gesamten Web) einfach positionaus einem einzigen Grund zurück: Sie sind immer einzigartig. Wenn ein Adapter zurückkehrt position, bedeutet dies jedoch, dass er diese Funktion überhaupt nicht verwenden möchte, da dies positionohnehin bereits bekannt ist.

Wenn Sie also einen anderen Wert zurückgeben müssen, den Sie für richtig halten, können Sie dies tun:

@Override
public long getItemId(int position) {
  return data.get(position).Id;
}
Gábor
quelle
1
Gute Erklärung dafür getItemId()... Was passiert, wenn diese Methode in Ihrem benutzerdefinierten Adapter nicht überschrieben wird?
Dentex
Um in der Basisklasse als abstrakt markiert zu sein, muss man. Es sei denn, Sie überschreiben etwas, das den Originaladapter überschreibt. Versuchen Sie, es wegzulassen, und wenn sich Eclipse beschwert, müssen Sie es tun. :-)
Gábor
Vielen Dank. Ich habe diese Methode immer ohne Warnungen auskommentieren lassen. Ich habe einen CustomAdapter, der ArrayAdapter <CustomListItem> mit getCount (), getItem (...) und getView (...) unter Verwendung des "Haltermusters" erweitert. Nur aus Neugier ...
Dentex
Yep, können Sie das tun, weil ArrayAdapter erstreckt BaseAdapter und bereits eine eigene Implementierung zur Verfügung stellt.
Gábor
Und mit einem einfachen Array ist das in Ordnung. Betrachten Sie jedoch einen anderen Fall, wenn Sie beispielsweise Elemente aus einer Datenbank anzeigen möchten. Sie werden dann wahrscheinlich BaseAdapter erweitern und können diese lange ID zum Speichern des Datenbankschlüssels verwenden. Wenn der Benutzer etwas auswählt, erhalten Sie den Schlüssel des ausgewählten Datensatzes direkt über das Argument id zurück . Sie können es dann beispielsweise sofort aus der Datenbank laden. Einziges Problem, dass Sie Zifferntasten verwenden müssen, weil Android sich für eine lange statt für etwas breiteres entschieden hat.
Gábor
6

Die getItemIdMethode ist weitgehend für die Verwendung mit Cursorn ausgelegt, die von SQLite-Datenbanken unterstützt werden. Das ID-Feld des zugrunde liegenden Cursors für das Element an Position 1 wird zurückgegeben.

In Ihrem Fall gibt es keine ID für das Element an Position 1: Ich gehe davon aus, dass die Implementierung von ArrayAdapter nur -1 oder 0 zurückgibt.

EDIT: Eigentlich gibt es nur die Position zurück: in diesem Fall 1.

Femi
quelle
2
Nein, es kehrt zurück -1. Hier ist die Implementierungassert false : "TODO"; return -1;
29.
5
Ab Android 4.1.1 wird die folgende Position zurückgegeben: grepcode.com/file/repository.grepcode.com/java/ext/…
emmby
4

Ich möchte erwähnen , dass nach der Umsetzung getItemund getItemIdSie verwenden können ListView.getItemAtPosition und ListView.getItemIdAtPosition direkt Sie auf Daten zugreifen, statt durch den Adapter gehen. Dies kann besonders nützlich sein, wenn Sie einen onClick-Listener implementieren.

leo9r
quelle
3
Dies ist in der Tat sehr nützlich , wenn Sie einen Header auf dem Listview haben und die Positionen auf den Klick - Handler übergeben von einem aus sind
Entropie
4

Wenn Sie getItemIdrichtig implementieren , kann es sehr nützlich sein.

Beispiel:

Sie haben eine Liste von Alben:

class Album{
     String coverUrl;
     String title;
}

Und Sie implementieren getItemIdwie folgt:

@Override
public long getItemId(int position){
    Album album = mListOfAlbums.get(position);
    return (album.coverUrl + album.title).hashcode();
}

Jetzt hängt Ihre Element-ID von den Werten der Felder coverUrl und title ab. Wenn Sie diese ändern und notifyDataSetChanged()Ihren Adapter aufrufen, ruft der Adapter die Methode getItemId () jedes Elements auf und aktualisiert nur die Elemente, deren ID sich geändert hat.

Dies ist sehr nützlich, wenn Sie einige "schwere" Operationen in Ihrem ausführen getView().

Übrigens: Wenn dies funktionieren soll, müssen Sie sicherstellen, dass Ihre hasStableIds()Methode false zurückgibt.

Danylo Volokh
quelle
Dies ist eine wertvolle Beobachtung. Können Sie einige Daten bereitstellen, um diesen selektiven Aktualisierungsmechanismus zu unterstützen?
Jaime Agudo
Warum sollte hasStableIds()false zurückgegeben werden? Es scheint mir, dass der aus derselben Zeichenfolge berechnete Hashcode jedes Mal denselben Wert zurückgeben würde, was laut den Dokumenten eine stabile ID ist .
Big McLargeHuge
Bedenken Sie, dass die Verwendung von
hashCode
1

getItemoder es getItemIdgibt nur wenige Methoden, die hauptsächlich zum Anhängen von Daten an Elemente in der Liste entwickelt wurden. In diesem Fall getItemkönnen Sie jedes Objekt übergeben, das an das Element in der Liste angehängt wird. Normalerweise kehren die Leute zurück null. getItemIdist ein eindeutiger longWert, den Sie mit demselben Element in der Liste verknüpfen können. Personen geben im Allgemeinen die Position in der Liste zurück.

Was ist der Nutzen. Da diese Werte an das Element in der Liste gebunden sind, können Sie sie extrahieren, wenn der Benutzer auf das Element klickt. Auf diese Werte kann über AdapterViewMethoden zugegriffen werden.

// template class to create list item objects
class MyListItem{
    public String name;
    public long dbId;

    public MyListItem(String name, long dbId){
        this.name = name;
        this.dbId = dbId;
    }
}

///////////////////////////////////////////////////////////

// create ArrayList of MyListItem
ArrayList<MyListItem> myListItems = new ArrayList<MyListItem>(10);

// override BaseAdapter methods
@Override
public Object getItem(int position) {
    // return actual object <MyListItem>
    // which will be available with item in ListView
    return myListItems.get(position);
}

@Override
public long getItemId(int position) {
    // return id of database document object
    return myListItems.get(position).dbId;
}

///////////////////////////////////////////////////////////

// on list item click, get name and database document id
my_list_view.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

        // extract item data
        MyListItem selectedItem = (MyListItem)parent.getItemAtPosition(position);      
        System.out.println("Your name is : " + selectedItem.name);

        // extract database ref id
        long dbId = id;

        // or you could also use
        long dbId = parent.getItemIdAtPosition(position);
    }
});
Uday Hiwarale
quelle