Инженерное дело в Sprout: создание средства выбора месяца для Android
Опубликовано: 2020-06-26Примечание. Эта статья основана на версии Material Components 1.2.0-beta01 от 1 июня 2020 г.
За три с половиной года работы в небольшой команде Android в Sprout Social одной из главных вещей, которая мотивирует меня каждый день приходить на работу, является свобода и доверие со стороны нашей компании для решения проблемы любым способом, который мы считаем лучшим.
Свобода исследовать и исследовать множество различных решений проблемы, которую мы считаем необходимой, с учетом сроков доставки обновлений продукта, позволяет нам найти лучшее решение как для наших клиентов, так и для нашего программного обеспечения.
Одна из таких задач заключалась в создании компонента пользовательского интерфейса для нашей новой функции Mobile Reporting. Этот новый компонент представлял собой средство выбора месяца, которое позволяло нашим пользователям определять диапазон дат для аналитического отчета.
Отправной точкой, которую мы выбрали, была существующая библиотека компонентов материалов. Вместо того, чтобы начинать с нуля, эта библиотека активно поддерживается и соответствует спецификациям материалов. Используя эту библиотеку в качестве основы, мы, вероятно, могли бы уменьшить количество логики, которую нам пришлось бы писать самим.
В этой статье я расскажу, как мы подошли к этому процессу, некоторые уникальные факторы при создании приложения Sprout для Android, несколько «подводных камней», которые возникли (и были исправлены) в процессе, и что нужно знать, если вы работает над подобным проектом.
Введение
В выпуске Android Material Components 1.1.0 представлен новый компонент пользовательского интерфейса Date Picker. Одним из приятных дополнений этого нового MaterialDatePicker
по сравнению с AppCompat CalendarView
является возможность выбирать диапазон дат, используя представление календаря или поле ввода текста.
Старый AppCompat CalendarView был не очень гибким. Это был хороший компонент для ограниченного варианта использования, для решения которого он предназначался; то есть выбор одной даты и необязательных минимальной и максимальной дат, чтобы указать допустимую границу диапазона дат.
Новый MaterialDatePicker был построен с большей гибкостью, чтобы позволить использовать расширенные функциональные возможности поведения. Он работает через серию интерфейсов, которые можно реализовать для настройки и изменения поведения средства выбора.
Это изменение поведения выполняется во время выполнения с помощью набора функций шаблона построителя в классе MaterialDatePicker.Builder
.
Это означает, что мы можем расширить базовое поведение этого MaterialDatePicker
с помощью компонуемых компонентов интерфейса.
Примечание. Несмотря на то, что MaterialDatePicker
использует несколько различных компонентов, в этой статье мы рассмотрим только компонент выбора даты.
Выбор диапазона дат
Команда Sprout Social Android находилась в процессе создания нашего раздела аналитических отчетов.
Этот новый раздел позволит нашим пользователям выбирать набор фильтров и набор диапазонов дат, которые будут охватывать отчет.
MaterialDatePicker
поставлялся с некоторыми предварительно созданными компонентами, которые мы могли использовать для выполнения нашего варианта использования.
Для нашего наиболее распространенного случая, позволяющего пользователю выбирать диапазон дат, будет достаточно предварительно созданного MaterialDatePicker
:
С помощью этого блока кода мы получаем средство выбора даты, которое позволяет пользователям выбирать диапазон дат.
Ежемесячный выбор даты
Одним из отчетов Sprout Social, который имеет более уникальный выбор дат, является отчет о тенденциях Twitter.
Этот отчет отличается от других тем, что вместо того, чтобы разрешать какой-либо диапазон дат, он принудительно выбирает один месяц, а это означает, что пользователь может выбрать только март 2020 г. по сравнению с 3 марта по 16 марта 2020 г.
Наше веб-приложение обрабатывает это с помощью раскрывающегося поля формы:
У MaterialDatePicker
нет способа применить такое ограничение с помощью предварительно созданного средства выбора диапазона дат материала, которое обсуждалось в предыдущем разделе. К счастью, MaterialDatePicker состоит из составных частей, которые позволяют нам расширить поведение по умолчанию для нашего конкретного случая использования.
Поведение при выборе даты
MaterialDatePicker
использует DateSelector
в качестве интерфейса, используемого для логики выбора средства выбора.
Из Javadoc:
«Интерфейс для пользователей {@link MaterialCalendar<S>}
для управления тем, как Календарь отображает и возвращает выборки…»
Вы заметите, что MaterialDatePicker.Builder.dateRangePicker()
возвращает экземпляр RangeDateSelector
, который мы использовали в приведенном выше примере.
Этот класс представляет собой предварительно созданный селектор, который реализует DateSelector
.
Мозговой штурм ежемесячного выбора даты
Для нашего варианта использования мы хотели, чтобы наши пользователи выбирали весь месяц в качестве выбранного диапазона дат; например, май 2020 г., апрель 2020 г. и т. д.
Мы думали, что предварительно созданный RangeDateSelector
упомянутый выше, помог нам в этом. Компонент позволял пользователю выбирать диапазон дат и применять [нижнюю, верхнюю] границу .
Единственное, чего не хватало, так это способа принудительного выбора для автоматического выбора всего месяца. В поведении RangeDateSelector
по умолчанию пользователь выбирает дату начала и дату окончания.
Мы хотели, чтобы когда пользователь выбирает день в месяце, средство выбора автоматически выбирало весь месяц в качестве диапазона дат.
Решение, которое мы выбрали, состояло в том, чтобы расширить RangeDateSelector
а затем переопределить поведение выбора дня, чтобы вместо этого автоматически выбирать весь месяц.
К счастью, есть функция, которую мы можем переопределить из интерфейса DateSelector
: select(selection: Long)
.
Эта функция будет вызываться, когда пользователь выбирает день в средстве выбора, при этом выбранный день передается в миллисекундах UTC от эпохи.
Реализация поведения ежемесячного выбора даты
Реализация оказалась самой простой частью, так как у нас есть четкая функция, которую мы можем переопределить, чтобы получить желаемое поведение.
Основная логика будет такой:
- Пользователь выбирает день.
- Функция
select()
вызывается с выбранным днем в миллисекундах Long UTC от эпохи. - Найдите первый и последний день месяца из переданного нам дня.
- Позвоните в
super.select(1st of month)
иsuper.select(last day of month)
- Родительское поведение
RangeDateSelector
должно работать должным образом и выбирать месяц в качестве диапазона дат.
Собираем все вместе
Теперь, когда у нас есть Custom MonthRangeDateSelector
, мы можем настроить MaterialDatePicker
.
Чтобы продолжить пример, мы можем обработать результат выбора следующим образом:
Результат будет выглядеть так:
Гочки
Была только одна серьезная проблема, которая мешала прийти к этому решению.
Основными компонентами, использованными для создания нашего MonthRangeDateSelector
, были класс RangeDateSelector
и интерфейс DateSelector
. Версия библиотеки, использованная в этой статье (1.2.0-beta01), ограничивала доступ к этим двум файлам, чтобы препятствовать их расширению или реализации.
В результате, хотя мы смогли успешно скомпилировать наш новый MonthRangeDateSelector
, компилятор показал очень страшное предупреждение, чтобы отговорить нас от этого:
Один из способов скрыть это предупреждение компилятора — добавить @Suppress("RestrictedApi")
следующим образом:
Этот опыт показывает, что, несмотря на то, что библиотека компонентов материалов предоставила сообществу разработчиков Android несколько замечательных новых компонентов, работа над ней все еще продолжается.
Большой частью этой библиотеки является открытость для отзывов от сообщества Android! Обнаружив это ограничение видимости компонента, я открыл проблему в проекте Github и даже создал PR, чтобы немедленно решить ее.
Этот открытый цикл обратной связи между командой Material Components и сообществом Android обеспечивает отличное сотрудничество и результаты для всех.
Вывод
Новый MaterialDatePicker
обладает отличной готовой функциональностью, которая, вероятно, будет охватывать большинство случаев использования выбора даты.
Однако его лучшая часть по сравнению с чем-то вроде AppCompat CalendarView заключается в том, что он построен компонуемым способом. Следовательно, его можно легко расширить и изменить для конкретных случаев использования, тогда как в CalendarView
было бы гораздо сложнее реализовать такие вещи.
Специальная благодарность
Я хотел бы выделить некоторых людей, которые помогли рецензировать эту статью:
- Ник Раут (Github)
- Майк Вольфсон (Github)
- Райан Филлипс (LinkedIn)
- Лукас Меллерс (Github)
- Мит Патель (LinkedIn)