miércoles, 23 de noviembre de 2011

Mensajes Toast personalizados V2.0

Buenos días a todos,

ayer publique la primera versión de como hacer mensajes Toast personalizados. En esa publicación indicaba que había varias cosas que no sabia como solucionar, pues nada, hoy pongo la solución a estos problemas, espero que os guste.

Para realizar los mensajes toast personalizados que yo estaba buscando, debemos realizar los siguientes pasos:

1- Crear el layout (vista) que luego asignaremos al Toast.
2- Crear el xml (borde) donde indicaremos como tienen que ser los bordes para e mensaje.
3- Introduciremos el código Java necesario para cargar la vista y poder poner el mensaje que queramos en cada momento.
4- Llevaremos todo esto a una clase static, para compartir el mensaje en todo momento, y así tener un control sobre él para hacer que desaparezca el mensaje cuando creamos oportuno.

Este ultimo paso lo considero indispensable, ya que sino nos pasara que pulsaremos multiples veces sobre un botón, y estaremos viendo dicho Toast durante un rato hasta que se muestren todos. Para evitar que pueda pasar esto, y para evitar que el Toast se siga mostrando incluso después de abandonar la pantalla donde se mostró vamos ha realizar el paso 4.

Este ultimo paso es opcional, ya que aunque yo lo considero útil no tiene por que servir o gustar a todo el mundo.

PASO 1:
Crearemos el layout que sera la vista tal y como queremos que se muestre en los mensajes.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/toast_layout_root"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_gravity="center"
    android:layout_marginLeft="20px"
    android:layout_marginRight="20px"
    android:background="#0FFF"
    android:orientation="horizontal"
    android:padding="20dp" 
    android:gravity="center">
 <LinearLayout 
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_gravity="center"
     android:background="@drawable/borde"
    android:orientation="horizontal"
     >
     <ImageView
         android:id="@+id/toastImagen"
         android:layout_width="58dp"
         android:layout_height="58dp"
         android:src="@drawable/icon" android:layout_gravity="center"/>
     <TextView android:id="@+id/toastText"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:textColor="#000"
              android:gravity="center" android:layout_marginRight="10px" android:layout_marginLeft="10px" android:layout_gravity="center"/>
 </LinearLayout>
</LinearLayout>

Como se puede observar a diferencia de la entrada anterior en este caso la vista esta compuesta por un LinearLayout dentro de otro, esto es necesario si queremos que nuestro mensaje tenga un margen o separación entre los laterales del terminal y él.

El LinearLayout mas externo sera el que cargaremos en la clase java, de ahí que tenga un id. También se puede observar que hemos puesto un background a dicho layout, esto es necesario si queremos que el fondo sea transparente. Y mediante el padding indicamos la distancia que habrá entre el borde del terminal y el mensaje que mostramos.

El LinearLayout mas interior sera el que contendrá el background encargado de hacer el borde y el color del fondo del mensaje.

El resto de componentes es adaptable, ya que en nuestro caso hemos puesto una imagen y un texto, pero se podría modificar y añadir otra clase de objetos.

Ahora seguimos con el paso 2, que es el encargado de crear el borde y el fondo del mensaje.

PASO 2:

En este paso creamos el xml que contendrá el borde y el fondo del mensaje. Para ello crearemos un xml en el directorio res/drawable, el cual tendrá el siguiente código.

<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <stroke android:width="4dp" android:color="#FAAA" />
    <padding android:left="7dp" android:top="7dp"
            android:right="7dp" android:bottom="7dp" />
    <corners android:radius="4dp" />
    <solid android:color="#DAAA" />
</shape>

PASO 3:
Ahora pondremos el código necesario para hacer uso de la vista que hemos creado y poder montar nuestros Toast personalizados.

Para ello necesitaremos obtener la vista mediante el siguiente codigo:


Mediante este codigo obtenemos la vista, que posteriormente pasaremos al toast con esto:
Toast toast = new Toast(ctx);
     toast.setView(layout);
     text = (TextView) vista.findViewById(R.id.toastText);
     text.setText("texto a mostar");
     toast.show();

Ya con esto es suficiente para poder personalizar los mensajes, pero claro para mis desarrollos necesito una vuelta de tuerca mas, ya que con lo que hemos hecho hasta ahora lo que conseguimos es personalizar los mensajes, pero no evitamos que si pulsamos repetidamente un elemento que muestre el toast este se tenga que mostrar tantas veces como pulsaciones hubo, ni evita que si se cambia de ventana mientras se muestra el mensaje, este se siga viendo en la siguiente ventana a la que vayamos.

Para poder solucionar esto yo he realizado el paso 4, a ver que os parece.

PASO 4:
Para realizar este paso crearemos una clase static, la cual tendrá el toast que generaremos al iniciar la aplicación, la vista, la cual cargaremos nada mas empezar, y algunos objetos mas.

El código de la clase static será:
package es.atndroider.qui.util;
import android.content.Context;
import android.graphics.Paint.Join;
import android.view.Gravity;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import es.atndroider.qui.R;

public class MensajeToast {
 private static Toast toast = null;
 private static Context ctx = null;
 private static View vista = null;
 private static TextView text = null;

 public static void prepararVista(Context c, View view){
  if (ctx==null)
   ctx = c;
  if (vista==null)
   vista=view;
  text = (TextView) vista.findViewById(R.id.toastText);
  toast = Toast.makeText(ctx, "", Toast.LENGTH_LONG);
  toast.setView(vista);
 }

 public static void mostrarMensajeLargo(int mensaje){
  mostrarMensaje(mensaje, Toast.LENGTH_LONG);
 }

 public static void mostrarMensajeCorto(int mensaje){
  mostrarMensaje(mensaje, Toast.LENGTH_SHORT);
 }

 private static void mostrarMensaje(int mensaje, int duration) {
  ctx = ctx.getApplicationContext();
  Thread th = null;
  if (toast!=null){
   th = new Thread(new Runnable() {
    @Override
    public void run() {
     toast.cancel();
    }
   });
   th.start();
  }
  try {
   if (th!=null)
    th.join();
  } catch (InterruptedException e) {}
  text.setText(mensaje);
  toast.setDuration(duration);
  toast.show();
 }
}

Como se puede observar, la clase necesita una inicialización que vamos a realizar llamando al método prepararVista() al cual le pasaremos el contexto global de la aplicación y la vista obtenida a partir del layout que creamos al principio.
  LayoutInflater inflater =  getLayoutInflater();
  View layout = inflater.inflate(R.layout.toast_layout,
                                 (ViewGroup) findViewById(R.id.toast_layout_root));
  MensajeToast.prepararVista(getApplicationContext(),layout);
A partir de este momento cada vez que queramos escribir un mensaje Toast llamaremos a MensajeToast.mostrarMensaje(R.string.mensaje) el cual cancelara el toast anterior (si es que se esta mostrando), y cambiara el texto por el nuevo mensaje.

Se pueden hacer variantes, como cambiar la imagen que se muestre y otras cosas, pero eso ya queda a la imaginación de cada uno.

Espero que os haya gustado todo lo que he puesto, y si veis algo raro, no dudéis en corregidme.

Nos vemos, espero que pronto.

martes, 22 de noviembre de 2011

Mensajes Toast personalizados

Después de mucho buscar y realizar muchas pruebas, he llegado a la conclusión de que quedan bonitos los menajes Toast personalizados, es decir, dando la posibilidad de incluir una imagen al mensaje, o incluso cambiar la ubicación del mismo.

Para ello voy a poner los distintos pasos a seguir para poder llegar a esto:


Para poder llegar a esto necesitamos hacer lo siguiente:
1- Crear un xml en res/layout, yo lo he creado con el nombre toast_layout.xml, con el siguiente código:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/toast_layout_root"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_gravity="center"
    android:layout_marginLeft="20px"
    android:layout_marginRight="20px"
    android:background="@drawable/borde"
    android:fitsSystemWindows="true"
    android:orientation="horizontal"
    android:padding="10dp"
    android:gravity="center">
    <ImageView android:id="@+id/toastImagen"
        android:layout_width="58dp"
        android:layout_height="99dp"
        android:layout_marginRight="10dp"
        android:layout_marginLeft="10dp"
        android:src="@drawable/icon" />
    <TextView android:id="@+id/toastText"
              android:layout_width="wrap_content"
              android:layout_height="fill_parent"
              android:textColor="#FFF"
              android:text="esto es uyna prueba" android:gravity="center"/>
</LinearLayout>


En mi ejemplo, al utilizar una imagen, la cual es res/drawable/icon.png, y ser esa imagen mayor que el tamaño que quiero utilizar he preferido darle un tamaño de forma manual, como se ve en las lineas 15 y 16.

2- Asignar el borde al Toast, para ello creamos en res/drawable un xml, en mi caso borde.xml, el cual asignamos al background del linearlayout que contiene el Toast (linea 9). Tendra el siguiente código:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <stroke android:width="2dp" android:color="#FFFFFFFF" />
    <padding android:left="3dp" android:top="3dp"
            android:right="3dp" android:bottom="3dp" />
    <corners android:radius="2dp" />
    <solid android:color="#DAAA" />
</shape>

En este código definimos el grosor de las lineas del borde (lineas 4 y 5) y el grosor de las esquinas (linea 6). Así como el color del fondo que tendrá (linea 7).

3- Lo ultimo es asignar al Toast que estemos utilizando la vista que crearemos con el linearlayout que tenemos al principio.

Para ello debemos realizar lo siguiente:
LayoutInflater inflater =  getLayoutInflater();
 View layout = inflater.inflate(R.layout.toast_layout,
                        (ViewGroup) findViewById(R.id.toast_layout_root));
 Toast toast = new Toast(ctx);
 toast.setView(layout);
 text = (TextView) vista.findViewById(R.id.toastText);
 text.setText("texto a mostar");
 toast.show();

En las lineas 2 y 3 obtendremos la vista (LinearLayout en nuestro caso) que se incluira en el Toast.

Cada vez que se quiera escribir en el Toast deberemos utilizar text.setText("Texto"); para modificarlo. Esto nos lleva a lo siguiente, un Toast persistente para asi poder cancelar los mensajes entre una ventana y otra. Pero eso se tratara en otro momento, que todavia estoy mirando como conseguir que el ancho de estos Toast personalizados sea menor que el ancho de los terminales.

Nos vemos.