Tip Android #25: galería de imágenes con el widget Gallery

03/07/2013
android tip

Independientemente de que podamos recuperar una imagen de la galería de imágenes de nuestro propio dispositivo Android, es posible que dentro de una app queramos dar al usuario la opción de seleccionar una imagen, por ejemplo un icono o un background para personalizar la propia app. Android siempre ha ofrecido el widget Gallery para estos menesteres; este widget permite mostrar de forma sencilla una galería de imágenes en formato horizontal y scrollable, mostrando siempre en el centro del widget la imagen seleccionada. Sorprendentemente este widget está deprecated desde JellyBean y Google no ofrece ningún widget específico para esta tarea, viniendo a decir «hágalo usted mismo». Sin embargo, es un widget muy fácil de utilizar y si queremos construir rápidamente una galería de imágenes es la mejor opción sin salir de lo que nos ofrece Android y utilizar librerias de terceros. Asimismo, en el próximo tip veremos como crear una galería de imágenes multicolumna en formato vertical.

El proyecto de ejemplo consistirá en una activity que mostrará en pantalla un Gallery con algunos fondos de pantallas de Ubuntu. Cuando se seleccione una de estas imágenes, ésta se mostrará a mayor tamaño en un ImageView (se puede conseguir un resultado más vistoso usando ImageSwitcher ;)). El layout es el siguiente

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

<Gallery
    android:id="@+id/gallery"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:spacing="8dp"
    android:background="@android:color/darker_gray"
    android:paddingTop="4dp"
/>

<ImageView
    android:id="@+id/seleccionada"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@+id/gallery"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="30dp"
    android:contentDescription="@string/seleccionada" />

</RelativeLayout>

En la Activity tendremos que establecer una implementación de Adapter (no hay ninguna predefinida para Gallery) e implementar el listener que nos permitirá conocer qué imagen ha sido seleccionada. En nuestro caso, vamos a mostrar en pantalla la imagen que el usuario ha seleccionado, pero en el código se ha comentado cómo se haría para mostrar la imagen que se muestra en el centro del Gallery; por defecto para Gallery la imagen que muestra en el centro es la seleccionada.

package com.danielme.tipsandroid.gallery;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Gallery;
import android.widget.ImageView;

import com.danieme.tipsandroid.gallery.R;

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

	ImageView imagenSeleccionada;
	Gallery gallery;

	@Override 
	public void onCreate(Bundle savedInstanceState) 
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		imagenSeleccionada = (ImageView) findViewById(R.id.seleccionada);
		
		final Integer[] imagenes = { R.drawable.a, R.drawable.b, R.drawable.c,
				R.drawable.d, R.drawable.e,R.drawable.f,R.drawable.g
				}; 
 
	    gallery = (Gallery) findViewById(R.id.gallery);
		gallery.setAdapter(new GalleryAdapter(this, imagenes));
		//al seleccionar una imagen, la mostramos en el centro de la pantalla a mayor tamaño
		
		//con este listener, sólo se mostrarían las imágenes sobre las que se pulsa
		gallery.setOnItemClickListener(new OnItemClickListener() 
		{
			public void onItemClick(AdapterView parent, View v, int position, long id) 
			{
		        imagenSeleccionada.setImageBitmap(BitmapUtils.decodeSampledBitmapFromResource(getResources(), imagenes[position], 300, 0));
			}

		});
		 
		//con este otro listener se mostraría la imagen seleccionada en la galería, esto es, la que se encuentre en el centro en cada momento
//		gallery.setOnItemSelectedListener(new OnItemSelectedListener() {
//
//			@Override
//			public void onItemSelected(AdapterView parent, View v, int position, long id) 
//			{
//		        imagenSeleccionada.setImageBitmap(BitmapUtils.decodeSampledBitmapFromResource(getResources(), imagenes[position], 400, 0));
//			}
//
//			@Override
//			public void onNothingSelected(AdapterView<?> arg0) 
//			{
//				// TODO Auto-generated method stub
//				
//			}
//		});
		
	} 
	


}

Ahora vamos con lo más intersante, el Adapter. Heredamos de BaseAdapter e implementamos los métodos que estamos obligados a implementar. Realmente el Adapter es muy sencillo, sólo hay que devolver la imagen correspondiente en el método getView en un ImageView (a diferencia de un ListView en un Gallery no se reciclan los elementos, cuidado con el consumo de memoria 😉 ). Sin embargo, debido a la elevada resolución de las imágenes que he usado de ejemplo, se hace imprescindible reescalarlas si no queremos obtener un bonito «java.lang.OutOfMemory».

outofmemory

Cursos aplicaciones móviles

Para este reescalado he copiado un par de métodos de aquí, para ver todo el código descargar el proyecto de ejemplo. Puesto que esta operación de reescalado es costosa, he optado por «cachear» las imágenes ya reescaladas en un SparseArray (nuevamente cuidado con el consumo de memoria de la aplicación). El adapter finalmente queda tal que así:

package com.danielme.tipsandroid.gallery;


import com.danieme.tipsandroid.gallery.R;

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

public class GalleryAdapter extends BaseAdapter
{
	Context context;
	Integer[] imagenes;
	int background;
	//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
	SparseArray<Bitmap> imagenesEscaladas = new SparseArray<Bitmap>(7);
	
	public GalleryAdapter(Context context, Integer[] imagenes) 
	{
		super();
		this.imagenes = imagenes;
		this.context = context;
		
		//establecemos un marco para las imágenes (estilo por defecto proporcionado)
		//por android y definido en /values/attr.xml 
		TypedArray typedArray = context.obtainStyledAttributes(R.styleable.Gallery1);
	    background = typedArray.getResourceId(R.styleable.Gallery1_android_galleryItemBackground, 1);
	    typedArray.recycle();
	}

	@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) 
	{
		 ImageView imagen = new ImageView(context);

	    //reescalamos la imagen para evitar "java.lang.OutOfMemory" en el caso de imágenes de gran resolución
		 //como es este ejemplo
		 if (imagenesEscaladas.get(position) == null)
		 {
			 Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromResource(context.getResources(), imagenes[position], 120, 0);
			 imagenesEscaladas.put(position, bitmap);			 
		 }
		 imagen.setImageBitmap(imagenesEscaladas.get(position));
	     //se aplica el estilo
	     imagen.setBackgroundResource(background);
	     
	     return imagen;
	}
 
}

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

 

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

17 comentarios sobre “Tip Android #25: galería de imágenes con el widget Gallery

    1. Sí, está deprecated tal y como comento en el tip. No obstante, lo he probado en 4.3 en un Galaxy Nexus y un Nexus 7 y no he tenido ningún problema. La cuestión es que en la SDk no hay un widget equivalente para hacer lo mismo así que tienes que implementar tu propio Gallery o buscar alguna API de terceros, por ejemplo ListView horizontales o similres.

  1. Tengo un problema, he utilizado tu código y me da error de compilación en la línea:
    imagenSeleccionada.setImageBitmap(BitmapUtil.decodeSampledBitmapFromResource(getResources(), imagesIds[position], 300, 0));

    BitmapUtil cannot be resolved.

    Alguién me podría ayudar??

  2. Recorta los pasos, agregando desde la vista gráfica de eclipce( con adt android) el widget » imagen galery» y creara automáticamente el código en Activity_Main XML. Luego agrega el resto del código explicado en este tuto, o simplemente buscarlo en sources->methods y getview. Y ya, si tienes errores, control+espacio y adt te proporcionara una lista de recursos, selecciona el correcto y listo.
    Espero que esto sirva de algo.

    1. Los layout suelo generarlos inicialmente con el editor de Eclipse ADT y luego los voy modificando directamente en el XML. No soy muy amigo de asistentes (y suelo indicarlo en todos mis artículos) y similares ya que siempre terminas modificando el código generado y el ahorro de tiempo es mínimo, además del hecho de que tienes que comprender qué estás haciendo para solucionar los posibles problemas que surjan.

    1. La imagen tines que guardarla como un Blob en la BD. Cuando la recuperas, los Blob se mapean como un byte[] y lo puedes cargar en un ImageView así:

      imageView.setImageBitmap(BitmapFactory.decodeByteArray(myBlob, 0,myBlob.length));

  3. Muy bueno en verdad tu aplicación, a eso del soporte de la memoria yo quiero cargar el gallery de URLs de paginas web y mostrarlas, no se si podrias ayudarme con el codigo, soy nuevo

  4. Estimado en el Code_file r. Java del proyecto se generan dos métodos que se citan en el galleryadapter (R.STYLEABLE.GALLERY1 y el r.styleable.gallery1_android_galleryItemBackgroun) cosa que no sucede cuando tratas de implementar la galeria en otra aplicación, esta clase tampoco se puede modificar manualmente por tanto no se puede implementar en otra app ¿alguna solución?

  5. amigo una duda!…Al seleccionar y visualizar la imagen como poner el nombre en un Texview el nombre de lo seleccionado.. ejemplo al seleccionar una imagen de un perro que me muestre en un Texview… espero tu ayuda amigo. Gracias!

  6. Hola, disculpa, tengo exactamente el mismo código que tu mas sin embargo, la imagen previa (la que está en los cuadritos chiquitos) no se me muestra al momento de correr la aplicación, no sé si podrías ayudarme

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s

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