Selector de hora de Android

Para proporcionarle al usuario una manera de seleccionar una hora, puede usar TimePicker. Normalmente, las aplicaciones de Android usan TimePicker con TimePickerDialog para seleccionar un valor de tiempo, lo que ayuda a garantizar una interfaz coherente en los dispositivos y aplicaciones. TimePicker permite a los usuarios seleccionar la hora del día en el modo de 24 horas o 12 horas a. m./p. m. TimePickerDialog es una clase auxiliar que encapsula TimePicker en un cuadro de diálogo.

Example screenshot of the Time Picker Dialog in action

Información general

Las aplicaciones modernas de Android muestran TimePickerDialog un DialogFragment. Esto permite a una aplicación mostrar TimePicker como un cuadro de diálogo emergente o insertarlo en una actividad. Además, DialogFragment administra el ciclo de vida y la visualización del cuadro de diálogo, lo que reduce la cantidad de código que se debe implementar.

En esta guía se muestra cómo usar TimePickerDialog, encapsulado en un DialogFragment. La aplicación de ejemplo muestra TimePickerDialog como un cuadro de diálogo modal cuando el usuario hace clic en un botón en una actividad. Cuando el usuario establece la hora, el cuadro de diálogo se cierra y un controlador actualiza un TextView en la pantalla Actividad con la hora seleccionada.

Requisitos

La aplicación de ejemplo de esta guía tiene como destino Android 4.1 (nivel de API 16) o superior, pero se puede usar con Android 3.0 (nivel de API 11 o superior). Es posible admitir versiones anteriores de Android con la adición de la biblioteca de soporte técnico de Android v4 al proyecto y algunos cambios de código.

Uso de TimePicker

En este ejemplo se extiende DialogFragment; la implementación de subclase de DialogFragment (denominado TimePickerFragment a continuación) hospeda y muestra un TimePickerDialog. Cuando se inicia la aplicación de ejemplo por primera vez, muestra un botón SELECCIONAR HORA situado encima de un TextView que se usará para mostrar la hora seleccionada:

Initial sample app screen

Al hacer clic en el botón SELECCIONAR HORA, la aplicación de ejemplo inicia TimePickerDialog como se muestra en esta captura de pantalla:

Screenshot of default Time Picker dialog displayed by the app

En TimePickerDialog, seleccionar una hora y hacer clic en el botón Aceptar hace que TimePickerDialog invoque el método IOnTimeSetListener.OnTimeSet. Esta interfaz se implementa mediante el DialogFragment de hospedaje (TimePickerFragment, descrito a continuación). Al hacer clic en el botón Cancelar, el fragmento y el cuadro de diálogo se descartan.

DialogFragment devuelve la hora seleccionada a la actividad de hospedaje de una de estas tres maneras:

  1. Invocar un método o establecer una propiedad: la actividad puede proporcionar una propiedad o método específicamente para establecer este valor.

  2. Generar un evento: el DialogFragment puede definir un evento que se generará cuando se invoque OnTimeSet.

  3. Usar una Action: el DialogFragment puede invocar una Action<DateTime> para mostrar la fecha en la actividad. La actividad proporcionará la Action<DateTime al crear una instancia del DialogFragment.

Este ejemplo usará la tercera técnica, que requiere que la actividad proporcione un controlador de Action<DateTime> al DialogFragment.

Inicio de un proyecto de aplicación

Inicie un nuevo proyecto de Android llamado TimePickerDemo (si no está familiarizado con la creación de proyectos de Xamarin.Android, consulte Hello, Android para aprender a crear un proyecto).

Edite Resources/layout/Main.axml y reemplace su contenido por el siguiente XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center_horizontal"
    android:padding="16dp">
    <Button
        android:id="@+id/select_button"
        android:paddingLeft="24dp"
        android:paddingRight="24dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="PICK TIME"
        android:textSize="20dp" />
    <TextView
        android:id="@+id/time_display"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:paddingTop="22dp"
        android:text="Picked time will be displayed here"
        android:textSize="24dp" />
</LinearLayout>

Se trata de un elemento LinearLayout básico con un elemento TextView que muestra la hora y un botón que abre TimePickerDialog. Tenga en cuenta que este diseño usa cadenas y dimensiones codificadas de forma rígida para que la aplicación sea más sencilla y fácil de entender; una aplicación de producción normalmente usa recursos para estos valores (como se puede ver en el ejemplo de código de DatePicker).

Edite MainActivity.cs y reemplace el contenido por el código siguiente:

using Android.App;
using Android.Widget;
using Android.OS;
using System;
using Android.Util;
using Android.Text.Format;

namespace TimePickerDemo
{
    [Activity(Label = "TimePickerDemo", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        TextView timeDisplay;
        Button timeSelectButton;

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            SetContentView(Resource.Layout.Main);
            timeDisplay = FindViewById<TextView>(Resource.Id.time_display);
            timeSelectButton = FindViewById<Button>(Resource.Id.select_button);
        }
    }
}

Al compilar y ejecutar este ejemplo, debería ver una pantalla inicial similar a la siguiente captura de pantalla:

Initial App screen

Al hacer clic en el botón SELECCIONAR HORA, no sucede nada porque DialogFragment todavía no se ha implementado para mostrar TimePicker. El paso siguiente consiste en crear este DialogFragment.

Extensión de DialogFragment

Para ampliar DialogFragment para usarlo con TimePicker, es necesario crear una subclase derivada de DialogFragment y que implementa TimePickerDialog.IOnTimeSetListener. Agregue la siguiente clase a MainActivity.cs:

public class TimePickerFragment : DialogFragment, TimePickerDialog.IOnTimeSetListener
{
    public static readonly string TAG = "MyTimePickerFragment";
    Action<DateTime> timeSelectedHandler = delegate { };

    public static TimePickerFragment NewInstance(Action<DateTime> onTimeSelected)
    {
        TimePickerFragment frag = new TimePickerFragment();
        frag.timeSelectedHandler = onTimeSelected;
        return frag;
    }

    public override Dialog OnCreateDialog (Bundle savedInstanceState)
    {
        DateTime currentTime = DateTime.Now;
        bool is24HourFormat = DateFormat.Is24HourFormat(Activity);
        TimePickerDialog dialog = new TimePickerDialog
            (Activity, this, currentTime.Hour, currentTime.Minute, is24HourFormat);
        return dialog;
    }

    public void OnTimeSet(TimePicker view, int hourOfDay, int minute)
    {
        DateTime currentTime = DateTime.Now;
        DateTime selectedTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, hourOfDay, minute, 0);
        Log.Debug(TAG, selectedTime.ToLongTimeString());
        timeSelectedHandler (selectedTime);
    }
}

Esta clase TimePickerFragment se divide en partes más pequeñas y se explica en la sección siguiente.

Implementación de DialogFragment

TimePickerFragment implementa varios métodos: un Factory Method, un método de creación de instancias de diálogo y el método de controlador OnTimeSet requerido por TimePickerDialog.IOnTimeSetListener.

  • El objeto TimePickerFragment es una subclase de DialogFragment. También implementa la interfaz TimePickerDialog.IOnTimeSetListener (es decir, proporciona el método OnTimeSet necesario):

    public class TimePickerFragment : DialogFragment, TimePickerDialog.IOnTimeSetListener
    
  • TAG se inicializa con fines de registro (MyTimePickerFragment se puede cambiar a cualquier cadena que quiera usar). La acción timeSelectedHandler se inicializa en un delegado vacío para evitar excepciones de referencia nula:

    public static readonly string TAG = "MyTimePickerFragment";
    Action<DateTime> timeSelectedHandler = delegate { };
    
  • Se llama al Factory Method NewInstance para crear una instancia nueva de TimePickerFragment. Este método toma un controlador Action<DateTime> que se invoca cuando el usuario hace clic en el botón Aceptar en el TimePickerDialog:

    public static TimePickerFragment NewInstance(Action<DateTime> onTimeSelected)
    {
        TimePickerFragment frag = new TimePickerFragment();
        frag.timeSelectedHandler = onTimeSelected;
        return frag;
    }
    
  • Cuando se va a mostrar el fragmento, Android llama al método DialogFragmentOnCreateDialog. Este método crea un nuevo objeto TimePickerDialog y lo inicializa con la actividad, el objeto de devolución de llamada (que es la instancia actual de TimePickerFragment) y la hora actual:

    public override Dialog OnCreateDialog (Bundle savedInstanceState)
    {
        DateTime currentTime = DateTime.Now;
        bool is24HourFormat = DateFormat.Is24HourFormat(Activity);
        TimePickerDialog dialog = new TimePickerDialog
            (Activity, this, currentTime.Hour, currentTime.Minute, is24HourFormat);
        return dialog;
    }
    
  • Cuando el usuario cambia la configuración de hora en el cuadro de diálogo TimePicker, se invoca el método OnTimeSet. OnTimeSet crea un objeto DateTime con la fecha actual y combina la hora (hora y minuto) seleccionada por el usuario:

    public void OnTimeSet(TimePicker view, int hourOfDay, int minute)
    {
        DateTime currentTime = DateTime.Now;
        DateTime selectedTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, hourOfDay, minute, 0);
    
  • Este objeto DateTime se pasa al timeSelectedHandler que se registra con el objeto TimePickerFragment en la hora de creación. OnTimeSet invoca este controlador para actualizar la visualización de la hora de la actividad a la hora seleccionada (este controlador se implementa en la sección siguiente):

    timeSelectedHandler (selectedTime);
    

Visualización de TimePickerFragment

Ahora que DialogFragment se ha implementado, es el momento de crear una instancia de DialogFragment mediante el Factory Method NewInstance y mostrarlo invocando DialogFragment.Show:

Agregue el método siguiente a MainActivity:

void TimeSelectOnClick (object sender, EventArgs eventArgs)
{
    TimePickerFragment frag = TimePickerFragment.NewInstance (
        delegate (DateTime time)
        {
            timeDisplay.Text = time.ToShortTimeString();
        });

    frag.Show(FragmentManager, TimePickerFragment.TAG);
}

Después de que TimeSelectOnClick cree una instancia de TimePickerFragment, crea y pasa un delegado para un método anónimo que actualiza la visualización de la hora de la actividad con el valor de tiempo que se ha pasado. Por último, inicia el fragmento de diálogo TimePicker (a través de DialogFragment.Show) para mostrar TimePicker al usuario.

Al final del método OnCreate, agregue la siguiente línea para adjuntar el controlador de eventos al botón SELECCIONAR HORA que inicia el cuadro de diálogo:

timeSelectButton.Click += TimeSelectOnClick;

Cuando se haga clic en el botón SELECCIONAR HORA, se invocará TimeSelectOnClick para mostrar el fragmento de diálogo TimePicker al usuario.

¡Inténtelo!

Compile y ejecute la aplicación. Al hacer clic en el botón SELECCIONAR HORA, se muestra TimePickerDialog en el formato de hora predeterminado para la actividad (en este caso, el modo a. m./p. m. de 12 horas):

Time Dialog is displayed in AM/PM mode

Al hacer clic en Aceptar en el cuadro de diálogo TimePicker, el controlador actualiza el elemento TextView de la actividad con la hora elegida y, a continuación, se cierra:

A/M time is displayed in the Activity TextView

A continuación, agregue la siguiente línea de código a OnCreateDialog inmediatamente después de que is24HourFormat se declare e inicialice:

is24HourFormat = true;

Este cambio hace que la marca pasada al constructor TimePickerDialog sea true para que se use el modo de 24 horas en lugar del formato de hora de la actividad de hospedaje. Al compilar y ejecutar la aplicación de nuevo, haga clic en el botón SELECCIONAR HORA; el cuadro de diálogo TimePicker se muestra ahora en formato de 24 horas:

TimePicker dialog in 24-Hour format

Dado que el controlador llama a DateTime.ToShortTimeString para imprimir la hora en el elemento TextView de la actividad, la hora todavía se imprime en el formato predeterminado de 12 horas a. m./p. m.

Resumen

En este artículo, se ha explicado cómo mostrar un widget TimePicker como un cuadro de diálogo modal emergente desde una actividad de Android. Se proporcionó una implementación de DialogFragment de ejemplo y explicó la interfaz IOnTimeSetListener. En este ejemplo, también se ha mostrado cómo DialogFragment puede interactuar con la actividad de hospedaje para mostrar la hora seleccionada.