Как добавить виджеты выбора в приложение

Попробуйте Compose
Jetpack Compose – это рекомендуемый набор инструментов для создания пользовательского интерфейса в Android. Узнайте, как добавлять компоненты в Compose.

В Android есть готовые диалоговые окна, с помощью которых пользователь может выбрать время или дату. Эти виджеты позволяют выбрать каждую часть времени (час, минута, AM/PM) или даты (месяц, день, год).

Пример окна выбора времени с сайта material.io
Рисунок 1. Выбор часа в мобильном календаре.

Использование этих средств выбора помогает пользователям выбрать дату или время, которые действительны, правильно отформатированы и адаптированы к языковому стандарту пользователя.

Пример модального окна выбора даты с сайта material.io
Рисунок 2. Модальное окно выбора даты.

Рекомендуем использовать DialogFragment для размещения каждого окна выбора даты или времени. DialogFragment управляет жизненным циклом диалогового окна и позволяет показывать окна выбора в разных конфигурациях макета, например в виде базового диалогового окна на телефонах или встроенной части макета на больших экранах.

Как создать окно выбора времени

Чтобы показать TimePickerDialog с помощью DialogFragment, определите класс фрагмента, который расширяет DialogFragment, и верните TimePickerDialog из метода onCreateDialog() фрагмента.

Расширение DialogFragment для выбора времени

Чтобы определить DialogFragment для TimePickerDialog, выполните следующие действия:

  • Определите метод onCreateDialog(), чтобы возвращать экземпляр TimePickerDialog.
  • Реализуйте интерфейс TimePickerDialog.OnTimeSetListener, чтобы получать обратный вызов, когда пользователь устанавливает время.

Рассмотрим пример.

Kotlin

class TimePickerFragment : DialogFragment(), TimePickerDialog.OnTimeSetListener {

    override fun onCreateDialog(savedInstanceState: Bundle): Dialog {
        // Use the current time as the default values for the picker.
        val c = Calendar.getInstance()
        val hour = c.get(Calendar.HOUR_OF_DAY)
        val minute = c.get(Calendar.MINUTE)

        // Create a new instance of TimePickerDialog and return it.
        return TimePickerDialog(activity, this, hour, minute, DateFormat.is24HourFormat(activity))
    }

    override fun onTimeSet(view: TimePicker, hourOfDay: Int, minute: Int) {
        // Do something with the time the user picks.
    }
}

Java

public static class TimePickerFragment extends DialogFragment
                            implements TimePickerDialog.OnTimeSetListener {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current time as the default values for the picker.
        final Calendar c = Calendar.getInstance();
        int hour = c.get(Calendar.HOUR_OF_DAY);
        int minute = c.get(Calendar.MINUTE);

        // Create a new instance of TimePickerDialog and return it.
        return new TimePickerDialog(getActivity(), this, hour, minute,
                DateFormat.is24HourFormat(getActivity()));
    }

    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        // Do something with the time the user picks.
    }
}

Информацию об аргументах конструктора можно найти в описании класса TimePickerDialog.

Теперь вам нужно событие, которое добавляет экземпляр этого фрагмента в объект activity.

Показать окно выбора времени

После того как вы определите DialogFragment, как в примере выше, вы можете отобразить окно выбора времени, создав экземпляр DialogFragment и вызвав метод show().

Например, вот кнопка, при нажатии на которую вызывается метод, показывающий диалоговое окно:

<Button
    android:id="@+id/pickTime"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Pick time" />

Когда пользователь нажимает на эту кнопку, система вызывает следующий метод:

Kotlin

findViewById<Button>(R.id.pickTime).setOnClickListener {
    TimePickerFragment().show(supportFragmentManager, "timePicker")
}

Java

findViewById<Button>(R.id.pickTime).setOnClickListener {
    TimePickerFragment().show(supportFragmentManager, "timePicker");
}

Этот метод вызывает show() для нового экземпляра DialogFragment, определенного в предыдущем примере. Метод show() требует экземпляр FragmentManager и уникальное название тега для фрагмента.

Как создать окно выбора даты

Создание DatePickerDialog похоже на создание TimePickerDialog. Разница заключается в диалоге, который вы создаете для фрагмента.

Чтобы показать DatePickerDialog с помощью DialogFragment, определите класс фрагмента, который расширяет DialogFragment, и верните DatePickerDialog из метода onCreateDialog() фрагмента.

Расширение DialogFragment для окна выбора даты

Чтобы определить DialogFragment для DatePickerDialog, выполните следующие действия:

  • Определите метод onCreateDialog(), чтобы возвращать экземпляр DatePickerDialog.
  • Реализуйте интерфейс DatePickerDialog.OnDateSetListener, чтобы получать обратный вызов, когда пользователь устанавливает дату.

Рассмотрим пример.

Kotlin

class DatePickerFragment : DialogFragment(), DatePickerDialog.OnDateSetListener {

    override fun onCreateDialog(savedInstanceState: Bundle): Dialog {
        // Use the current date as the default date in the picker.
        val c = Calendar.getInstance()
        val year = c.get(Calendar.YEAR)
        val month = c.get(Calendar.MONTH)
        val day = c.get(Calendar.DAY_OF_MONTH)

        // Create a new instance of DatePickerDialog and return it.
        return DatePickerDialog(requireContext(), this, year, month, day)

    }

    override fun onDateSet(view: DatePicker, year: Int, month: Int, day: Int) {
        // Do something with the date the user picks.
    }
}

Java

public static class DatePickerFragment extends DialogFragment
                            implements DatePickerDialog.OnDateSetListener {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current date as the default date in the picker.
        final Calendar c = Calendar.getInstance();
        int year = c.get(Calendar.YEAR);
        int month = c.get(Calendar.MONTH);
        int day = c.get(Calendar.DAY_OF_MONTH);

        // Create a new instance of DatePickerDialog and return it.
        return new DatePickerDialog(requireContext(), this, year, month, day);
    }

    public void onDateSet(DatePicker view, int year, int month, int day) {
        // Do something with the date the user picks.
    }
}

Информацию об аргументах конструктора можно найти в описании класса DatePickerDialog.

Вам нужно только событие, которое добавляет экземпляр этого фрагмента в объект activity.

Показать окно выбора даты

После того как вы определите DialogFragment, как в примере выше, вы можете отобразить окно выбора даты, создав экземпляр DialogFragment и вызвав show().

Например, вот кнопка, при нажатии на которую вызывается метод, показывающий диалоговое окно:

<Button
    android:id="@+id/pickDate"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Pick date"/>

Когда пользователь нажимает на эту кнопку, система вызывает следующий метод:

Kotlin

findViewById<Button>(R.id.pickDate).setOnClickListener {
    val newFragment = DatePickerFragment()
    newFragment.show(supportFragmentManager, "datePicker")
}

Java

findViewById<Button>(R.id.pickDate).setOnClickListener {
    val newFragment = DatePickerFragment();
    newFragment.show(supportFragmentManager, "datePicker");
}

Этот метод вызывает show() для нового экземпляра DialogFragment, определенного в предыдущем примере. Метод show() требует экземпляр FragmentManager и уникальное название тега для фрагмента.

Как использовать виджеты выбора с автозаполнением

В 2017 году в Android появился фреймворк автозаполнения, который позволяет пользователям сохранять данные для заполнения форм в разных приложениях. Окна выбора могут быть полезны в сценариях автозаполнения, предоставляя пользовательский интерфейс, который позволяет пользователям изменять значение поля, в котором хранятся данные о дате или времени. Например, в форме кредитной карты окно выбора даты позволяет пользователям вводить или изменять срок действия своей кредитной карты.

Поскольку виджеты выбора являются диалоговыми окнами, они не отображаются в действии вместе с другими полями. Чтобы показывать данные, когда окно выбора скрыто, можно использовать другой элемент, например EditText.

Объект EditText по умолчанию ожидает данные автозаполнения типа AUTOFILL_TYPE_TEXT. Сервисы автозаполнения сохраняют данные в виде AUTOFILL_TYPE_DATE, чтобы создать их подходящее представление. Чтобы решить проблему с несоответствием типов, мы рекомендуем создать специальное представление, которое наследует от EditText и реализует методы, необходимые для правильной обработки значений типа AUTOFILL_TYPE_DATE.

Чтобы создать подкласс EditText, который может обрабатывать значения типа AUTOFILL_TYPE_DATE, выполните следующие действия:

  1. Создайте класс, наследующий от EditText.
  2. Реализуйте метод getAutofillType(), который возвращает AUTOFILL_TYPE_DATE.
  3. Реализуйте метод getAutofillValue(), который возвращает объект AutofillValue, представляющий дату в миллисекундах. Чтобы создать возвращаемый объект, используйте метод forDate(), который генерирует объект AutofillValue.
  4. Реализуйте метод autofill(). Этот метод содержит логику для обработки параметра AutofillValue, который относится к типу AUTOFILL_TYPE_DATE. Чтобы обработать параметр, создайте его строковое представление, например mm/yyyy. Используйте строковое представление, чтобы задать свойство text вашего представления.
  5. Реализуйте функцию, которая показывает окно выбора, когда пользователь хочет изменить дату в пользовательском подклассе EditText. Представление обновляет свойство text, используя строковое представление значения, которое пользователь выбирает в окне выбора.

Пример подкласса EditText, который обрабатывает значения AUTOFILL_TYPE_DATE, можно найти в примере фреймворка автозаполнения на Java или Kotlin.

Подробнее о том, как обеспечить поддержку автозаполнения для пользовательских представлений…