Quelle est l'intention des méthodes getItem et getItemId dans la classe Android BaseAdapter?

155

Je suis curieux de connaître le but des méthodes getItemet getItemIdde la classe Adapter dans le SDK Android.

D'après la description, il semble que getItemdevrait renvoyer les données sous-jacentes. Donc, si j'ai un tableau de noms ["cat","dog","red"]et que je crée un adaptateur à l' aaide de cela, alors a.getItem(1)devrait retourner "chien", correct? Que devrait a.getItemId(1)revenir?

Si vous avez utilisé ces méthodes dans la pratique, pourriez-vous donner un exemple?

oneporter
la source
16
+1 Excellente question. Je tiens à souligner que getItemId()dans ArrayAdapter()revient toujours -1avecassert false : "TODO"; return -1;
rds

Réponses:

86

Je vois ces méthodes comme une approche plus propre pour accéder aux données de ma liste. Au lieu d'accéder directement à mon objet adaptateur via quelque chose comme myListData.get(position)je peux simplement appeler l'adaptateur comme adapter.get(position).

Il en va de même getItemId. Habituellement, j'utiliserais cette méthode lorsque je souhaite exécuter une tâche en fonction de l'ID unique d'un objet dans la liste. Ceci est particulièrement utile lorsque vous travaillez avec une base de données. Le retourné idpourrait être une référence à un objet de la base de données sur lequel je pourrais alors effectuer différentes opérations (mise à jour / suppression / etc).

Donc, au lieu d'accéder à l'ID à partir de l'objet de données brutes comme myListData.get(position).getId()vous pouvez l'utiliser adapter.getItemId(position).

Un exemple où j'ai senti que j'avais besoin d'utiliser ces méthodes était dans un projet utilisant le SeparatedListViewAdapter . Cet adaptateur peut contenir plusieurs types différents d'adaptateurs, chacun représentant des données d'un type différent (généralement). Lors de l'appel getItem(position)sur le SeparatedListViewAdapter, l'objet retourné peut être différent selon la "section" dans laquelle vous l'envoyez.

Par exemple, si vous aviez 2 sections dans votre liste (fruits et bonbons): Si vous avez utilisé getItem(position)et que vous vous trouviez positionsur un article dans la section des fruits , vous recevriez un objet différent de celui que vous aviez demandé getItem(position)en positionpointant un article dans le bonbon section. Vous pouvez ensuite renvoyer une sorte de valeur d'ID constante dans getItemId(position)laquelle représente le type de données getItem(position)renvoyées, ou utiliser instanceofpour déterminer quel objet vous avez.

À part ce que j'ai mentionné, je n'ai jamais senti que j'avais vraiment besoin d'utiliser ces méthodes

James
la source
7
pour les adaptateurs non liés à SQL, getItemId aura-t-il toujours un but? si oui, que faut-il retourner? position?
développeur android
1
le but ou l'utilisation de la méthode incombe principalement au développeur et n'est pas lié à une application basée sur une base de données. utilisez-le à votre avantage pour créer du code clair / lisible / réutilisable.
james
1
Ouais je suppose. getView, getCount, getViewTypeCount, Etc sont utilisés spécifiquement pour montrer correctement votre interface utilisateur listview. les autres fonctions aident simplement à créer implémenter d'autres fonctionnalités telles que l'exécution d'actions supplémentaires en cliquant sur un élément, etc. bien que j'utilise souvent à l' getItemintérieurgetView
james
1
@NicolasZozol Bien sûr, il est prudent de ne pas l'implémenter getItemId, de simplement retourner 0Lou de nullne pas l'utiliser n'importe où. Je ne vois aucune raison évidente pour laquelle un UUID serait plus précieux qu'une simple longvaleur pour l'ID. Mode déconnecté? Qu'est-ce que c'est?
james
1
@binnyb: Nicolas voulait dire qu'avec les UUID, il est toujours possible de créer des identifiants uniques valides (par exemple sur votre appareil mobile), même sans avoir de connexion réseau.
Lévite
32

Eh bien, il semble que cette question pourrait trouver une réponse plus simple et plus directe ... :-)

En termes simples, Android vous permet de joindre un longà n'importe quel ListViewélément, c'est aussi simple que cela. Lorsque le système vous informe de la sélection de l'utilisateur, vous recevez trois variables d'identification pour vous indiquer ce qui a été sélectionné:

  • une référence à la vue elle-même,
  • sa position numérique dans la liste,
  • ce que longvous avez joint aux éléments individuels.

C'est à vous de décider lequel de ces trois est le plus facile à gérer dans votre cas particulier, mais vous avez le choix entre les trois à tout moment. Considérez cela longcomme une étiquette automatiquement attachée à l'élément, mais c'est encore plus simple et plus facile à lire.

Le malentendu sur ce qu'il fait habituellement découle d'une simple convention. Tous les adaptateurs doivent fournir un getItemId()même s'ils n'utilisent pas réellement cette troisième identification. Ainsi, par convention, ces adaptateurs (y compris de nombreux exemples dans le SDK ou partout sur le Web) reviennent simplement positionpour une seule raison: c'est toujours unique. Pourtant, si un adaptateur revient position, cela signifie vraiment qu'il ne veut pas du tout utiliser cette fonctionnalité, car elle positionest déjà connue, de toute façon.

Donc, si vous devez renvoyer une autre valeur que vous jugez appropriée, n'hésitez pas à le faire:

@Override
public long getItemId(int position) {
  return data.get(position).Id;
}
Gábor
la source
1
Belle explication pour cela getItemId()... Que se passe-t-il quand / si cette méthode n'est pas remplacée dans votre adaptateur personnalisé?
dentex
Étant marqué abstrait dans la classe de base, vous devez. Sauf si vous remplacez quelque chose qui remplace l'adaptateur d'origine, bien sûr. Essayez de le laisser de côté, et si Eclipse se plaint, vous devez le faire. :-)
Gábor
Merci. J'ai toujours fait commenter cette méthode sans avertissement. J'ai un CustomAdapter étend ArrayAdapter <CustomListItem> avec getCount (), getItem (...) et getView (...), en utilisant le "modèle de support". Juste par curiosité ...
dentex
Oui, vous pouvez le faire car ArrayAdapter étend BaseAdapter et fournit déjà sa propre implémentation.
Gábor
Et avec un tableau simple, c'est très bien. Mais considérez simplement un autre cas, lorsque vous souhaitez afficher des éléments d'une base de données, par exemple. Vous étendrez alors probablement BaseAdapter et vous pourrez utiliser cet ID long pour stocker la clé de base de données. Lorsque l'utilisateur sélectionne quelque chose, vous récupérez directement la clé de l'enregistrement sélectionné via l' argument id . Vous pouvez ensuite le charger immédiatement à partir de la base de données, par exemple. Seul problème que vous devez utiliser des touches numériques car Android a opté pour un long au lieu de quelque chose de plus large.
Gábor
6

La getItemIdméthode est en grande partie conçue pour fonctionner avec des curseurs qui sont sauvegardés par des bases de données SQLite. Il renverra le champ id du curseur sous-jacent pour l'élément en position 1.

Dans votre cas, il n'y a pas d'identifiant pour l'élément en position 1: je suppose que l'implémentation d'ArrayAdapter renvoie simplement -1 ou 0.

EDIT: en fait, il renvoie simplement la position: dans ce cas 1.

Femi
la source
2
Non, il revient -1. Voici l'implémentationassert false : "TODO"; return -1;
rds
5
À partir d'Android 4.1.1, il renvoie la position: grepcode.com/file/repository.grepcode.com/java/ext/…
emmby
4

Je tiens à mentionner qu'après la mise en œuvre getItem, getItemIdvous pouvez utiliser ListView.getItemAtPosition et ListView.getItemIdAtPosition pour accéder directement à vos données, au lieu de passer par l'adaptateur. Cela peut être particulièrement utile lors de l'implémentation d'un écouteur onClick.

leo9r
la source
3
Ceci est en effet extrêmement utile si vous avez un en-tête sur votre liste et que les positions passées au gestionnaire de clics sont décalées d'une
unité
4

Si vous implémentez getItemIdcorrectement, cela peut être très utile.

Exemple :

Vous avez une liste d'albums:

class Album{
     String coverUrl;
     String title;
}

Et vous implémentez getItemIdcomme ceci:

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

Maintenant, votre identifiant d'élément dépend des valeurs des champs coverUrl et title et si vous changez alors et appelez notifyDataSetChanged()votre adaptateur, alors l'adaptateur appellera la méthode getItemId () de chaque élément et mettra à jour uniquement les éléments dont l'ID a changé.

Ceci est très utile si vous effectuez des opérations "lourdes" dans votre fichier getView().

BTW: si vous voulez que cela fonctionne, vous devez vous assurer que votre hasStableIds()méthode renvoie false;

Danylo Volokh
la source
C'est une observation précieuse, pouvez-vous fournir des données pour étayer ce mécanisme de mise à jour sélective?
Jaime Agudo
Pourquoi hasStableIds()retourner faux? Il me semble que le hashcode calculé à partir de la même chaîne renverrait la même valeur à chaque fois, ce qui est un ID stable selon la documentation .
Big McLargeHuge
considérez que l'utilisation de hashCode peut retourner true à deux chaînes
htafoya
2

getItemou getItemIdsont quelques méthodes principalement conçues pour joindre des données avec des éléments dans la liste. Dans le cas de getItem, vous pouvez transmettre tout objet qui s'attachera à l'élément dans la liste. Normalement, les gens reviennent null. getItemIdest toute longvaleur unique que vous pouvez attacher avec le même élément dans la liste. Les gens retournent généralement la position dans la liste.

Quel en est l'usage. Eh bien, comme ces valeurs sont liées à l'élément dans la liste, vous pouvez les extraire lorsque l'utilisateur clique sur l'élément. Ces valeurs sont accessibles par des AdapterViewméthodes.

// 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
la source