Pese a que los AlertDialog son muy fáciles y cómodos de utilizar, resulta muy díficil conseguir una personalización a fondo de los mismos sobre todo por la aplicación de estilos, siendo necesario modificar estilos por defecto y aún así el resultado depende tanto de de la versión de Android como del fabricante del dispositivo. En este tip vamos a «emular» un AlertDialog pero diseñando nosotros todo el layout así como sus estilos usando directamente la clase Dialog. Esta técnica permitirá controlar completamente tanto el formato del Dialog como sus estilos, haciendo que estos sean los mismos en cualquier dispositivo independientemente de la versión de Android que ejecuten y que, por lo tanto,siempre «encajen» con el estilo de nuestra app.
El ejemplo que se mostrará consistirá en un Dialog de color blanco con un borde gris y redondeado. Tendrá un título negro separado del cuerpo del mensaje por un View tal y como vimos en el tip #4 , y un par de botones Aceptar-Cancelar con un estilo también personalizado. El layout es el siguiente:
<?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="@drawable/dialog_container" android:orientation="vertical" > <TextView android:id="@+id/titulo" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:textColor="@android:color/black" android:layout_weight="0"/> <View android:id="@+id/divider" android:layout_width="fill_parent" android:layout_height="5dp" android:layout_marginBottom="3dp" android:layout_marginTop="3dp" android:background="@drawable/dialog_divider" /> <ScrollView android:id="@+id/cuerpo" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1"> <TextView android:id="@+id/contenido" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="@android:color/black" /> </ScrollView> <LinearLayout android:id="@+id/botonera" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="0"> <Button android:id="@+id/aceptar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:background="@drawable/dialog_button_selector" android:text="@string/aceptar" android:textColor="@android:color/white" /> <Button android:id="@+id/cancelar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_weight="1" android:background="@drawable/dialog_button_selector" android:text="@string/cancelar" android:textColor="@android:color/white" /> </LinearLayout> </LinearLayout>
Lo más reseñable es que ha habido que jugar con los weight (técnica ya comentada en este artículo) para que el contenido sea «scrolable» pero haciendo que los botones siempre salgan debajo incluso cuando el texto se desborde. Con respecto a los drawables (res/drawable) aplicados, no tienen nada de particular, es cuestión de ir «jugando» hasta que demos con un diseño apropiado que encaje con el de nuestra aplicación. Los incluyo «minimizados» aunque se pueden consultar en el proyecto completo de ejemplo que se enlaza al final del tip.
- dialog_container.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <!-- color de fondo --> <solid android:color="@android:color/white" /> <!-- curvatura de las esquinas --> <corners android:radius="10dp" /> <!-- padding para dejar una separacin prudencial entre los elementos conetenidos y el borde --> <padding android:left="8dp" android:top="4dp" android:right="8dp" android:bottom="8dp"/> <!-- borde --> <stroke android:width="3dp" android:color="@android:color/darker_gray" /> </shape>
- dialog_divider.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <solid android:color="@color/dialog_divider" /> <corners android:radius="5dp" /> </shape>
- dialog_button_selector.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"> <shape> <solid android:color="@color/button_pressed" /> <corners android:radius="12dp" /> <padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" /> </shape> </item> <item android:state_focused="true"> <shape> <solid android:color="@color/button_pressed" /> <corners android:radius="12dp" /> <padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" /> </shape> </item> <item> <shape> <solid android:color="@color/button_normal" /> <corners android:radius="12dp" /> <padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" /> </shape> </item> </selector>
Como buena práctica, los colores se han definido en el fichero /res/values/colors.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="button_normal">#929392</color> <color name="button_pressed">#c0c1c0</color> <color name="dialog_divider">@android:color/black</color> </resources>
Por último, veamos el código con el que creamos y mostramos el Dialog. Lo haremos desde un botón que invocará al método mostrar y tan sólo hay que tener en cuenta lo resaltado en el código y el «truco» para que no se muestre el título y los bordes por defecto que tienen de forma natural los Dialog.
package com.danielme.tipsandroid.dialog; import android.app.Activity; import android.app.Dialog; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.danieme.tipsandroid.dialog.R; public class MainActivity extends Activity { Dialog customDialog = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void mostrar(View view) { // con este tema personalizado evitamos los bordes por defecto customDialog = new Dialog(this,R.style.Theme_Dialog_Translucent); //deshabilitamos el título por defecto customDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); //obligamos al usuario a pulsar los botones para cerrarlo customDialog.setCancelable(false); //establecemos el contenido de nuestro dialog customDialog.setContentView(R.layout.dialog); TextView titulo = (TextView) customDialog.findViewById(R.id.titulo); titulo.setText("Título del Dialog"); TextView contenido = (TextView) customDialog.findViewById(R.id.contenido); contenido.setText("Mensaje con el contenido del dialog"); ((Button) customDialog.findViewById(R.id.aceptar)).setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { customDialog.dismiss(); Toast.makeText(MainActivity.this, R.string.aceptar, Toast.LENGTH_SHORT).show(); } }); ((Button) customDialog.findViewById(R.id.cancelar)).setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { customDialog.dismiss(); Toast.makeText(MainActivity.this, R.string.cancelar, Toast.LENGTH_SHORT).show(); } }); customDialog.show(); } }
El «tema» que aplicamos al Dialog es el siguiente (/res/values/styles.xml):
<?xml version="1.0" encoding="utf-8"?> <resources> <!-- http://stackoverflow.com/questions/1883425/android-borderless-dialog --> <style name="Theme_Dialog_Translucent" parent="android:Theme.Dialog"> <item name="android:windowBackground">@android:color/transparent</item> </style> </resources>
Aquí podemos ver «cara a cara» el resultado final en Android 4.1 y 2.1 para comprobar que hemos conseguido crear un Dialog independiente de la plataforma con un estilo apropiado para nuestra aplicación:
El proyecto completo para Eclipse ADT se encuentra en Github. Para más información sobre cómo utilizar GitHub, consultar este artículo.
Quiero dar las gracias al autor de este tema, me sirvió de mucha utilidad para hacer mi aplicación mas atractiva.
Primero quiero darte las Gracias por esta Espectacular web.
Y lo segundo quiero pedirte orientación de como hacer para capturar el valor de un EditText desde este dialogo, ya que lo he hecho de diferentes formas pero al enviar el valor a una base de dato o mostrar el valor mediante Toast.makeText().show(); se cierra la aplicación.. Soy nuevo en programación Android y me seria muy Útil tu ayuda… de Antemano Gracias…
En principio el EditText dentro del cuadro de diálogo se utiliza como cualquier editText, sólo hay que obtenerlo del layout del dialog:
EditText editText = (EditText) customDialog.findViewById(R.id.editText);
Hola Daniel
Me parece que la idea no funciona del todo bien. Sí, es cierto que en las capturas de pantalla que se muestran da la sensación de que funciona. Pero sólo es la sensación, ya que en realidad el fallo es ocultado por el fondo negro. Si cambiamos a un fondo blanco, vemos como el fondo negro original del dialogo sigue apareciendo por las esquinas. Por lo tanto, creo que no funciona.
En este enlace dejo la captura del resultado de la aplicación con fondo blanco:

Espero que publiques la corrección de este fallo.
Un saludo.
Hola de nuevo
En el estilo ‘Theme_Dialog_Translucent’ que se le asigna al diálogo, basta con cambiar
@null
por
#00000000
Donde se asigna un color transparente al fondo por defecto del diálogo.

De esta manera si que queda realmente un diálogo transparente.
Dejo enlace a imagen con el resultado de este cambio:
Un saludo
Gracias por la corrección!!! Ya está aplicada
Muchas gracias por el aporte, me ha servido, sólo bastó cambiar algunos colores para aplicar al tema que uso en la app. Excelente !!!
Hola estimado, hice el cambio de @null por #00000000 e igual me sigue apareciendo el borde negro.
Agradeceria la ayuda que me pueda brindar.
Saludos.