¿Cuál es la intención de los métodos getItem y getItemId en la clase de Android BaseAdapter?

155

Tengo curiosidad sobre el propósito de los métodos getItemy getItemIden la clase Adaptador en el SDK de Android.

De la descripción, parece que getItemdebería devolver los datos subyacentes. Entonces, si tengo una serie de nombres ["cat","dog","red"]y creo un adaptador acon eso, entonces a.getItem(1)debería devolver "dog", ¿correcto? ¿Qué debería a.getItemId(1)volver?

Si ha utilizado estos métodos en la práctica, ¿podría dar un ejemplo?

oneporter
fuente
16
+1 Excelente pregunta. Quiero señalar que getItemId()en ArrayAdapter()vuelve siempre -1conassert false : "TODO"; return -1;
RDS

Respuestas:

86

Veo estos métodos como un enfoque más limpio para acceder a los datos de mi lista. En lugar de acceder directamente a mi objeto adaptador a través de algo como myListData.get(position), simplemente puedo llamar al adaptador como adapter.get(position).

Lo mismo vale para getItemId. Por lo general, usaría este método cuando quiero ejecutar alguna tarea basada en la ID única de un objeto en la lista. Esto es especialmente útil cuando se trabaja con una base de datos. El devuelto idpodría ser una referencia a un objeto en la base de datos en el que luego podría realizar diferentes operaciones (actualizar / eliminar / etc.).

Entonces, en lugar de acceder a la ID desde el objeto de datos sin procesar como myListData.get(position).getId()puede usar adapter.getItemId(position).

Un ejemplo de donde sentí que necesitaba usar estos métodos fue en un proyecto usando SeparatedListViewAdapter . Este adaptador puede contener múltiples tipos diferentes de adaptadores, cada uno representando datos de un tipo diferente (típicamente). Al llamar getItem(position)al SeparatedListViewAdapter, el objeto devuelto puede ser diferente dependiendo de qué "sección" es la posición en la que lo envía.

Por ejemplo, si usted tenía 2 secciones de la lista (frutas y dulces): Si ha utilizado getItem(position)y positionpasó a ser en un elemento de la fruta de la sección, que recibiría un objeto diferente que si usted solicitó getItem(position)con positionque apunta a un elemento en el caramelo sección. Luego, puede devolver algún tipo de valor de ID constante en el getItemId(position)que represente qué tipo de datos getItem(position)están regresando, o usar instanceofpara determinar qué objeto tiene.

Aparte de lo que he mencionado, nunca sentí que realmente necesitaba usar estos métodos

James
fuente
77
para adaptadores no relacionados con SQL, ¿getItemId todavía tendrá un propósito? Si es así, ¿qué se debe devolver? ¿posición?
Desarrollador de Android
1
El propósito o uso del método depende principalmente del desarrollador y no está vinculado a una aplicación basada en bases de datos. úselo a su favor para crear código claro / legible / reutilizable.
james
1
Yeah Yo supongo. getView, getCount, getViewTypeCount, Etc se utilizan específicamente para mostrar correctamente la interfaz de usuario de lista. las otras funciones simplemente ayudan a crear, implementar otras funcionalidades, como realizar acciones adicionales al hacer clic en un elemento, etc., aunque a menudo lo uso getItemdentrogetView
james
1
@NicolasZozol Claro, es seguro no implementarlo getItemId, solo regrese 0Lo nullno lo use en ningún lado. No veo ninguna razón obvia por la que un UUID sea más valioso que solo un longvalor para la ID. Modo desconectado? ¿Que es eso?
James
1
@binnyb: Nicolas quiso decir que con UUID, todavía es posible crear identificaciones únicas válidas (por ejemplo, en su dispositivo móvil), incluso sin tener una conexión de red.
Levite
32

Bueno, parece que esta pregunta podría responderse de una manera más simple y directa ... :-)

En pocas palabras, Android le permite adjuntar un longa cualquier ListViewelemento, es así de simple. Cuando el sistema le notifica la selección del usuario, recibe tres variables de identificación para decirle qué se seleccionó:

  • una referencia a la vista en sí,
  • su posición numérica en la lista,
  • esto longlo adjuntaste a los elementos individuales.

Depende de usted decidir cuál de estos tres es el más fácil de manejar en su caso particular, pero tiene los tres para elegir todo el tiempo. Piense en esto longcomo una etiqueta adjunta automáticamente al elemento, solo que es aún más simple y fácil de leer.

El malentendido sobre lo que suele hacer se deriva de una simple convención. Todos los adaptadores deben proporcionar getItemId()incluso si realmente no usan esta tercera identificación. Entonces, por convención, esos adaptadores (incluidos muchos en muestras en el SDK o en toda la web) simplemente regresan positionpor una sola razón: siempre es única. Aún así, si un adaptador regresa position, esto realmente significa que no quiere usar esta característica, ya que de positiontodos modos ya se sabe.

Por lo tanto, si necesita devolver cualquier otro valor que le parezca, no dude en hacerlo:

@Override
public long getItemId(int position) {
  return data.get(position).Id;
}
Gábor
fuente
1
Buena explicación para esto getItemId()... ¿Qué sucede cuando / si este método no se anula en su adaptador personalizado?
dentex
Al estar marcado como abstracto en la clase base, tienes que hacerlo. A menos que anule algo que anule el adaptador original, por supuesto. Intenta dejarlo afuera, y si Eclipse se queja, entonces tienes que hacerlo. :-)
Gábor
Gracias. Siempre he comentado este método sin advertencias. Tengo un CustomAdapter que extiende ArrayAdapter <CustomListItem> con getCount (), getItem (...) y getView (...), utilizando el "patrón de soporte". Solo por curiosidad ...
dentex
Sí, puede hacerlo porque ArrayAdapter extiende BaseAdapter y ya proporciona su propia implementación.
Gábor
Y con una matriz simple, está bien. Pero considere otro caso, por ejemplo, cuando desee mostrar elementos de una base de datos. Probablemente extenderá BaseAdapter y podrá usar esta identificación larga para almacenar la clave de la base de datos. Cuando el usuario selecciona algo, obtendrá directamente la clave del registro seleccionado a través del argumento id . Luego puede cargarlo desde la base de datos de inmediato, por ejemplo. El único problema es que tienes que usar teclas numéricas porque Android decidió una opción larga en lugar de algo más amplio.
Gábor
6

El getItemIdmétodo está diseñado en gran medida para trabajar con cursores respaldados por bases de datos SQLite. Devolverá el campo de identificación del cursor subyacente para el elemento en la posición 1.

En su caso, no hay una identificación para el elemento en la posición 1: supongo que la implementación de ArrayAdapter solo devuelve -1 o 0.

EDITAR: en realidad, solo devuelve la posición: en este caso 1.

Femi
fuente
2
No, vuelve -1. Aquí está la implementaciónassert false : "TODO"; return -1;
rds
55
A partir de Android 4.1.1, devuelve la posición: grepcode.com/file/repository.grepcode.com/java/ext/…
emmby
4

Me gustaría mencionar que después de implementar getItemy getItemIdque puedo utilizar ListView.getItemAtPosition y ListView.getItemIdAtPosition para acceder directamente a sus datos, en lugar de ir a través del adaptador. Esto puede ser particularmente útil al implementar un oyente onClick.

leo9r
fuente
3
De hecho, esto es extremadamente útil si tiene un encabezado en su vista de lista y las posiciones pasadas al controlador de clics están desactivadas por uno
entropía
4

Si implementa getItemIdcorrectamente, puede ser muy útil.

Ejemplo:

Tienes una lista de álbumes:

class Album{
     String coverUrl;
     String title;
}

Y lo implementas getItemIdasí:

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

Ahora su ID de elemento depende de los valores de los campos coverUrl y title y si cambia luego y llama notifyDataSetChanged()a su adaptador, entonces el adaptador llamará al método getItemId () de cada elemento y actualizará solo los elementos cuyo ID ha cambiado.

Esto es muy útil si está haciendo algunas operaciones "pesadas" en su getView().

Por cierto: si desea que esto funcione, debe asegurarse de que su hasStableIds()método devuelva falso;

Danylo Volokh
fuente
Esta es una observación valiosa, ¿puede proporcionar algunos datos para respaldar este mecanismo de actualización selectiva?
Jaime Agudo
¿Por qué debería hasStableIds()devolver falso? Me parece que el código hash calculado a partir de la misma cadena devolvería el mismo valor cada vez, que es una ID estable según los documentos .
Big McLargeHuge
tenga en cuenta que el uso de hashCode puede devolver verdadero a dos cadenas
htafoya
2

getItemo getItemIdson pocos métodos diseñados principalmente para adjuntar datos con elementos en la lista. En caso de getItem, puede pasar cualquier objeto que se adjuntará al elemento en la lista. Normalmente la gente regresa null. getItemIdes cualquier longvalor único que puede adjuntar con el mismo elemento en la lista. Las personas generalmente devuelven el puesto en la lista.

Cual es el uso. Bueno, como estos valores están vinculados al elemento de la lista, puede extraerlos cuando el usuario hace clic en el elemento. Estos valores son accesibles a través de AdapterViewmétodos.

// 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
fuente