- 24/10/2013 (Primera publicación)
- 15/08/2015: Actualizado a Lollipop, con trol de salida de la Activity
Hoy en día en portales web los conocidos como «Image Slider» o carrusel de imágenes son elementos habituales y existen numerosas implementaciones, muchas basadas en jQuery como Camera o Nivo Slider. En este tip se va a implementar algo parecido pero usando sólo la SDK de Android. Se reutilizarán los wallpapers de Ubuntu de los tip 25 y 26 pero con la resolución reducida para evitar su reescalado.
La idea es utilizar el widget ImageSwitcher que permite cambiar la imagen de un ImageView aplicando una animación tanto a la imagen actual como a la nueva que aparece. Las animaciones son las clásicas de Android que ya vimos en el artículo Diseño Android: Transiciones entre Activities, de hecho se van a reutilizar las animaciones «fade in» y «fade out» para hacer aparecer y desaparecer las imágenes de forma elegante.
Por otra parte, el carrusel de imágenes será automático, esto es, las imágenes se irán mostrando sucesivamente cada cierto intervalo tiempo. Esto lo podemos conseguir mediante la clase Timer que de forma asíncrona cada cierto intervalo de tiempo realiza una acción, en nuestro caso cambiar la imagen.
En primer lugar, veamos el layout. Muestra centrado un ImageSwitcher así como los botones para iniciar y detener el carrusel.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_above="@+id/buttons" android:gravity="center" > <ImageSwitcher android:id="@+id/imageSwitcher" android:layout_width="wrap_content" android:layout_height="wrap_content" > </ImageSwitcher> </LinearLayout> <LinearLayout android:id="@+id/buttons" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" > <Button android:id="@+id/buttonStart" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:onClick="start" android:text="@string/start" /> <Button android:id="@+id/buttonStop" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:onClick="stop" android:text="@string/stop" /> </LinearLayout> </RelativeLayout>
Y ahora vamos con el código de la Activity, que muestra un uso básico de ImageSwitcher y Timer; al ImageSwitcher le proporcionamos las dos animaciones para realizar el cambio de imagen y a la hora de utilizar el Timer hay que hacer dos consideraciones:
- El cambio de imagen ha de ejecutarse en el UI Thread, esto es, el hilo de la aplicación que puede modificar la interfaz gráfica.Para ello se utiliza el método
runOnUiThread. - Una vez que el Timer se lanza, es más que recomendable para mejorar el rendimiento de la aplicación evitar que se continúe ejecutando una vez que el usuario ha salido de la Activity o de la aplicación. Este control se realiza sobreescribiendo el método onPause para cancelar el timer siempre que la Activity se envíe al segundo plano o se destruya.
package com.danielme.tipsandroid.imageslider; import java.util.Timer; import java.util.TimerTask; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.ImageSwitcher; import android.widget.ImageView; import android.widget.ViewSwitcher.ViewFactory; /** * * @author danielme.com * */ public class MainActivity extends Activity { private ImageSwitcher imageSwitcher; private int[] gallery = { R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e, R.drawable.f }; private int position; private static final Integer DURATION = 2500; private Timer timer = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageSwitcher = (ImageSwitcher) findViewById(R.id.imageSwitcher); imageSwitcher.setFactory(new ViewFactory() { public View makeView() { return new ImageView(MainActivity.this); } }); // Set animations // https://danielme.com/2013/08/18/diseno-android-transiciones-entre-activities/ Animation fadeIn = AnimationUtils.loadAnimation(this, R.anim.fade_in); Animation fadeOut = AnimationUtils.loadAnimation(this, R.anim.fade_out); imageSwitcher.setInAnimation(fadeIn); imageSwitcher.setOutAnimation(fadeOut); } // ////////////////////BUTTONS /** * starts or restarts the slider * * @param button */ public void start(View button) { if (timer != null) { timer.cancel(); } position = 0; startSlider(); } public void stop(View button) { if (timer != null) { timer.cancel(); timer = null; } } public void startSlider() { timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { public void run() { // avoid exception: // "Only the original thread that created a view hierarchy can touch its views" runOnUiThread(new Runnable() { public void run() { imageSwitcher.setImageResource(gallery[position]); position++; if (position == gallery.length) { position = 0; } } }); } }, 0, DURATION); } // Stops the slider when the Activity is going into the background @Override protected void onPause() { super.onPause(); if (timer != null) { timer.cancel(); } } @Override protected void onResume() { super.onResume(); if (timer != null) { startSlider(); } } }
El resultado final en Jelly Bean:
El proyecto completo se encuentra en Github. Para más información sobre cómo utilizar GitHub, consultar este artículo.
<< TIPS ANDROID
Muy buen tuto 😀 te felicito pero me quedo una duda, ¿como podrias sacar imagenes pero desde la SD en vez de R.drawable?
Echa un vistazo a los métodos de la clase BitmapFactory, por ejemplo:
imageView.setImageBitmap(BitmapFactory.decodeFile(«/mnt/sdcard/imagen1.jpg»));
En el tip #05 tienes algo de info para trabajar con el almacenamiento en Android.
Hola buen día… está muy interesante y muy padre el aporte pero tengo una duda. Estoy tratando de implementar un slider como el tuyo en un proyecto con Fragments, pero cuando me marca un error en el return new ImageView(MainActivity.this);… me dice que el constructor no está definido… Espero me puedas echar la mano ya que es un proyecto escolar… De antemano muchas gracias y muy buen aporte
De entrada para obtener en un Fragment la Activity asociada debes usar getActivity().
Así es ya lo hice y eclipse no me marca error… pero al momento de correr la app colapsa 😦
No se si lo hice mal pero en return new ImageView(MainActivity.this); cambien el main activity por el getActivity() y eclipse no marca error pero la aplicación colapsa cuando se corre en el celular
Hola quiciera saber como ejecutar una actividad una ves finalizado el bucle yo modifique el codigo para que no tenga botones, lo puse en fullscreen la activity y luego de ejecutar todo cuando llega a la posicion 0 intento finalizar el hilo y ejecutar una actividad como podia lograrlo gracias
No hay que hacer nada particular, dentro del método run al llegar a la posición cero lanza la activity que quieras de forma habitual y para el slider con el método cancel del timer (es lo que se hace al pulsarse stop), incluso puedes finalizar la activity (finish) si no quieres que se restaure al pulsar back.
hey bro how can i can make it run automatically , that mean no need to pres start to make it start
you can call the method run in onCreate 🙂
soory i mean the method start
geneal 🙂 excelente tip, me sirvio mucho, solo tengo una duda como le hago para que sea automatico, sin el boton de start.
soy nuevo desarrollando en android
saludos 🙂
Utiliza estos métodos:
@Override
protected void onPause() {
super.onPause();
timer.cancel();
}
@Override
protected void onResume() {
super.onResume();
startSlider();
}
El slider se iniciará siempre al mostrarse la pantalla y se detendrá al salir de ella.
Parcero, de hecho hay una forma mas sencilla de hacerlo. Usa el metodo del Image switcher que se llama postDelayed, te dejo la idea basica:
imageSwitcher.postDelayed(new Runnable() {
@Override
public void run() {
}
}, 1000);
De esta forma no tienes que meterme con el thread principal del ui, el metodo lo llama solo.
Cualquier correccion es bienvenida
si quiero dar click a una imagen mientras pasa como puedo hacer?
Excelente post, claro y sencillo.
Estoy intentando usar el codigo tal cual está acá para luego ir modificandolo, pero me da error en las siguientes lineas:
Animation fadeIn = AnimationUtils.loadAnimation(this, R.anim.fade_in);
Animation fadeOut = AnimationUtils.loadAnimation(this, R.anim.fade_out);
Cannot resolve symbol ‘fade_in’ y ‘fade_out’
Todo el codigo está tal cual, es el único error que muestra, por favor espero pueda ayudarme. Saludos
Disculpa, ya solucioné jeje leyendo un poco me di cuenta que tenía que crear los xml de las transiciones, lo siento mucho. buen post, gracias.. Saludos
por que no me deja declarar el fade_out y el fade_in
Hola, excelente código : ). Tengo una duda, las imagenes se aparecen repentinamente, no va una por una. Es decir cambiar muy rápido de imagen a imagen aunque tengo los tiempos descritos aquí en el tutorial
A mi me sucedió lo mismo, mas cuando utilizaba imágenes un poco grandes. Lo que solucionó este problema fue sacar toda la parte de TImer y utilizar el método «postDelayed» como mencionó RGM más arriba. Por las dudas copio nuevamente el código que el publicó.
imageSwitcher.postDelayed(new Runnable() {
@Override
public void run() {
}
}, 1000);
gracias por el tutorial, una pregunta como puedo agregar botones dentro de la imagen para asi ver la transicion
me sale este error java.lang.NullPointerException
Hola, antes que nada gracias por el tutorial. Como puedo hacer para lanzar la animación en el onCreate de la activity? en realidad intenté hacerlo pero no funciona el fade_out, van apareciendo de golpe las imágenes.
muy bueno y gran aporte
namas me gustaria saber si hay uno asi igual pero sin necesiidad de darle play a un botton es decir que automaticamente cuando entres a ese layout este comienze
hola amiga tengo dentro de mi drawable 7 imagenes y se me cuelga la app en la quinta imagen que puedo hacer gracias de antemano
Gran aporte, se podra enviar a un webview al dar clic en una imagen en el slider?
Excelente, después de 5 horas de búsqueda encontré la respuesta. Gracias