Cómo crear una notificación personalizada en Android

17-08-2017 - Andrés Cruz

Cómo crear una notificación personalizada en Android In english

Este material forma parte de mi curso y libro completo; puedes adquirirlos desde el apartado de libros y/o cursos.

En esta entrada veremos cómo crear una notificación personalizada con Android empleando como ambiente de desarrollo de software Android Studio.

¿A qué nos referimos con notificación personalizada?

A que emplearemos una vista desarrollada desde cero para tal fin; incluso emplearemos una serie de botones los cuales pueden ejercer sus acciones sobre la actividad que crea dicha notificación, servicios o Broadcast que definieramos.

Creando la notificación personalizada

Los intent en el paso de datos

El código de la vista está un poco más adelante en la entrada, pero por ahora indicaremos los primeros pasos para crear una notificación en Android.

Intent intent = new Intent(getApplicationContext(), MusicService.class); noBundle.putInt("accion", 1);//This is the value I want to pass intent.putExtras(noBundle); PendingIntent pendingIntent = PendingIntent.getService(ListActivity.this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT); view.setOnClickPendingIntent(R.id.ibAccion, pendingIntent);

Expliquemos el propósito de cada componente del código presentado anteriormente:

  • El Bundle Es empleado generalmente para pasar datos entre componentes como actividades y fragments a través de números, textos o booleanos.
  • El Intent es el componente empleado para iniciar la comunicación entre los componentes como inicar una actividad o servicio (más información en la documentación oficial).
  • El PendingIntent es un objeto que contiene a un intent; este objeto PendingIntent permite al NotificationManager mandar al sistema la acción a ejecutar (como abrir una actividad).

Muy importante la bandera PendingIntent.FLAG_UPDATE_CURRENT que en conjunto con:

<activity    android:name=".MainActivity"    android:configChanges="orientation|keyboardHidden|screenSize"    android:launchMode="singleTop"    android:screenOrientation="portrait"> ... </activity>

Indica que la actividad existe actualmente (por defecto la MainActivity es la primera actividad que inició) y el android:launchMode="singleTop" y es una forma de indicar que la instancia de la actividad sea única, aunque existen otras formas como puedes consultar en el siguiente enlace (en otras palabras significa actualizar la instancia de la actividad que está en ejecución actualmente).

Definiendo las acciones de la vista personalizada

Cómo son tres botones, necesitamos tres Intents diferentes y todo lo que esto conlleva, por lo tanto ahora multiplicamos esto por tres:

Intent intent = new Intent(getApplicationContext(), MusicService.class);
noBundle.putInt("accion", 1);//This is the value I want to pass
intent.putExtras(noBundle);
PendingIntent pendingIntent = PendingIntent.getService(ListActivity.this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
view.setOnClickPendingIntent(R.id.ibAccion, pendingIntent);

Bundle noBundle2 = new Bundle();
noBundle2.putInt("accion", 2);//This is the value I want to pass
Intent intent2 = new Intent(getApplicationContext(), ListActivity.class);
intent2.putExtras(noBundle2);
PendingIntent pendingIntent2 = PendingIntent.getActivity(ListActivity.this, 2, intent2, PendingIntent.FLAG_UPDATE_CURRENT);
view.setOnClickPendingIntent(R.id.ibQuitar, pendingIntent2);

Bundle noBundle3 = new Bundle();
noBundle3.putInt("accion", 3);//This is the value I want to pass
Intent intent3 = new Intent(getApplicationContext(), ListActivity.class);
intent3.putExtras(noBundle3);
PendingIntent pendingIntent3 = PendingIntent.getActivity(ListActivity.this, 3, intent3, PendingIntent.FLAG_UPDATE_CURRENT);
view.setOnClickPendingIntent(R.id.ibSiguiente, pendingIntent3);

Lanzando la notificación personalizada

Ahora podemos crear nuestra notificación; primero el administrador de la notificación (NotificationManager) que como su nombre indica y mediante una instancia de esta clase podemos lanzar la notificación con el NotificationCompat.Builde podemos establecer las propiedades de la notificación y crear la notificación propiamente dicha; por ejemplo indicamos el título, subtítulo el icono y el PendingIntent cuya acción será ejercida al momento de que el usuario realice un clic sobre la notificación:

NotificationManager nManager;
NotificationCompat.Builder nBuilder;
RemoteViews remoteView;

PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
        new Intent(this, ListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);

nBuilder = new NotificationCompat.Builder(this)
        .setContentTitle(getString(R.string.app_name))
        .setTicker(getString(R.string.app_name))
        .setSmallIcon(R.mipmap.ic_launcher)
        .setContentIntent(contentIntent)
        .setOngoing(false)
        .setAutoCancel(false);

Como podemos apreciar, al momento de crear la notificación establecemos varios parámetros como el contenido (título), icono y el mismo Intent que definimos anteriormente.

Ahora falta especificar cual es la vista en cuestión (la que posee el diseño que creamos con los tres botones), esto lo hacemos mediante un RemoteViews:

remoteView = new RemoteViews(getPackageName(), R.layout.notification_layout);
remoteView.setImageViewResource(R.id.image, R.mipmap.ic_launcher);
remoteView.setTextViewText(R.id.title, "Título");
remoteView.setTextViewText(R.id.text,"Nombre");

Como podemos ver en el código anterior, accedemos a cada una de sus componentes y establecemos un valor que para efectos de este tutorial son fijos.

En nuestro caso la vista tiene el siguiente contenido:

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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#333333"
    android:orientation="vertical"
    android:weightSum="1">

    <LinearLayout
        android:id="@+id/right"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/imagenotileft"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:background="@mipmap/ic_launcher" />

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:orientation="vertical">

            <TextView
                android:id="@+id/title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:layout_marginRight="5dp"
                android:layout_marginTop="9dp"
                android:textColor="#FFFFFF"
                android:textSize="16sp"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/title"
                android:layout_marginLeft="5dp"
                android:layout_marginRight="5dp"
                android:textColor="#EEEEEE"
                android:textSize="14sp" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="horizontal">

            <ImageButton
                android:id="@+id/ibAtras"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_margin="0dp"
                android:background="@NULL"
                android:gravity="center_horizontal|center_vertical"
                android:padding="0dp"
                android:src="@drawable/ic_action_av_skip_previous" />

            <ImageButton
                android:id="@+id/ibAccion"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_margin="0dp"
                android:background="@NULL"
                android:gravity="center_horizontal|center_vertical"
                android:padding="0dp"
                android:src="@drawable/ic_action_ic_play_pause" />

            <ImageButton
                android:id="@+id/ibSiguiente"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_margin="0dp"
                android:background="@NULL"
                android:gravity="center_horizontal|center_vertical"
                android:padding="0dp"
                android:src="@drawable/ic_action_av_skip_next" />

            <Button
                android:id="@+id/ibQuitar"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_margin="0dp"
                android:background="@NULL"
                android:gravity="center_horizontal|center_vertical"
                android:padding="0dp"
                android:text="X"
                android:textColor="#444444"
                android:textSize="16sp" />
        </LinearLayout>
    </LinearLayout>
</LinearLayout>
Notificación Personalizada

Finalmente establecemos la vista anterior y lanzamos la notificación:

        setListeners(remoteView);
        nBuilder.setContent(remoteView);

        nManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        nManager.notify(2, nBuilder.build());

Replicar las notificaciones en Android

Hasta ahora hemos visto cómo crear una notificación con un layout personalizado y cómo interactuar con los elementos que definimos en este layout como Buttons o ImageView, etc; en esta segunda parte de esta entrada veremos cómo crear notificaciones que se puedan replicar; esta es una acción muy empleada en aplicaciones de mensajería como Hangout de Google o el mismo Whatsapp en Android.

Compatibilidad entre las notificaciones

Las notificaciones tienen una gran compatibilidad hacia atrás empleando la clase NotificationCompat.Builder y podemos emplear muchisimas opciones que tienen desde prioridad hasta estilos pero debemos tener muy presentes que funcionalidades empleamos ya que algunas funciones en específico están provistas solo para versiones superiores de Android superiores a la 4.1.

Creando el medio para el pase de datos entre componentes: PendingIntent

Lo primero que debemos hacer es crear un PendingIntent y asociar el Intent que es el medio empleado para pasar datos de un componente; estos componentes no solo pueden ser Actividades o Fragments, también pueden ser otros como Broadcast para realizar actualizaciones en segundo plano sin la necesidad de invocar a la actividad; aunque para esto debemos emplear Android N en adelante; para no extender demasiada esta entrada, nos iremos por el caso en que se invoca a la actividad.

El PendingIntent es el mecanismo existente que tenemos para pasar datos entre distintos componentes como actividades, broadcast o servicios que embebe dentro del mismo los datos que queremos pasar a estos componentes (en caso de que se desee pasar un dato):

RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY)
       .setLabel(replyLabel)
       .build();

Intent resultIntent = new Intent(this, MainActivity.class);
resultIntent.setAction(KEY_TEXT_REPLY);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent replyPendingIntent =
       stackBuilder.getPendingIntent(
               0,
               PendingIntent.FLAG_UPDATE_CURRENT
       );

El método setAction permite asociar el nombre del dato a pasar, que en este caso será el texto que el usuario suministre en el campo de réplica y con este nombre accedemos al texto que ingreso el usuario en el onCreate de nuestra actividad:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);

   Intent intent = getIntent();

   if (KEY_TEXT_REPLY.equals(intent.getAction())) {

       Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
       if (remoteInput != NULL) {
           Log.i("REPLICA", "" + remoteInput.getCharSequence(KEY_TEXT_REPLY));
       }

   }
   replyNotification();
}

Con el método RemoteInput indicamos que crearemos un mecanismo para el pase de datos dentro del PendingIntent; además de asociamos el label o texto que va a aparecer en el campo de la réplica.

Creando la notificación replica

Asociamos el PendingIntent que creamos anteriormente:

NotificationCompat.Action action =
       new NotificationCompat.Action.Builder(R.mipmap.ic_launcher,
               "Replicar", replyPendingIntent)
               .addRemoteInput(remoteInput)
               .build();

Creando la notificación replica

Creamos la notificación estableciendo el contenido de la misma: icono representativo, título, contenido y la acción que configuramos en el bloque anterior:

NotificationCompat.Builder newMessageNotification =
       new NotificationCompat.Builder(MainActivity.this)
               .setSmallIcon(R.mipmap.ic_launcher)
               .setContentTitle("My notification")
               .setContentText("Hello World!")
               .addAction(action);

Finalmente lanzamos la notificación junto con las acción que configuramos en los pasos anteriores a través del método build():

NotificationManagerCompat notificationManager =
       NotificationManagerCompat.from(MainActivity.this);
notificationManager.notify(NOTIFICATION_ID, newMessageNotification.build());

El método completo queda como:

    public void replyNotification() {
        RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY)
                .setLabel(replyLabel)
                .build();

        Intent resultIntent = new Intent(this, MainActivity.class);
        resultIntent.setAction(KEY_TEXT_REPLY);
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(MainActivity.class);
        stackBuilder.addNextIntent(resultIntent);
        PendingIntent replyPendingIntent =
                stackBuilder.getPendingIntent(
                        0,
                        PendingIntent.FLAG_UPDATE_CURRENT
                );

        NotificationCompat.Action action =
                new NotificationCompat.Action.Builder(R.mipmap.ic_launcher,
                        "Replicar", replyPendingIntent)
                        .addRemoteInput(remoteInput)
                        .build();


        NotificationCompat.Builder newMessageNotification =
                new NotificationCompat.Builder(MainActivity.this)
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setContentTitle("My notification")
                        .setContentText("Hello World!")
                        .addAction(action);

        NotificationManagerCompat notificationManager =
                NotificationManagerCompat.from(MainActivity.this);
        notificationManager.notify(NOTIFICATION_ID, newMessageNotification.build());
    }

Finalmente el resultado es el siguiente:

Notificación de Réplica en android

Enlaces de referencia:


Andrés Cruz
Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz en Udemy

Acepto recibir anuncios de interes sobre este Blog.

Conozca nuestros cursos sobre Laravel, CodeIgniter, Flutter, Electron, Django, Flask y muchos más!

Ver los cursos
¡Hazte afiliado en Gumroad!

!Cursos desde!

4$

En Academia

Ver los cursos

!Libros desde!

1$

Ver los libros
!Web Alojada en Hostinger!