Создание и стилизация раскрывающихся меню на Android

Недавно мне пришлось реализовать настраиваемое раскрывающееся меню для своего приложения, и мне потребовалось много времени, чтобы понять, что нужно стилизовать и как это делать правильно, чтобы добиться того внешнего вида, к которому я стремился. Итак, в этой статье я расскажу, как настроить раскрывающееся раскрывающееся меню с помощью TextInputLayout и AutoCompleteTextView. Мы собираемся уйти от этого:

к этому:

Что представляют собой раскрывающиеся меню?

Открытые раскрывающиеся меню отображают текущий выбранный пункт меню над списком параметров. Некоторые варианты могут принимать вводимые пользователем данные. В Android эту функцию можно реализовать с помощью TextInputLayout вместе с вложенным AutocompleteTextView, которые являются компонентами библиотеки материалов Android. Импортируем библиотеку в наш проект:

implementation ‘com.google.android.material:material:1.4.0’

Я также собираюсь использовать V iewBinding в этом руководстве, поэтому убедитесь, что вы включили его в build.gradle вашего модуля, добавив следующее:

android {
   ...
   buildFeatures {
       viewBinding true
   }
}

Разработка базового макета

Начнем с объявления основного макета раскрывающегося меню материала.

<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/dateFilterContainer" style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:hint="@string/label">

    <AutoCompleteTextView
        android:id="@+id/datesFilterSpinner"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:inputType="none"
        tools:text="All Time"" />
</com.google.android.material.textfield.TextInputLayout>

Здесь мы просто добавляем TextInputLayout, который содержит AutoCompleteTextView, который будет действовать как раскрывающийся список. Обратите внимание на строку inputType="none", поскольку она сообщает AutoCompleteTextView, что мы не планируем вводить какие-либо текстовые данные вручную. Это в сочетании с настраиваемым стилем, который мы назначаем TextInputLayout, позволит AutoCompleteTextView работать как счетчик при нажатии.

Чтобы узнать больше о различных доступных стилях и параметрах настройки, взгляните на документацию Google Меню - Дизайн материалов.

Придаем нашему раскрывающемуся списку более индивидуальный вид

Начнем с цвета, который мы будем использовать для фона раскрывающегося списка.

<color name="pastel_orange_light">#FBE8DF</color>

Затем мы собираемся создать новый filter_spinner_dropdown_bg.xml рисованный объект и использовать следующий код, чтобы придать ему форму:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/pastel_orange_light" />
    <corners android:radius="20dp" />
</shape>

Последнее, что нам понадобится, это значок раскрывающегося списка, который будет помещен в конец TextInputLayout. Я использовал стрелку вниз из бесплатного набора значков с перьями. Хорошо, теперь, когда у нас есть все ресурсы, давайте продолжим оформление раскрывающегося списка.

Прежде всего, мы собираемся установить background нашего TextInputLayout в значение drawable, которое мы создали ранее.

Далее мы собираемся скруглить углы поля, расположенного вокруг TextInputLayout (из-за нашего выбранного стиля), изменив radius различных углов, а также установив boxStrokeWidth и boxStrokeWidthFocused на 0dp, поскольку мы не хотим ничего наброски в раскрывающемся списке.

Кроме того, давайте добавим наш настраиваемый значок стрелки раскрывающегося списка с помощью атрибута endIconDrawable, а также подкрашиваем его в соответствии с нашим стилем с помощью endIconTint.

Мы закончили стилизацию TextInputLayout, поэтому перейдем к AutoCompleteTextView. Здесь мы устанавливаем background="@null", чтобы он не перекрывал фон нашего TextInputLayout.

Затем мы указываем dropDownSelector drawable, который в этом случае будет таким же, как и drawable, который мы создали ранее.

DropDownSelector - это элемент рисования, который используется для выделения элемента при щелчке по нему.

В моем приложении я не хотел, чтобы что-то подобное было видно, поэтому я просто установил его таким же, как и наш фон для рисования.

Продолжая, добавление следующих строк просто ограничит текст одной строкой, чтобы все выглядело согласованно, а многоточие (...) будет добавлено в конце текста, когда он будет слишком длинным.

android:ellipsize="end"
android:maxLines="1"
android:singleLine="true"

Мы также собираемся добавить отступы сверху и снизу, поскольку по какой-то причине вложение AutoCompleteTextView в TextInputLayout заставляет его немного обрезаться до границ TextInputLayout.

Наконец, мы немного центрируем и стилизуем наш текст, и все. Ниже приведен окончательный код.

<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/typesFilterContainer" style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu"
    android:layout_width="wrap_content"
    android:layout_height="40dp"
    android:background="@drawable/filter_spinner_dropdown_bg"
    app:boxBackgroundColor="@color/pastel_orange_light"
    app:boxCornerRadiusBottomEnd="20dp"
    app:boxCornerRadiusBottomStart="20dp"
    app:boxCornerRadiusTopEnd="20dp"
    app:boxCornerRadiusTopStart="20dp"
    app:boxStrokeWidth="0dp"
    app:boxStrokeWidthFocused="0dp"
    app:endIconDrawable="@drawable/ic_arrow_down"
    app:endIconTint="@color/pastel_orange_txt_highlight">

    <AutoCompleteTextView
        android:id="@+id/typesFilter"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@null"
        android:fontFamily="@font/lato"       android:dropDownSelector="@drawable/filter_spinner_dropdown_bg"
        android:ellipsize="end"
        android:inputType="none"
        android:maxLines="1"
        android:paddingTop="10dp"
        android:paddingBottom="10dp"
        android:singleLine="true"
        android:text="All Types"
        android:textAlignment="center"
        android:textColor="@color/pastel_orange_txt_highlight"
        tools:ignore="LabelFor" />
</com.google.android.material.textfield.TextInputLayout>

Теперь у вас должно получиться что-то вроде этого:

Давайте посмотрим, как мы можем заполнить раскрывающийся список элементами дальше!

Указание элементов раскрывающегося списка

Чтобы установить элементы раскрывающегося списка для нашего меню, нам нужно использовать ArrayAdapter<>() и передать контекст, макет элемента и список элементов. В этом случае мы собираемся использовать предопределенный макет android.R.layout.simple_spinner_dropdown_item, поскольку он покроет наши потребности на данный момент, но вы также можете использовать собственный макет и настроить его еще больше в пользовательском подклассе ArrayAdapter. Давайте посмотрим на код:

val adapter = ArrayAdapter(
    requireContext(), android.R.layout.simple_spinner_dropdown_item, arraylistOf("All Types", "Assignments", "Exam", "Lab")
)
binding?.typesFilterSpinner?.setAdapter(adapter)
binding?.typesFilterSpinner?.setText("All Types")

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

Использование настраиваемого выпадающего фона с возможностью рисования

И снова мы воспользуемся нашим filter_spinner_dropdown_bg.xml drawable. Просто вызовите setDropDownBackgroundDrawable() в AutoCompleteTextView, и это будет для фона.

binding?.typesFilterSpinner.setDropDownBackgroundDrawable(
    ResourcesCompat.getDrawable(
        resources,
        R.drawable.filter_spinner_dropdown_bg,
        null
    )
)

Хорошо, мы наконец закончили со стилем нашего раскрывающегося списка. Теперь это должно выглядеть примерно так:

В нашей последней части мы увидим, как обрабатывать события щелчка по элементам!

Обработка событий щелчка по элементам

Чтобы получать уведомления при щелчке по раскрывающемуся элементу, мы собираемся просто определить наш собственный AdapterView.OnItemClickListener и использовать его в нашем AutoCompleteTextView.

binding?.typesFilterSpinner.onItemClickListener =
    AdapterView.OnItemClickListener { parent, view, position, id->
        // do something with the available information
    }

На этом мы завершаем этот урок, теперь у вас должно быть полностью функциональное и красивое выпадающее меню / счетчик. Если у вас есть какие-либо предложения, улучшения или комментарии в целом, сообщите мне об этом в комментариях, и я постараюсь их учесть. Удачного кодирования!