rubezh-TWnlenfritplesuk

Стилизация кнопки под Spinner в Android Oreo

Очень часто из-за какой-то маленькой хотелки дизайнера программисту приходится долго мучиться... Сегодня я расскажу Вам, как я переделывал кнопку в спиннер, чтоб удовлетворить "хотелки" дизайнера.

По задумке дизайнера нужно было реализовать спиннер c нестандартное поведением, а именно - по клику вывести диалоговое окно с заголовком, радиокнопками и с кнопками "СОХРАНИТЬ" и "ОТМЕНА".

filter category

Задумка дизайнера

Стандартный контрол позволяет видоизменить диалог выбора, но не достаточно радикально. Свойство spinnerMode задает вид - либо всплывающий список (MODE_DROPDOWN), либо модальное диалоговое окно с заголовком (MODE_DIALOG + свойство prompt для заголовка).

spinner mode

Режимы отображения спиннера в Android.

Но нам нужно диалоговое окно с заголовком и с перламутромыми пуговицами кнопками "ОТМЕНА" и "СОХРАНИТЬ". Так что возможностей стандартного контрола нам недостаточно. На просторах интернета встречается иной способ - вместо Spinner вывести контрол Button, привести его вид к Spinner-у и повесить обработчик - вывод кастомного диалогового окна... Элементарно!

<Button
android:id="@+id/spn_sort"
style="@style/Base.Widget.AppCompat.Spinner.Underlined"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Сортировка"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@color/grey_40" />

Но у этого способа есть один нюанс - на новой версии Android Oreo вид кнопки становится... мягко говоря не ожидаемым.

 button as spinner

Ожидаемый и получаемый вид (справа вид на Android Oreo).

После этого я решил подойти к изменению вида кнопки иным способом, я составил xml файл в папке drawable с бэкграундом, который имитирует вид радиокнопки. Вот мой файл со стилями drawable\btn_underlined:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<layer-list>
<item android:bottom="4dp" android:left="4dp" android:right="4dp" android:top="4dp">
<shape android:shape="rectangle">
<corners android:bottomLeftRadius="30dp" android:bottomRightRadius="30dp" android:radius="30dp" android:topLeftRadius="30dp" android:topRightRadius="30dp" />
<stroke android:width="2dp" android:color="@color/grey_own" />
<solid android:color="@color/white" />
<padding android:bottom="0dp" android:left="0dp" android:right="0dp" android:top="0dp" />
<size android:width="110dp" android:height="40dp" />
</shape>
</item>
</layer-list>
</item>
<item android:state_focused="true">
<layer-list>
<item android:bottom="4dp" android:left="4dp" android:right="4dp" android:top="4dp">
<shape android:shape="rectangle">
<corners android:bottomLeftRadius="30dp" android:bottomRightRadius="30dp" android:radius="30dp" android:topLeftRadius="30dp" android:topRightRadius="30dp" />
<stroke android:width="2dp" android:color="@color/grey_own" />
<solid android:color="@color/white" />
<padding android:bottom="0dp" android:left="0dp" android:right="0dp" android:top="0dp" />
<size android:width="110dp" android:height="40dp" />
</shape>
</item>
</layer-list>
</item>
<item android:state_checked="true">
<layer-list>
<item android:bottom="4dp" android:left="4dp" android:right="4dp" android:top="4dp">
<shape android:shape="rectangle">
<corners android:bottomLeftRadius="30dp" android:bottomRightRadius="30dp" android:radius="30dp" android:topLeftRadius="30dp" android:topRightRadius="30dp" />
<stroke android:width="2dp" android:color="@color/grey_own" />
<solid android:color="@color/grey_own" />
<padding android:bottom="0dp" android:left="0dp" android:right="0dp" android:top="0dp" />
<size android:width="110dp" android:height="40dp" />
</shape>
</item>
</layer-list>
</item>
<item>
<layer-list>
<item android:bottom="4dp" android:left="4dp" android:right="4dp" android:top="4dp">
<shape android:shape="rectangle">
<corners android:bottomLeftRadius="30dp" android:bottomRightRadius="30dp" android:radius="30dp" android:topLeftRadius="30dp" android:topRightRadius="30dp" />
<stroke android:width="2dp" android:color="@color/grey_own" />
<solid android:color="@color/transparent" />
<padding android:bottom="0dp" android:left="0dp" android:right="0dp" android:top="0dp" />
<size android:width="110dp" android:height="40dp" />
</shape>
</item>
</layer-list>
</item>
</selector>

Здесь у нас описано 4 состояния: кнопка нажата (state_pressed), с фокусом (state_focused), отмечена (state_checked) и наконец дефолтное состояние. Стиль до безобразия простой - прозрачный прямоугольник и полоска снизу :-)

Теперь изменим бэкграунд в разметке экрана:

<Button
android:id="@+id/button_spn_order"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textAlignment="textStart"
android:text="Сортировка"
android:drawableEnd="@drawable/ic_arrow_drop_down"
android:drawableRight="@drawable/ic_arrow_drop_down"
android:background="@drawable/btn_underlined"
android:gravity="center_vertical|start"/>

Тут стоит отметить одно свойство - textAllCaps, оно выключает изменение регистра текста, по-умолчанию в кнопке текст весь выводится в верхнем регистре.

button as spinner1Вот так - другое дело.

Теперь осталось описать обработку нажатия. Создадим метод showOrderDialog, который будет вызываться при клике на кнопку-спиннер Сортировка:

    private void showOrderDialog() {
        final Dialog dialog = new Dialog(FilterActivity.this);
        dialog.setContentView(R.layout.radio_button_dialog);
        final RadioGroup rg = (RadioGroup) dialog.findViewById(R.id.radio_group);
        String[] items = getResources().getStringArray(R.array.text_array_sort_news);
        for (int i = 0; i < items.length; i++) {
            RadioButton rb = new RadioButton(FilterActivity.this);
            rb.setText(items[i]);
            rg.addView(rb);
        }
        dialog.findViewById(R.id.button_cancel).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dialog.cancel();
            }
        });
        dialog.findViewById(R.id.button_apply).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                for (int i = 0; i < rg.getChildCount(); i++) {
                    RadioButton btn = (RadioButton) rg.getChildAt(i);
                    if (btn.isChecked()) {
                        orderId = i;
                        orderButton.setText(btn.getText());
                    }
                }
                dialog.dismiss();
            }
        });
        dialog.show();
    }

 В качестве строк заполнения у нас выступает string-array следующего вида:

  • По дате
  • По популярности

 После вызова метода на экран выводится следующий диалог:

radio button dialog

Выбор пунктов не приводит к закрытию диалога, в отличие от стандартного режима MODE_DIALOG. По кнопке Сохранить пользователь возвращается на экран фильтрации, поле orderId принимает значение номера выбранного пункта, текст кнопки-спиннера меняется на текст выбранного пункта.

Собственно вот и вся магия :-)

1 1 1 1 1 1 1 1 1 1 Рейтинг 100%

Метки: java, android

Печать E-mail

Добавить комментарий


Защитный код
Обновить