En este documento:
- Crear un fragmento de diálogo
- Crear un diálogo de alerta
- Traspasar eventos al host del diálogo
- Exhibir un diálogo
- Exhibir un diálogo en pantalla completa o como fragmento integrado
- Descartar un diálogo
Clases fundamentales
Consulta también:
- Guía de diseño de diálogos
- Selectores (diálogos de fecha/hora)
Un diálogo es una ventana pequeña que le indica al usuario que debe tomar una decisión o ingresar información adicional. Un diálogo no ocupa toda la pantalla y generalmente se usa para eventos modales que requieren que los usuarios realicen alguna acción para poder continuar.
Diseño de diálogos
Para obtener información acerca de cómo diseñar tus diálogos, incluidas recomendaciones para idioma, lee la guía de diseño Diálogos.
La clase Dialog es la clase de base para los diálogos, pero debes
evitar crear instancias de Dialog directamente.
En su lugar, usa una de las siguientes subclases:
AlertDialog- Un diálogo que puede mostrar un título, hasta tres botones, una lista de elementos seleccionables o un diseño personalizado.
DatePickerDialogoTimePickerDialog- Un diálogo con una IU predefinida que le permite al usuario seleccionar una fecha o una hora.
Evita ProgressDialog
Android incluye otra clase de diálogo llamada
ProgressDialog que muestra un diálogo con una barra de progreso. No obstante, si
necesitas indicar la carga o un progreso indeterminado, debes seguir las pautas
de diseño para Progreso y
actividad y usar una ProgressBar en tu diseño.
Estas clases definen el estilo y la estructura para tu diálogo, pero debes
usar un DialogFragment como contenedor de tu diálogo.
La clase DialogFragment proporciona todos los controles que
necesitas para crear tu diálogo y administrar su aspecto, en lugar de llamar a métodos
en el objeto Dialog.
El uso de DialogFragment para administrar el diálogo
garantiza que aborde correctamente eventos de ciclo de vida
como cuando el usuario presiona el botón Atrás o gira la pantalla. La clase DialogFragment también te permite reutilizar la IU del diálogo como
componente integrable a una IU más grande, al igual que un Fragment tradicional (por ejemplo, cuando deseas que la IU del diálogo se vea diferente
en pantallas grandes y pequeñas).
En las siguientes secciones de esta guía se describe la manera de usar un DialogFragment junto con un objeto
AlertDialog. Si quisieras crear un selector de fecha y hora, debes leer la guía
Selectores.
Nota:
Debido a que la clase DialogFragment se agregó originalmente con
Android 3.0 (nivel de API 11), en este documento, se describe la manera de usar la clase DialogFragment que se proporciona en la biblioteca de compatibilidad. Al agregar esta biblioteca
a tu app, puedes usar DialogFragment y una variedad de otras
API en dispositivos con Android 1.6 o versiones posteriores. Si la versión mínima que admite tu app
corresponde al nivel de API 11 o a niveles superiores, puedes usar la versión de framework de DialogFragment, pero debes saber que los enlaces que aparecen en este documento son para las Support
Library API. Cuando usas la biblioteca de compatibilidad,
asegúrate de importar la clase android.support.v4.app.DialogFragment
y no android.app.DialogFragment.
Crear un fragmento de diálogo
Puedes lograr una gran variedad de diseños de diálogo; incluidos
diseños personalizados y los que se describen en la guía de diseño Diálogos;
al extender
DialogFragment y crear un AlertDialog
en el método callback onCreateDialog().
Por ejemplo, aquí te mostramos un AlertDialog básico administrado dentro de
un DialogFragment:
public class FireMissilesDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(R.string.dialog_fire_missiles)
.setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// FIRE ZE MISSILES!
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
}
});
// Create the AlertDialog object and return it
return builder.create();
}
}
Figura 1. Diálogo con un mensaje y dos botones de acción.
Ahora, cuando creas una instancia de esta clase y llamas a show() en ese objeto, el diálogo aparece como
se muestra en la figura 1.
La siguiente sección describe más acerca del uso de las AlertDialog.Builder
API para crear el diálogo.
Según la complejidad del diálogo, puedes implementar una variedad de otros métodos de
callback en el DialogFragment, incluidos todos los
métodos básicos del ciclo de vida del fragmento.
Crear un diálogo de alerta
La clase AlertDialog te permite crear una variedad de diseños de diálogo y
generalmente es la única clase que necesitarás.
Como se muestra en la figura 2, hay tres regiones en un diálogo de alerta:
Figura 2: Diseño de un diálogo.
- Título
Esto es opcional y solo se debe usar cuando el área de contenido esté ocupada por un mensaje detallado, una lista o un diseño personalizado. Si necesitas indicar un mensaje o una pregunta simple (como el diálogo de la figura 1), no necesitas un título.
- Área de contenido
Esto puede mostrar un mensaje, una lista u otro diseño personalizado.
- Botones de acción
No debe haber más de tres botones de acción en un diálogo.
La clase AlertDialog.Builder
proporciona muchas API que te permiten crear un AlertDialog
con estos tipos de contenido, incluido un diseño personalizado.
Para crear un AlertDialog:
// 1. Instantiate anAlertDialog.Builderwith its constructor AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); // 2. Chain together various setter methods to set the dialog characteristics builder.setMessage(R.string.dialog_message) .setTitle(R.string.dialog_title); // 3. Get theAlertDialogfromcreate()AlertDialog dialog = builder.create();
Los siguientes temas muestran cómo definir varios atributos de diálogo usando la clase
AlertDialog.Builder.
Agregar botones
Para agregar botones de acción como los de la figura 2,
llama a los métodos setPositiveButton() y
setNegativeButton():
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Add the buttons
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User clicked OK button
}
});
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
}
});
// Set other dialog properties
...
// Create the AlertDialog
AlertDialog dialog = builder.create();
Los métodos set...Button() requieren un título para el botón (proporcionado
por un recurso de string) y un
DialogInterface.OnClickListener que defina la acción que se realizará
cuando el usuario presione el botón.
Hay tres botones de acción diferentes que puedes agregar:
- Positivo
- Debes usar este botón para aceptar y continuar con la acción (la acción "Aceptar").
- Negativo
- Debes usar este botón para cancelar la acción.
- Neutral
- Debes usar este botón cuando el usuario no quiera continuar con la acción, pero no necesariamente quiera cancelar. Aparece entre los botones positivo y negativo. Por ejemplo, la acción podría ser "Recordarme más tarde".
Puedes agregar un solo tipo de botón a un AlertDialog. Es decir, no puedes tener más de un botón "positivo".
Figura 3. Diálogo con un título y una lista.
Agregar una lista
Hay tres tipos de listas disponibles con las AlertDialog API:
- Una lista de opción única tradicional
- Una lista de opción única persistente (botones de selección)
- Una lista de opciones múltiples persistente (casillas de verificación)
Para crear una lista de opción única como la de la figura 3,
usa el método setItems():
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.pick_color)
.setItems(R.array.colors_array, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// The 'which' argument contains the index position
// of the selected item
}
});
return builder.create();
}
Debido a que la lista aparece en el área de contenido del diálogo,
en este no pueden mostrarse un mensaje y una lista, y debes proporcionar un título para el diálogo
con setTitle().
A fin de especificar los elementos para la lista, llama a setItems() y pasa una matriz.
Opcionalmente, puedes especificar una lista usando setAdapter(). Esto te permite respaldar la lista
con datos dinámicos (como de una base de datos) usando un ListAdapter.
Si decides respaldar tu lista con un ListAdapter,
usa siempre un Loader de modo que el contenido se cargue
de forma asincrónica. Esto se describe en más detalle en
Crear diseños
con un adaptador y en la guía Cargadores.
Nota: De forma predeterminada, al tocar un elemento de la lista se descarta el diálogo, a menos que estés usando una de las siguientes listas de opción persistentes.
Figura 4. Una lista con elementos de opciones múltiples.
Agregar una lista persistente de opción única u opciones múltiples.
Para agregar una lista de elementos de varias opciones (casillas de verificación) o
de una opción (botones de opción), usa los métodos
setMultiChoiceItems() o
setSingleChoiceItems(), respectivamente.
Por ejemplo, aquí te mostramos cómo puedes crear una lista de opciones múltiples como la que
se muestra en la figura 4, que guarda los elementos
seleccionados en una ArrayList:
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
mSelectedItems = new ArrayList(); // Where we track the selected items
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Set the dialog title
builder.setTitle(R.string.pick_toppings)
// Specify the list array, the items to be selected by default (null for none),
// and the listener through which to receive callbacks when items are selected
.setMultiChoiceItems(R.array.toppings, null,
new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which,
boolean isChecked) {
if (isChecked) {
// If the user checked the item, add it to the selected items
mSelectedItems.add(which);
} else if (mSelectedItems.contains(which)) {
// Else, if the item is already in the array, remove it
mSelectedItems.remove(Integer.valueOf(which));
}
}
})
// Set the action buttons
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
// User clicked OK, so save the mSelectedItems results somewhere
// or return them to the component that opened the dialog
...
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
...
}
});
return builder.create();
}
Si bien tanto una lista tradicional como una lista con botones de selección
proporcionan una acción de “opción única”, debes usar setSingleChoiceItems() si deseas conservar la selección del usuario.
Es decir, si al volver a abrir el diálogo posteriormente se debe indicar la selección actual del usuario,
debes crear una lista con botones de selección.
Crear un diseño personalizado
Figura 5: Diseño de diálogo personalizado.
Si deseas un diseño personalizado en un diálogo, crea un diseño y agrégalo a un
AlertDialog llamando a setView() en tu objeto AlertDialog.Builder.
De forma predeterminada, el diseño personalizado ocupa toda la ventana de diálogo, pero aún puedes
usar métodos AlertDialog.Builder para agregar botones y un título.
Por ejemplo, aquí te mostramos un archivo de diseño para el diálogo de la figura 5:
res/layout/dialog_signin.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:src="@drawable/header_logo"
android:layout_width="match_parent"
android:layout_height="64dp"
android:scaleType="center"
android:background="#FFFFBB33"
android:contentDescription="@string/app_name" />
<EditText
android:id="@+id/username"
android:inputType="textEmailAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="4dp"
android:hint="@string/username" />
<EditText
android:id="@+id/password"
android:inputType="textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="16dp"
android:fontFamily="sans-serif"
android:hint="@string/password"/>
</LinearLayout>
Sugerencia: Según la configuración predeterminada, cuando estableces un elemento EditText
para usar el tipo de entrada "textPassword", la familia de fuentes se establece en monospace, de modo que
puedas cambiarla a "sans-serif" para que ambos campos de texto usen
un estilo de fuente similar.
Para inflar el diseño en tu DialogFragment,
obtén un LayoutInflater con
getLayoutInflater() y llama a
inflate(), donde el primer parámetro
es el ID de recursos del diseño y el segundo una vista principal para el diseño.
Luego, puedes llamar a setView()
para disponer el diseño en el diálogo.
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
LayoutInflater inflater = getActivity().getLayoutInflater();
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
builder.setView(inflater.inflate(R.layout.dialog_signin, null))
// Add action buttons
.setPositiveButton(R.string.signin, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
// sign in the user ...
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
LoginDialogFragment.this.getDialog().cancel();
}
});
return builder.create();
}
Sugerencia: Si quieres un diálogo personalizado,
puedes mostrar un Activity como el diálogo
en lugar de usar las Dialog API. Simplemente, crea una actividad y fija su tema en
Theme.Holo.Dialog
en el elemento de manifiesto <activity>:
<activity android:theme="@android:style/Theme.Holo.Dialog" >
Eso es todo. Ahora la actividad se muestra en una ventana de diálogo en lugar de mostrarse en pantalla completa.
Traspasar eventos al host del diálogo
Cuando el usuario toca uno de los botones de acción del diálogo o selecciona un elemento de su lista,
tu DialogFragment debería realizar la
acción necesaria por sí mismo, pero a menudo querrás enviar el evento a la actividad o al fragmento que
abrió el diálogo. Para hacerlo, define una interfaz con un método para cada tipo de evento de clic.
Luego implementa esa interfaz en el componente de host que
recibirá los eventos de acción del diálogo.
Por ejemplo, aquí te mostramos un DialogFragment que define una
interfaz a través de la cual envía eventos de regreso a la actividad host:
public class NoticeDialogFragment extends DialogFragment {
/* The activity that creates an instance of this dialog fragment must
* implement this interface in order to receive event callbacks.
* Each method passes the DialogFragment in case the host needs to query it. */
public interface NoticeDialogListener {
public void onDialogPositiveClick(DialogFragment dialog);
public void onDialogNegativeClick(DialogFragment dialog);
}
// Use this instance of the interface to deliver action events
NoticeDialogListener mListener;
// Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Verify that the host activity implements the callback interface
try {
// Instantiate the NoticeDialogListener so we can send events to the host
mListener = (NoticeDialogListener) activity;
} catch (ClassCastException e) {
// The activity doesn't implement the interface, throw exception
throw new ClassCastException(activity.toString()
+ " must implement NoticeDialogListener");
}
}
...
}
La actividad que aloja el diálogo crea una instancia del diálogo
con el constructor del fragmento de diálogo y recibe los eventos
del diálogo a través de una implementación de la interfaz NoticeDialogListener:
public class MainActivity extends FragmentActivity
implements NoticeDialogFragment.NoticeDialogListener{
...
public void showNoticeDialog() {
// Create an instance of the dialog fragment and show it
DialogFragment dialog = new NoticeDialogFragment();
dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
}
// The dialog fragment receives a reference to this Activity through the
// Fragment.onAttach() callback, which it uses to call the following methods
// defined by the NoticeDialogFragment.NoticeDialogListener interface
@Override
public void onDialogPositiveClick(DialogFragment dialog) {
// User touched the dialog's positive button
...
}
@Override
public void onDialogNegativeClick(DialogFragment dialog) {
// User touched the dialog's negative button
...
}
}
Debido a que la actividad host implementa el NoticeDialogListener (ejecutado
por el método de callback onAttach()
que se mostró anteriormente), el fragmento de diálogo puede usar los
métodos de callback de la interfaz para enviar eventos de clic a la actividad:
public class NoticeDialogFragment extends DialogFragment {
...
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Build the dialog and set up the button click handlers
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(R.string.dialog_fire_missiles)
.setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Send the positive button event back to the host activity
mListener.onDialogPositiveClick(NoticeDialogFragment.this);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Send the negative button event back to the host activity
mListener.onDialogNegativeClick(NoticeDialogFragment.this);
}
});
return builder.create();
}
}
Exhibir un diálogo
Cuando desees mostrar tu diálogo, crea una instancia de tu DialogFragment, llama a show(), y pasa el FragmentManager y un nombre de etiqueta
para el fragmento de diálogo.
Puedes obtener el FragmentManager llamando a
getSupportFragmentManager() desde
la FragmentActivity o getFragmentManager() desde un Fragment. Por ejemplo:
public void confirmFireMissiles() {
DialogFragment newFragment = new FireMissilesDialogFragment();
newFragment.show(getSupportFragmentManager(), "missiles");
}
El segundo argumento, "missiles", es un nombre de etiqueta único que el sistema usa para guardar
y restaurar el estado del fragmento cuando es necesario. La etiqueta también te permite obtener un controlador para
el fragmento llamando a findFragmentByTag().
Exhibir un diálogo en pantalla completa o como fragmento integrado
Podrías tener un diseño de IU en el que quieras que aparezca una parte de la IU como un diálogo en algunas
situaciones, pero como pantalla completa o fragmento integrado en otras (puede depender de si el
dispositivo tiene una pantalla grande o pequeña). La clase DialogFragment
te ofrece esta flexibilidad porque puede, de todos modos, comportarse como un Fragment integrable.
Sin embargo, en este caso no puedes usar AlertDialog.Builder
ni otros objetos Dialog para crear el diálogo. Si
deseas que el DialogFragment sea
integrable, debes definir la IU del diálogo en un diseño y luego cargar el diseño en el callback
onCreateView().
Este es un DialogFragment de ejemplo que puede aparecer como un
diálogo o un fragmento integrable (con un diseño llamado purchase_items.xml):
public class CustomDialogFragment extends DialogFragment {
/** The system calls this to get the DialogFragment's layout, regardless
of whether it's being displayed as a dialog or an embedded fragment. */
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout to use as dialog or embedded fragment
return inflater.inflate(R.layout.purchase_items, container, false);
}
/** The system calls this only when creating the layout in a dialog. */
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// The only reason you might override this method when using onCreateView() is
// to modify any dialog characteristics. For example, the dialog includes a
// title by default, but your custom layout might not need it. So here you can
// remove the dialog title, but you must call the superclass to get the Dialog.
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
}
Y aquí te mostramos código que decide si mostrar el fragmento como un diálogo o una IU en pantalla completa en función del tamaño de pantalla:
public void showDialog() {
FragmentManager fragmentManager = getSupportFragmentManager();
CustomDialogFragment newFragment = new CustomDialogFragment();
if (mIsLargeLayout) {
// The device is using a large layout, so show the fragment as a dialog
newFragment.show(fragmentManager, "dialog");
} else {
// The device is smaller, so show the fragment fullscreen
FragmentTransaction transaction = fragmentManager.beginTransaction();
// For a little polish, specify a transition animation
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
// To make it fullscreen, use the 'content' root view as the container
// for the fragment, which is always the root view for the activity
transaction.add(android.R.id.content, newFragment)
.addToBackStack(null).commit();
}
}
Para obtener más información acerca de cómo realizar transacciones con fragmentos, lee la guía Fragmentos.
En este ejemplo, el valor booleano mIsLargeLayout especifica si el dispositivo actual
debe usar el diseño grande de la app (y mostrar ese fragmento como un diálogo en lugar de
pantalla completa). La mejor manera de establecer este valor booleano es declarar un
valor de recurso booleano
con un valor de recurso alternativo para diferentes tamaños de pantalla. Por ejemplo, aquí hay dos
versiones del recurso booleano para diferentes tamaños de pantalla:
res/values/bools.xml
<!-- Default boolean values -->
<resources>
<bool name="large_layout">false</bool>
</resources>
res/values-large/bools.xml
<!-- Large screen boolean values -->
<resources>
<bool name="large_layout">true</bool>
</resources>
Luego, puedes inicializar el valor mIsLargeLayout durante el método
onCreate() de la actividad:
boolean mIsLargeLayout;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mIsLargeLayout = getResources().getBoolean(R.bool.large_layout);
}
Exhibir una actividad como un diálogo en pantallas grandes
En lugar de mostrar un diálogo como una IU de pantalla completa en pantallas pequeñas, puedes lograr
el mismo resultado mostrando una Activity como un diálogo en
pantallas grandes. El enfoque que elijas depende del diseño de tu app, pero
mostrar una actividad como diálogo generalmente es útil cuando tu app ya está diseñada para pantallas
pequeñas y quieres mejorar la experiencia en tablets al mostrar una actividad breve
como un diálogo.
Para mostrar una actividad como diálogo solo en pantallas grandes,
aplica el tema Theme.Holo.DialogWhenLarge
al elemento de manifiesto <activity>:
<activity android:theme="@android:style/Theme.Holo.DialogWhenLarge" >
Para obtener más información acerca de cómo diseñar tus actividades con temas, lee la guía Estilos y temas.
Descartar un diálogo
Cuando el usuario toca cualquiera de los botones de acción creados con un
AlertDialog.Builder, el sistema descarta el diálogo por ti.
El sistema también descarta el diálogo cuando el usuario toca un elemento en una lista de diálogo, excepto
cuando la lista usa botones de selección o casillas de verificación. De lo contrario, puedes descartar el diálogo manualmente
llamando adismiss() en tu DialogFragment.
En caso de que necesites realizar determinadas
acciones cuando el diálogo desaparezca, puedes implementar el método onDismiss() en tu DialogFragment.
También puedes cancelar un diálogo. Este es un evento especial que indica que el usuario
explícitamente abandonó el diálogo sin completar la tarea. Esto ocurre si el usuario presiona el botón
Atrás o toca la pantalla fuera del área de diálogo,
o si llamas explícitamente a cancel() en el Dialog (como en respuesta a un botón “Cancelar” en el diálogo).
Como se muestra en el ejemplo anterior, puedes responder al evento de cancelación implementando
onCancel() en tu clase DialogFragment.
Nota: El sistema llama a
onDismiss() con cada evento que
invoque el método callbackonCancel(). No obstante,
si llamas a Dialog.dismiss() o a DialogFragment.dismiss(),
el sistema llama a onDismiss() pero
no a onCancel(). Por lo tanto, generalmente debes
llamar a dismiss() cuando el usuario presiona el botón
positivo en tu diálogo para eliminar el diálogo de la vista.