Cómo funciona el mecanismo de reciclaje de ListView

146

Entonces tengo este problema que tenía antes, y naturalmente pedí ayuda aquí . La respuesta de Luksprog fue excelente porque no tenía idea de cómo ListView y GridView se optimizaban con vistas de reciclaje. Entonces, con su consejo, pude cambiar la forma en que agregué Vistas a mi GridView. El problema es que ahora tengo algo que no tiene sentido. Este es mi getViewde mi BaseAdapter:


public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.day_view_item, parent, false);
        }
        Log.d("DayViewActivity", "Position is: "+position);
        ((TextView)convertView.findViewById(R.id.day_hour_side)).setText(array[position]);
        LinearLayout layout = (LinearLayout)convertView.findViewById(R.id.day_event_layout);

        //layout.addView(new EventFrame(parent.getContext()));

        TextView create = new TextView(DayViewActivity.this);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 62, getResources().getDisplayMetrics()), 1.0f);
        params.topMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        params.bottomMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        create.setLayoutParams(params);
        create.setBackgroundColor(Color.BLUE);
        create.setText("Test"); 
        //the following is my original LinearLayout.LayoutParams for correctly setting the TextView Height
        //new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60, getResources().getDisplayMetrics()), 1.0f)   
        if(position == 0) {
            Log.d("DayViewActivity", "This should only be running when position is 0. The position is: "+position);
            layout.addView(create);
        }

        return convertView;
    }

}

El problema es cuando me desplazo, esto sucede, y no en la posición 0 ... Parece la posición 6 y la posición 8, además pone dos en la posición 8. Ahora todavía estoy tratando de acostumbrarme a usar ListView y GridView, así que lo hago No entiendo por qué está sucediendo esto. Una de las principales razones por las que estoy haciendo esta pregunta es para ayudar a otros que probablemente no saben sobre ListView y la Vista de reciclaje de GridView, o la forma en que este artículo lo expresa, el mecanismo ScrapView.

ingrese la descripción de la imagen aquí

Editar más tarde

Agregar un enlace a una charla de Google IO que es básicamente todo lo que necesita para comprender cómo funciona ListView. Link estaba muerto en uno de los comentarios. Entonces user3427079 fue lo suficientemente agradable como para actualizar ese enlace. Aquí es de fácil acceso.

Andy
fuente
Jaja, gracias por ese enlace. Comenzó a verlo ahora :)
Andy
No implementó completamente el ejemplo de código de la pregunta anterior. La idea es que siempre debe tener un código para revertir los cambios que realizó en otras filas (en su caso, la posición 0).
Luksprog
Bueno, entiendo que @Luksprog ¿Pero eso explica por qué también está poniendo la Vista en las otras posiciones? En todo caso, esperaba que solo la posición 0 tuviera vistas duplicadas. Idk, todavía tengo problemas para entender cómo ListView está vinculando estas cosas: /
Andy
1
Reciclar significa que la vista de fila que acaba de desaparecer (por ejemplo, la posición 0, después de desplazarse una fila hacia abajo) de la pantalla se puede usar más tarde cuando ListViewnecesite una nueva fila para mostrar (como continuar desplazándose hacia abajo / arriba). El problema es que la vista que acaba de desaparecer y que se utilizará en las futuras posiciones necesarias TextViewya se ha agregado, este es el problema. Para resolverlo, debe eliminarlo en el getViewmétodo si getViewse llama al método para cualquier otra posición que no sea 0.
Luksprog
2
El enlace de la publicación superior está muerto, creo que es así. youtube.com/watch?v=N6YdwzAvwOA
G_V

Respuestas:

330

Inicialmente, tampoco tenía conocimiento del reciclaje de la vista de lista y el mecanismo de uso de conversión, pero después de una investigación de días enteros, entiendo bastante los mecanismos de la vista de lista al referirme a una imagen de android.amberfog ingrese la descripción de la imagen aquí

Cada vez que su vista de lista se llena con un adaptador, básicamente muestra el número de filas que la vista de lista puede mostrar en la pantalla y el número de filas no aumenta incluso cuando se desplaza por la lista. Este es el truco que usa Android para que la vista de lista funcione de manera más eficiente y rápida. Ahora, la historia interna de la vista de lista que se refiere a la imagen, como puede ver, inicialmente la vista de lista tiene 7 elementos visibles, luego, si se desplaza hacia arriba hasta que el elemento 1 ya no sea visible, getView () pasa esta vista (es decir, elemento1) a reciclador y puedes usar

System.out.println("getview:"+position+" "+convertView);

dentro de tu

public View getView(final int position, View convertView, ViewGroup parent)
{
    System.out.println("getview:"+position+" "+convertView);
    ViewHolder holder;
    View row=convertView;
    if(row==null)
    {
        LayoutInflater inflater=((Activity)context).getLayoutInflater();
        row=inflater.inflate(layoutResourceId, parent,false);
        
        holder=new PakistaniDrama();
        holder.tvDramaName=(TextView)row.findViewById(R.id.dramaName);
        holder.cbCheck=(CheckBox)row.findViewById(R.id.checkBox);
        
        row.setTag(holder);
        
    }
    else
    {
        holder=(PakistaniDrama)row.getTag();
    }
            holder.tvDramaName.setText(dramaList.get(position).getDramaName());
    holder.cbCheck.setChecked(checks.get(position));
            return row;
    }

Notará en su logcat, inicialmente, convertview es nulo para todas las filas visibles, porque inicialmente no había vistas (es decir, elementos) en el reciclador, por lo que getView () crea una nueva vista para cada uno de los elementos visibles, pero el en el momento en que se desplaza hacia arriba y el elemento 1 se mueve fuera de la pantalla, se enviará al Reciclador con su estado actual (por ejemplo, el 'texto' de TextView o, en el caso mío, si la casilla de verificación está marcada, se asociará con la vista y almacenado en reciclador).

Ahora, cuando se desplaza hacia arriba / abajo, su vista de lista no creará una nueva vista, utilizará la vista que está en su reciclador. En su Logcat notará que el 'convertView' no es nulo, es porque su nuevo elemento 8 se dibujará utilizando convertview, es decir, básicamente toma la vista del elemento 1 del reciclador e infla el elemento 8 en su lugar, y puede observar eso en mi código. Si tenía una casilla de verificación y la marca en la posición 0 (digamos que el elemento 1 tenía una casilla de verificación y la marcó), de modo que cuando se desplace hacia abajo verá la casilla de verificación del elemento 8 ya marcada, esta es la razón por la que la vista de lista vuelve a utilizar la misma vista, no crea una nueva para usted debido a la optimización del rendimiento.

Cosas importantes

1 . Nunca fije la layout_heighty layout_widthde la vista de lista para wrap_contentque getView()obligará a su adaptador para obtener algún niño para medir la altura de los puntos de vista que se elaborará en la vista de lista y puede causar un comportamiento inesperado como volver convertview aunque la lista no es scrolled.always utilizan match_parento fijos ancho / alto.

2 . Si desea utilizar un diseño o una vista después de la vista de lista, puede que le surja una pregunta si configuro la vista después de layout_heightque fill_parentla vista de lista no se muestre a medida que avanza por la pantalla, por lo que es mejor colocar su vista de lista dentro de un Por ejemplo, Diseño lineal y establezca la altura y el ancho de ese diseño según sus requisitos y haga que el atributo de alto y ancho de su vista de lista sea como su diseño (como si su ancho de diseño es 320 y la altura es 280), entonces su vista de lista debe tener la misma altura y ancho. Esto le indicará a getView () la altura y el ancho exactos de las vistas que se representarán, y getView () no llamará una y otra vez algunas filas aleatorias, y otros problemas como devolver la vista de conversión incluso antes de que el desplazamiento no ocurra, tengo prueba esto mismo, a menos que mi vista de lista estuviera dentro de lineaLayout, también tenía problemas como repetir la llamada de vista y convertir la vista, ya que poner Listview dentro de LinearLayout funcionó como magia para mí (no sabía por qué)

01-01 14:49:36.606: I/System.out(13871): getview 0 null
01-01 14:49:36.636: I/System.out(13871): getview 0 android.widget.RelativeLayout@406082c0
01-01 14:49:36.636: I/System.out(13871): getview 1 android.widget.RelativeLayout@406082c0
01-01 14:49:36.646: I/System.out(13871): getview 2 android.widget.RelativeLayout@406082c0
01-01 14:49:36.646: I/System.out(13871): getview 3 android.widget.RelativeLayout@406082c0
01-01 14:49:36.656: I/System.out(13871): getview 4 android.widget.RelativeLayout@406082c0
01-01 14:49:36.666: I/System.out(13871): getview 5 android.widget.RelativeLayout@406082c0
01-01 14:49:36.666: I/System.out(13871): getview 0 android.widget.RelativeLayout@406082c0
01-01 14:49:36.696: I/System.out(13871): getview 0 android.widget.RelativeLayout@406082c0
01-01 14:49:36.706: I/System.out(13871): getview 1 null
01-01 14:49:36.736: I/System.out(13871): getview 2 null
01-01 14:49:36.756: I/System.out(13871): getview 3 null
01-01 14:49:36.776: I/System.out(13871): getview 4 null

Pero ahora está resuelto, lo sé, no soy tan bueno para explicar, pero como puse todo mi día en entender, pensé que otros principiantes como yo pueden obtener ayuda de mi experiencia y espero que ahora ustedes tengan un poco de comprensión. de ListView framework cómo funciona, ya que es realmente desordenado y complicado, por lo que los principiantes encontraron demasiados problemas para entenderlo

Muhammad Babar
fuente
36
"Sé que no soy tan bueno para explicar" >>> Los +37 votos en esta respuesta me dicen que hiciste un excelente trabajo al explicar. ¡Por favor, sigue compartiendo tus conocimientos! ;)
2Dee
1
Gran respuesta raja ji +1 de mí .. :)
Ehsan Sajjad
2
Gracias por la imagen. Después de tener muchos problemas con ListView cuando comencé con Android, ya conocía el principio de reciclaje después de luchar tanto con él. Aún así, tomó esta foto y la oración: if you had a checkbox and if you check it at position 0(let say item1 has also a checkbox and you checked it) so when you scroll down you will see item 8 checkbox is already checked,this is why listview is re using the same view not creating a new for you due to performance optimization.para que me diera cuenta de mi error. ¡La mejor publicación sobre el reciclaje de Android ListView que encontré!
Kevin Cruijssen
1
@MuhammadBabar Sí, he publicado Pregunta: stackoverflow.com/q/30122027/1318946
Pratik Butani
1
@MuhammadBabar increíble explicación hombre! Me ahorra tiempo Solo quiero saber, ¿ RecyclerViewfunciona también en el mismo mecanismo? En mi pregunta stackoverflow.com/questions/47897770/… , sé que la tarea es silenciosa imposible listView. ¿Debo probar con recyclerView?
Shambhu
8

Tenga cuidado, en el patrón Titular, si establece la posición en su objeto Titular, debe establecerla cada vez, por ejemplo:

@Override
public final View getView(int position, View view, ViewGroup parent) {
    Holder holder = null;
    if (view == null) {
        LayoutInflater inflater = (LayoutInflater) App.getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(getContainerView(), parent, false);
        holder = getHolder(position, view, parent);
        holder.setTag(tag);
        view.setTag(holder);
    } else {
        holder = (Holder) view.getTag();
    }
    holder.position = position;
    draw(holder);
    return holder.getView();
}

Este es un ejemplo de una clase abstracta, donde

getHolder(position, view, parent);

realiza todas las operaciones de configuración para el

ImageViews, TextViews, etc..
Ahmed Adel Ismail
fuente
1
No me di cuenta de que todo este tiempo estaba haciendo. parent.setTag(holder);en lugar de la forma correcta que es,view.setTag(holder);
ArtiomLK
2

Usando el patrón Holder puedes lograr lo que quieres:

Puede encontrar la descripción de este patrón aquí:

El reciclaje de la vista de lista ocurre cuando se desplaza hacia abajo en la pantalla y los elementos de la vista de lista anteriores están ocultos. Se reutilizan para mostrar nuevos elementos de vista de lista.

Krishna
fuente