Android 时间选取器

若要为用户提供选择时间的方法,可以使用 TimePicker。 Android 应用通常将 TimePickerTimePickerDialog 一起使用以选择时间值 - 这有助于确保跨设备和应用程序实现一致的接口。 TimePicker 允许用户在 24 小时或 12 小时 AM/PM 模式下选择一天中的时间。 TimePickerDialog 是一个帮助程序类,用于将 TimePicker 封装在对话框中。

操作中时间选取器对话框的示例屏幕截图

概述

新式 Android 应用程序在 DialogFragment 中显示 TimePickerDialog。 这样,应用程序就可以将 TimePicker 作为弹出对话框显示或嵌入到活动中。 此外,DialogFragment 还管理对话框的生命周期和显示,从而减少必须实现的代码量。

本指南演示如何使用 TimePickerDialog,它包装在 DialogFragment 中。 当用户单击活动上的按钮时,示例应用程序将 TimePickerDialog 显示为模态对话框。 当用户设置了时间时,对话框会退出,处理程序会在“活动”屏幕上用所选时间更新 TextView

要求

本指南的示例应用程序面向 Android 4.1(API 级别 16)或更高版本,但也可用于 Android 3.0(API 级别 11 或更高版本)。 通过向项目添加 Android 支持库 v4 并更改一些代码,可以支持旧版 Android。

使用 TimePicker

此示例扩展了 DialogFragmentDialogFragment 的子类实现(下面称为 TimePickerFragment)承载并显示 TimePickerDialog。 首次启动示例应用时,它会在将用于显示所选时间的 TextView 上方显示“选取时间”按钮:

初始示例应用屏幕

单击“选取时间”按钮时,示例应用将启动 TimePickerDialog,如以下屏幕截图所示:

应用显示的默认时间选择器对话框的屏幕截图

TimePickerDialog 中,选择时间并单击“确定”按钮会导致 TimePickerDialog 调用方法 IOnTimeSetListener.OnTimeSet 此接口由宿主 DialogFragmentTimePickerFragment,如下所述)实现。 单击“取消”按钮会导致消除片段和对话框。

DialogFragment 按以下三种方式之一返回宿主活动的选定时间:

  1. 调用某个方法或设置某个属性 – 活动可以提供专门用于设置此值的属性或方法。

  2. 引发某个事件 – DialogFragment 可以定义当 OnTimeSet 被调用时将引发的事件。

  3. 使用 ActionDialogFragment 可以调用 Action<DateTime> 来在活动中显示时间。 活动将在实例化 DialogFragment 时提供 Action<DateTime

此示例将使用第三种技术,这要求活动向 DialogFragment 提供 Action<DateTime> 处理程序。

启动应用项目

启动名为 TimePickerDemo 的新 Android 项目(如果不熟悉如何创建 Xamarin.Android 项目,请参阅 Hello, Android 了解如何创建新项目)。

编辑 Resources/layout/Main.axml,并将其内容替换为以下 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>

这是一个基本的 LinearLayout,其中包含一个用于显示时间的 TextView 和一个打开 TimePickerDialog按钮。 请注意,此布局使用硬编码的字符串和维度使应用更简单、更易于理解 – 生产应用通常使用这些值的资源(如 DatePicker 代码示例中所示)。

编辑 MainActivity.cs,用以下代码替代其内容:

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);
        }
    }
}

生成并运行此示例时,应会看到类似于以下屏幕截图的初始屏幕:

初始应用屏幕

单击“选取时间”按钮不执行任何操作,因为 DialogFragment 尚未实现,无法显示 TimePicker 下一步是创建此 DialogFragment

扩展 DialogFragment

若要扩展 DialogFragment 以与 TimePicker 一起使用,必须创建派生自 DialogFragment 且实现 TimePickerDialog.IOnTimeSetListener 的子类。 将以下类添加到 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);
    }
}

这个 TimePickerFragment 类被分为较小的部分,这在下一部分中有说明。

DialogFragment 实现

TimePickerFragment 实现了多种方法:工厂方法、对话框实例化方法和 TimePickerDialog.IOnTimeSetListener 所需的 OnTimeSet 处理程序方法。

  • TimePickerFragment 必须是 DialogFragment 的子类。 它还实现了 TimePickerDialog.IOnTimeSetListener 接口(即提供所需的 OnTimeSet 方法):

    public class TimePickerFragment : DialogFragment, TimePickerDialog.IOnTimeSetListener
    
  • TAG 出于记录目的被初始化(MyTimePickerFragment 可以更改为要使用的任何字符串)。 timeSelectedHandler 操作被初始化到一个空委托,以防止空引用异常:

    public static readonly string TAG = "MyTimePickerFragment";
    Action<DateTime> timeSelectedHandler = delegate { };
    
  • NewInstance 工厂方法被调用以实例化新的 TimePickerFragment。 此方法采用用户在 TimePickerDialog 中单击“确定”按钮时调用的 Action<DateTime> 处理程序:

    public static TimePickerFragment NewInstance(Action<DateTime> onTimeSelected)
    {
        TimePickerFragment frag = new TimePickerFragment();
        frag.timeSelectedHandler = onTimeSelected;
        return frag;
    }
    
  • 显示片段时,Android 将调用 DialogFragment 方法 OnCreateDialog。 此方法会创建一个新的 TimePickerDialog 对象,并使用活动、回调对象(即 TimePickerFragment 的当前实例)和当前时间初始化该对象:

    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;
    }
    
  • 当用户更改 TimePicker 对话框中的时间设置时,将调用 OnTimeSet 方法。 OnTimeSet 会使用当前日期创建一个 DateTime 对象,并在用户选择的时间(小时和分钟)合并:

    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);
    
  • 这个 DateTime 对象会传递到在创建时向 TimePickerFragment 对象注册的 timeSelectedHandlerOnTimeSet 调用此处理程序以将活动的时间显示更新为所选时间(此处理程序在下一部分中实现):

    timeSelectedHandler (selectedTime);
    

显示 TimePickerFragment

实现 DialogFragment 后,可以使用 NewInstance 工厂方法实例化 DialogFragment 并通过调用 DialogFragment.Show 来显示它:

将以下方法添加到 MainActivity

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

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

TimeSelectOnClick 实例化 TimePickerFragment 后,它会为匿名方法创建并传入委托,该方法使用传入的时间值更新活动的时间显示。 最后,它会启动 TimePicker 对话框片段(通过 DialogFragment.Show)向用户显示 TimePicker

OnCreate 方法末尾,添加以下行以将事件处理程序附加到启动对话框的“选取时间”按钮:

timeSelectButton.Click += TimeSelectOnClick;

单击“选取时间”按钮时,TimeSelectOnClick 将被调用以向用户显示 TimePicker 对话框片段。

尝试一下!

生成并运行应用。 单击“选取时间”按钮时,TimePickerDialog 按活动的默认时间格式显示(在本例中为 12 小时 AM/PM 模式):

时间对话框以 AM/PM 模式显示

TimePicker 对话框中单击“确定”时,处理程序会用所选的时间更新活动的 TextView,然后退出:

Activity TextView 以 A/M 时间显示

接下来,在声明和初始化 is24HourFormat 后立即将以下代码行添加到 OnCreateDialog

is24HourFormat = true;

此更改强制使传递给 TimePickerDialog 构造函数的标志为 true,以便使用 24 小时模式,而不是宿主活动的时间格式。 再次生成并运行应用时,单击“选取时间”按钮,TimePicker 对话框现在以 24 小时格式显示:

TimePicker 对话框采用 24 小时格式

由于处理程序调用 DateTime.ToShortTimeString 以将时间打印到活动的 TextView,因此时间仍以默认的 12 小时 AM/PM 格式打印。

总结

本文介绍了如何通过 Android 活动将 TimePicker 小组件显示为弹出模态对话框。 它提供了一个示例 DialogFragment 实现并讨论了 IOnTimeSetListener 接口。 此示例还演示了 DialogFragment 如何与宿主活动交互以显示所选时间。