Pregunta

tengo un GridView cuyos elementos se basan en un FrameLayout que contiene un ImageView y un CheckedTextView. El archivo XML para el framelayout es el siguiente:

<FrameLayout
xmlns:android = "http://schemas.android.com/apk/res/android"
android:id="@+id/gridImage"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<ImageView
android:id="@+id/image"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:adjustViewBounds="false">
</ImageView>
<CheckedTextView
android:id="@+id/imageTick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_gravity="center_horizontal|bottom"
android:checkMark="@drawable/icon"
android:checked="false"
android:visibility="invisible"
>
</CheckedTextView>
</FrameLayout>

Editar: este es el adaptador getView() Método que uso para cada elemento:

public View getView(int position, View convertView, ViewGroup parent) {
        Log.d("getView()", position+"");
        View v;
        ImageView imageView;
        if(convertView == null) {

            v = LayoutInflater.from(mContext).inflate(R.layout.photo_text_view, null);
            v.setLayoutParams(new GridView.LayoutParams(100,100));
        }
        else
        {
            v = convertView;

        }
        imageView = (ImageView) v.findViewById(R.id.image);
        imageView.setImageBitmap(mThumbsIds.get().get(position));

        v.setPadding(8, 8, 8, 8);
        return v;
    }

En mi clase de actividad, carga un menú contextual y una vez que selecciono una opción, hago el CheckedTextView visible para eso GridView elemento como así:

GridView gridView = (GridView) findViewById(R.id.gridview);
    View selected =gridView.getChildAt(position);
    CheckedTextView selectedCheck = (CheckedTextView)selected.findViewById(R.id.imageTick);
    selectedCheck.setChecked(true);
    selectedCheck.setVisibility(View.VISIBLE);

Entonces, potencialmente, dos cosas pueden suceder desde este punto:

1) Digamos que elegí el elemento en la posición 0, el checkedTextView se vuelve visible, pero cuando despliegue hacia abajo en mi View GridView, otro elemento (por ejemplo, la posición 17) tiene su Visita CheckEdText Visible también. Esto continúa en elementos aleatorios a medida que me desplace hacia abajo hacia la parte inferior.

2) Si elijo un elemento hacia la parte inferior, desplázate hacia arriba y ejecuto uno de los métodos para hacer que todos los checkedTextView sean invisibles, un NullPointerException se arroja para el elemento en la parte inferior. Esto sucede en este punto: View selected =gridView.getChildAt(position); donde seleccionado se vuelve nulo.

¿Que está pasando aqui? ¿Por qué estos checkedTextView aleatorios se vuelven visibles y por qué obtengo excepciones?

¿Fue útil?

Solución

Los problemas se encuentran en la forma en que rastrea los elementos verificados:

GridView gridView = (GridView) findViewById(R.id.gridview);
View selected =gridView.getChildAt(position);
CheckedTextView selectedCheck = (CheckedTextView)selected.findViewById(R.id.imageTick);
selectedCheck.setChecked(true);
selectedCheck.setVisibility(View.VISIBLE);

1) getChildAt No le dará la vista correcta si se ha desplazado en el contenido y está indexando por posición del adaptador. Al hacer referencia a una vista activa en un GridView o ListView quieres hacer algo como esto:

final int index = position - gridView.getFirstVisiblePosition();
View selected = gridView.getChildAt(index);

La razón es que tu GridView Solo mantiene las vistas de los niños para los elementos del adaptador que se muestra actualmente. Puede que solo tenga opiniones infantiles para los elementos de 4 a 23 si los elementos 0 a 3 se desplazaron de la parte superior anteriormente.

Es por eso que estás obteniendo excepciones cuando lo haces View selected =gridView.getChildAt(position); Una vista para esa posición en realidad no existe cuando está fuera de la pantalla, por lo que getChildAt devoluciones null.

2) Cuando un ListView o GridView tiene su cambio de contenido o de lo contrario necesita volver a recubrir sus opiniones infantiles, lo hace reiniciando las vistas existentes utilizando el convertView parámetro a su adaptador. Su adaptador nunca ajusta el estado marcado de las vistas de su artículo, por lo que se puede reutilizar una vista verificada para un artículo que debería no se controla más tarde.

Para resolver esto, debe tener su modelo de datos que presente su adaptador rastree el estado verificado de sus elementos en lugar de confiar en las vistas del elemento para hacerlo. Esto significa que su adaptador siempre debe verificar/establecer el estado marcado del artículo en getView. Puede parecer algo así:

imageView = (ImageView) v.findViewById(R.id.image);
imageView.setImageBitmap(mThumbsIds.get().get(position));

CheckedTextView checkView = (CheckedTextView) v.findViewById(R.id.imageTick);

if (mData.isItemChecked(position)) {
    checkView.setChecked(true);
    checkView.setVisibility(View.VISIBLE);
} else {
    checkView.setVisibility(View.GONE);
}

Luego, cuando se selecciona su opción, debe cambiar el estado marcado de un elemento, en lugar del fragmento de código anterior, haga algo como:

data.setItemChecked(position, true);
adapter.notifyDataSetChanged();

notifyDataSetChanged() le diré a la GridView que debe volver a unir las vistas del artículo, que tendrán su getView Método Arreglar los estados marcados correctamente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top