El método getView del adaptador de vista de lista personalizado se llama varias veces y sin un orden coherente

162

Tengo un adaptador de lista personalizado:

class ResultsListAdapter extends ArrayAdapter<RecordItem> {

en el método 'getView' anulado, hago una impresión para verificar qué posición es y si es convertView o no:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        System.out.println("getView " + position + " " + convertView);

El resultado de esto (cuando la lista se muestra por primera vez, todavía no hay entrada del usuario)

04-11 16:24:05.860: INFO/System.out(681): getView 0 null  
04-11 16:24:29.020: INFO/System.out(681): getView 1 android.widget.RelativeLayout@43d415d8  
04-11 16:25:48.070: INFO/System.out(681): getView 2 android.widget.RelativeLayout@43d415d8  
04-11 16:25:49.110: INFO/System.out(681): getView 3 android.widget.RelativeLayout@43d415d8  
04-11 16:25:49.710: INFO/System.out(681): getView 0 android.widget.RelativeLayout@43d415d8  
04-11 16:25:50.251: INFO/System.out(681): getView 1 null  
04-11 16:26:01.300: INFO/System.out(681): getView 2 null  
04-11 16:26:02.020: INFO/System.out(681): getView 3 null  
04-11 16:28:28.091: INFO/System.out(681): getView 0 null  
04-11 16:37:46.180: INFO/System.out(681): getView 1 android.widget.RelativeLayout@43cff8f0  
04-11 16:37:47.091: INFO/System.out(681): getView 2 android.widget.RelativeLayout@43cff8f0  
04-11 16:37:47.730: INFO/System.out(681): getView 3 android.widget.RelativeLayout@43cff8f0  

AFAIK, aunque no pude encontrarlo explícitamente, getView () solo se llama para filas visibles. Dado que mi aplicación comienza con cuatro filas visibles, al menos los números de posición que pasan de 0 a 3 tienen sentido. Pero el resto es un desastre:

  • ¿Por qué se llama a getview para cada fila tres veces?
  • ¿De dónde vienen estos convertViews cuando aún no me he desplazado?

Investigué un poco y, sin obtener una buena respuesta, noté que las personas asociaban este problema con problemas de diseño. Entonces, en caso, aquí está el diseño que contiene la lista:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="fill_parent"
    android:layout_width="fill_parent" 
    android:orientation="vertical" >

    <TextView android:id="@+id/pageDetails"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" />

    <ListView android:id="@+id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:drawSelectorOnTop="false" />

</LinearLayout>

y el diseño de cada fila individual:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="108dp"    
android:padding="4dp">

<ImageView
    android:id="@+id/thumb"        
    android:layout_width="120dp"
    android:layout_height="fill_parent"        
    android:layout_alignParentTop="true"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:layout_marginRight="8dp"        
    android:src="@drawable/loading" />

<TextView  
    android:id="@+id/price"
    android:layout_width="wrap_content"
    android:layout_height="18dp"         
    android:layout_toRightOf="@id/thumb"
    android:layout_alignParentBottom="true"       
    android:singleLine="true" />

<TextView  
    android:id="@+id/date"
    android:layout_width="wrap_content"
    android:layout_height="18dp"         
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true" 
    android:paddingRight="4dp"       
    android:singleLine="true" />

<TextView
    android:id="@+id/title"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textSize="17dp" 
    android:layout_toRightOf="@id/thumb"
    android:layout_alignParentRight="true"
    android:layout_alignParentTop="true"
    android:paddingRight="4dp"   
    android:layout_alignWithParentIfMissing="true"
    android:gravity="center" />

</RelativeLayout>

Gracias por tu tiempo

edzillion
fuente

Respuestas:

271

Esto no es un problema, no hay absolutamente ninguna garantía sobre el orden en que getView()se llamará ni cuántas veces. En su caso particular, está haciendo lo peor posible con a ListViewdándole a height=wrap_content. Esto obliga ListViewa medir algunos niños fuera del adaptador en el momento del diseño, para saber qué tan grande debería ser. Esto es lo que proporciona ListViewa la convertViewsque ves pasó a getView()incluso antes de que se desplaza.

Romain Guy
fuente
10
No me parece una buena razón. Está bien si quieres poder cambiar, pero actualmente no responde por qué está haciendo este comportamiento.
cdpnet
45
@cdpnet Dijo por qué lo hacía, ListViewtiene una altura de wrap_content, por lo que no está seguro de su altura, por lo tanto, presenta a algunos niños como una especie de probador para ver qué encaja. Cambie su ListViewpara fill_parentluego revisar sus registros las llamadas deberían reducirse drásticamente.
Blundell
44
El comportamiento entre 2.3 y 4.x era ListView ha cambiado significativamente. La conclusión es que, como ListView está diseñado, sus celdas / elementos_de_lista deben ser vistas puras y deben estar optimizados para dibujar rápidamente. De acuerdo con la charla de E / S de Google en ListView, donde presentó Romain, se podría llamar a getView solo para optimizar la representación de la vista y descartar el resultado. Si bien en mi opinión esto no es tan amigable para el desarrollador como iOS UITableView, es como es.
Cameron Lowell Palmer
44
Muchas gracias! No entendí por qué se llama a getView () con tanta frecuencia. Cambié wrap_content a fill_parent y ahora mi aplicación es rápida otra vez :)
Julia Hexen
28
Debería haber una advertencia al configurar ListViews en layout_height = wrap_content porque causa muchos problemas de rendimiento.
nathanielwolf
53

Pruebe con match_parentla layout_heightpropiedad de la vista de lista. Evitará getView()que lo llamen con tanta frecuencia.

Fred T
fuente
44
Tenga en cuenta que Fred T especifica el layout_height de la vista de lista ... no la fila, que es lo que he intentado una y otra vez
mblackwell8
8
Tenga en cuenta que fill_parentdebe reemplazarse match_parentpor el nivel de API 8 y superior.
Jason Axelson
Creo que el método getView () llamará varias veces, al establecer el ancho y la altura como match_parent para ListView solo resuelve el problema de rendimiento.
Cuong Vo
45

Me deshice de este problema cuando cambié tanto layout_width como layout_height por match_parent (cambiar solo layout_height no ayudó).


Nota útil tenga cuidado si tiene elementos anidados. Tienes que cambiar el "más alto" para emparejar_parent . Espero que ayude a alguien.

Eugene Chumak
fuente
una palabra amigo "increíble" pero no sé por qué wrap_content está creando un problema. Esto resolvió mi problema.
Android Killer
@AndroidKiller cuando lo usa wrap_content, entonces ListViewno sabe cuánto elemento de la lista hay para convertirse en el contenido de la Lista, es por eso que ListViewintenta crear tantas filas como cree que son adecuadas para mostrar, y cuando usa fill_parento match_parent, ListViewpiensa, está bien, mi altura es xy necesito un nnúmero de filas para mostrar.
Adil Soomro
Muchas gracias ! Con este comportamiento, las imágenes cambiaban aleatoriamente debido a múltiples llamadas ... Ahora esto está bien desde el principio.
Chostakovitch
7

No puedo responder a su pregunta "Por qué", pero definitivamente tengo una solución al problema del problema irritante " Repetición de elementos de ListView " (si tiene elementos en su colección que superan la altura de la pantalla).

Como muchas personas han mencionado anteriormente, mantener el androide: layout_height propiedad del ListVew etiqueta como fill_parent .

Y sobre la función getView (), la solución es usar una clase estática llamada ViewHolder . Mira este ejemplo. Realiza con éxito la tarea de agregar todos los elementos en su Array o ArrayCollection.

Espero que esto ayude a amigos !!

Saludos cordiales, Siddhant

Siddhant
fuente
Tenga cuidado al usar el patrón ViewHolder en la versión anterior de Android (pre ICS) porque puede llevarlo a la excepción MemoryLeak. siéntase libre de usarlo en las versiones ICS +.
Vahid Ghadiri
@Siddhant, gracias. Me paso un día para descubrir la razón de repetir imágenes en mi GridViewy cambiar android:layout_heighty android:layout_widthno funciona para mí. Pero usando ViewHolderimágenes repetidas fijas para mí, revisé
Nikolay Podolnyy
4

Ques: ¿Por qué Adaptador llama a getView () muchas veces? Respuesta: A medida que Listview se muestra en el desplazamiento, se actualiza su vista con las próximas vistas, para las cuales el adaptador necesita obtener vistas llamando a getView ().

Ques: ¿Por qué las llamadas son menores si el ancho y la altura de la vista de lista se establecen en fill_parent? Respuesta: Debido a que el inflador tiene el tamaño fijo para el área de pantalla para la lista, se calcula una vez para representar las vistas en la pantalla.

Espero que resuelva su consulta.

Gran Bretaña Sharma
fuente
Gran explicación Pero match_parent puede ser una mejor opción.
Fattie
Sí @JoeBlow, se recomienda usar match_parent en lugar de fill_parent desde API 2.4 / 3.0 en adelante
jitain sharma
4

Estaba teniendo el mismo problema con el menú desplegable en AutoCompleteTextView. Estuve luchando con el problema durante dos días hasta que llegué aquí y me muestras la solución.

Si escribo dropDownHeight = "match_parent", el problema se soluciona. Ahora el problema está relacionado con la interfaz de usuario (cuando tiene un elemento, el menú desplegable es demasiado grande), pero el problema de las llamadas múltiples (mucho más importante) está solucionado.

¡¡Gracias!!

Pau Arlandis Martinez
fuente
2

"¿Por qué se llama a getview para cada fila tres veces?" ¡Porque se llama a getView cuando se desplaza en la vista de lista y para decir mejor que cuando se cambia la posición de una vista de su lista!

Ungureanu Liviu
fuente
ok, pero la vista no ha cambiado. hay cuatro puntos de vista de filas que aparecen en pantalla cuando se inicia la actividad, y sin necesidad de desplazarse o cualquier entrada del usuario en todo lo que consigo tres llamadas getView para cada fila ...
edzillion
Esta respuesta es incorrecta. La respuesta correcta es que el uso de ** wrap_content ** provoca una gran cantidad de llamadas a getView.
Fattie
2

Tengo el mismo problema. Si tengo la altura establecida en fill_parent, entonces recibo "generalmente" 2 llamadas por fila. Pero, si configuro la altura de mi ListView al valor exacto, digamos a 300dp, entonces obtengo exactamente una llamada GetView por fila.

Entonces, me parece que la única forma es primero determinar la altura de la pantalla, luego establecer programáticamente la altura de listvilew en ese valor. No me gusta Espero que haya una mejor manera.

bobetko
fuente
1
Gracias por eso. Es curioso: esta pregunta ha estado inactiva durante meses solo para que aparezcan algunos comentarios en las últimas semanas. Ya no estoy haciendo este proyecto, pero como tengo algo de tiempo voy a intentarlo y luego volveré aquí para confirmar. gracias de nuevo.
edzillion
igual aquí, recibo dos llamadas por fila. Filas 0,1,2,3 y luego 100 ms después otras 0,1,2,3, lo cual es un fastidio cuando se recopilan estadísticas sobre qué filas se ven
Alguien en algún lugar
2

Para todos ustedes que todavía (Después de ajustar el heightde la ListViewa match_parent) está atascado (como yo):

También debe establecer el heightdiseño primario en match_parent.

Ver ejemplo a continuación. El LinearLayoutes el padre aquí:

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/something" />

        <ListView
            android:id="@+id/friendsList"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>
B001 ᛦ
fuente
1

Esto puede llegar tarde, pero si está utilizando layout_weightrecuerde configurar siemprelayout_width="0dp"

Kuti Gbolahan
fuente
0

Hice esta solución, tal vez no sea la mejor, pero funciona ...

//initialize control string
private String control_input = " ";

entonces =

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    View gridview = convertView;

    // change input_array for the array you use
    if (!control_input.equals(input_array[position])) {
        control_input = input_array[position];

        //do your magic

    } 

    return gridview;
}

¡Espero eso ayude!

yago
fuente