Tip Android #26: galería de imágenes con el widget GridView

18/07/2013
android tip

En el tip anterior vimos cómo utilizar el widget Gallery para mostrar una galería de imágenes «scrollable» en sentido horizontal. En esta ocasión vamos a hacer la galería con el widget GridView que permite mostrar una tabla scrollable en formato vertical. Tendremos que volver a implementar un Adapter, aunque en esta ocasión será equivalente a las implementaciones que hacemos para los ListView.

El proyecto de ejemplo consistirá en una activity que mostrará en pantalla un GridView con algunos fondos de pantallas de Ubuntu y un texto asociado. El layout es el siguiente.

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >

    <GridView
        android:id="@+id/gridView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:numColumns="2"
        android:stretchMode="columnWidth" 
        android:verticalSpacing="10dp"
        android:horizontalSpacing="5dp">
    </GridView>

</merge>

Algunas propiedades interesantes:

  • numColumns: número de columnas a mostrar, puede ser un valor fijo o la constante auto_fit que intentará mostrará el máximo número de columnas posible. Como luego veremos, no se usará el valor de esta propiedad sino que el número de columnas se establecerá programáticamente según el dispositivo.
  • stretchMode: ajuste del espacio libre. Usaremos columnWidth para que sea repartido proporcionalmente entre las columnas o spacingWidth para repartirlo entre el espaciado de las mismas
  • verticalSpacing,horizontalSpacing: espaciado entre celdas.

El contenido de la celda es el siguiente layout con la imagen y su texto asociado.

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    android:gravity="center">

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

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

</LinearLayout>

En la Activity hay que establecer el Adapter del GridView. Asimismo, para nuestro ejemplo vamos a mostrar un Toast cada vez que se seleccione una imagen. Se va a calcular programáticamente el número de columnas a mostrar en función de la orientación del dispositivo y de su densidad de pantalla, distinguiendo entre teléfonos y tablets. Esto también se podría hacer definiendo múltiples layouts, o incluso una variable en unos ficheros integers.xml con el número de columnas, ubicándolos en los directorios adecuados con sus correspondientes cualificadores (res/values/integers.xml, res/values-sw600dp-land/integers.xml, res/values-sw600dp-port/integers.xml), pero para este caso creo que es más sencillo hacerlo así.

package com.danielme.tipsandroid.gridview;

import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.danieme.tipsandroid.gridview.R;

/**
 * 
 * @author danielme.com
 * 
 */
public class MainActivity extends Activity {

	ImageView imagenSeleccionada;

	@Override 
	public void onCreate(Bundle savedInstanceState) 
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		final Integer[] imagenes = { R.drawable.a, R.drawable.b, R.drawable.c,
				R.drawable.d, R.drawable.e,R.drawable.f,R.drawable.g,R.drawable.h,
				R.drawable.i,R.drawable.j,R.drawable.k,R.drawable.l,R.drawable.m,R.drawable.n,R.drawable.o}; 
		
		GridView gridView = (GridView) findViewById(R.id.gridView);
		
		//el número de columnas se calculará en función del tamaño de pantalla y la posición
		boolean bigScreen = (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
		
		if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
		{
			if (bigScreen)
			{
				gridView.setNumColumns(4);
			}
			else
			{
				gridView.setNumColumns(3);
			}
		}
		else
		{
			if (bigScreen)
			{
				gridView.setNumColumns(3);
			}
			else
			{
				gridView.setNumColumns(2);
			}
		}
		 
		gridView.setAdapter(new GalleryAdapter(this, imagenes));
 
		gridView.setOnItemClickListener(new OnItemClickListener() {
			public void onItemClick(AdapterView<?> parent, View view,int position, long id) {
				Toast.makeText(MainActivity.this, ((TextView) view.findViewById(R.id.textView1)).getText() + " seleccionada", Toast.LENGTH_SHORT).show(); 
			}
		});
		
	} 
}

El Adapter como ya se ha comentado es equivalente al que se suele implementar para un ListView siguiendo el ViewHolderPattern. La única particularidad de este caso es que, tal y como vimos en el tip anterior, debido a la alta resolución de las imágenes es imprescindible reescalarlas y, por eficiencia, vamos a guardar las imágenes reescaladas en memoría (hay que tener precaución con esto). Para más detalles, ver el tip anterior.

package com.danielme.tipsandroid.gridview;


import com.danieme.tipsandroid.gridview.R;

import android.content.Context;
import android.graphics.Bitmap;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class GalleryAdapter extends BaseAdapter
{
	Context context;
	Integer[] imagenes;
	
	//guardamos las imágenes reescaladas para mejorar el rendimiento ya que estas operaciones son costosas
	//se usa SparseArray siguiendo la recomendación de Android Lint
	static SparseArray<Bitmap> imagenesEscaladas = new SparseArray<Bitmap>(7);
	
	public GalleryAdapter(Context context, Integer[] imagenes) 
	{
		super();
		this.imagenes = imagenes;
		this.context = context;
	}

	@Override
	public int getCount() 
	{
		return imagenes.length;
	}

	@Override
	public Object getItem(int position) 
	{ 
		return position;
	}

	@Override
	public long getItemId(int position) 
	{
		return position;
	}

	@Override 
	public View getView(int position, View convertView, ViewGroup parent) 
	{
		Holder holder = null;
		
		if (convertView == null) 
		{
			holder = new Holder(); 
	        LayoutInflater ltInflate = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE ); 
	        convertView = ltInflate.inflate(R.layout.gallery_item, null);

	        holder.setTextView((TextView) convertView.findViewById(R.id.textView1));
	        holder.setImage((ImageView) convertView.findViewById(R.id.imageView1));

	        convertView.setTag(holder); 
	      }
	      else
	      {   
	          holder = (Holder) convertView.getTag();
	      }
		
		
		 if (imagenesEscaladas.get(position) == null)
		 {
			Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromResource(context.getResources(), imagenes[position], 100, 0);
			imagenesEscaladas.put(position, bitmap);			 
		 }
		 
		holder.getImage().setImageBitmap(imagenesEscaladas.get(position));		
		holder.getTextView().setText(position + "");
		 
		return convertView;		
	}
	
	class Holder
	{
		ImageView image;
		
		TextView textView;
		
		public ImageView getImage() 
		{
			return image;
		}
		
		public void setImage(ImageView image) 
		{
			this.image = image;
		}
		
		public TextView getTextView() 
		{
			return textView;
		}
		
		public void setTextView(TextView textView) 
		{
			this.textView = textView;
		}		
		
	}
 
}

Con esto ya lo tenemos todo (los ficheros que he obviado están todos en el proyecto de ejemplo). El resultado final en JellyBean para una densidad xhdpi es el siguiente


En una Nexus 7 en formato horizontal veremos cuatro columnas tal y como cabría esperar:

android gridview

El proyecto completo para Eclipse ADT se encuentra en Github. Para más información sobre cómo utilizar GitHub, consultar este artículo.

<< TIPS ANDROID

3 comentarios sobre “Tip Android #26: galería de imágenes con el widget GridView

  1. Por favor seria tan amable de informarme si yo puedo convertir una imagen en un widget ya que tengo un huawei p6 y quiero poner una foto en la pantalla inicial donde se encuentras dos fotos ya preestablecidas. Gracias

  2. Muy buen tuto me ha sacado de un gran apuro tu proyecto. Una duda, como puedo hacer para que no me reescale las imagenes?

Deja un comentario

Este sitio utiliza Akismet para reducir el spam. Conoce cómo se procesan los datos de tus comentarios.