03/07/2013
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».
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.
Hola el Widget Gallery no está obsoleta ? ..he tenido problemas últimamente con eso. saludos
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.
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??
La clase no se llama BitmapUtil, sino BitmapUtils y está incluída en el código de ejemplo. Simplemente encapsula los métodos propuestos en el siguiente artículo del blog oficial de desarrollo Android:http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
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.
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.
oye y lo sabes hacer con Base de datos? rellenar el imageView con datos de una consulta?
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));
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
Prueba con Picasso: http://square.github.io/picasso/
Esos nombres de donde los esta obteniendo Gallery1 y Gallery1_android_galleryItemBackground
disculpa y para poder acceder a las fotos de mi galeria e insertarla a esta como seria
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?
Te agradezco bastante el tutorial, es muy gráfico
oye tienes algo parecido pero en ves de imagenes muestre archivos de texto
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!
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