Skip to main content

Блог переїхав

Блог переїхав на HUGO . Нова адреса сайту  https://sheremetat.dev

Создание кнопок не квадратной формы в Android




В статье описан небольшой пример того, как сделать кнопки нестандартной формы в Android приложении. Например, это могут быть овальные кнопки или кнопки в виде дуг. Пример интерфейса такой задачи изображен на рисунке.Разместив  кнопки таким образом  они “перекроют” друг друга и в области перекрытия, нажав на одну кнопку, можно выполнить действие другой кнопки. Проблема также в том,  что нажатие на кнопке будет срабатывать за пределами области кнопки. Красные линии на рисунке показывают реальные контуры кнопки, а заштрихованная область - это область перекрытия кнопок. В центре круга и за его пределами (на белом фоне) нажатие на кнопке не должно срабатывать.

Для решение этой задачи мы сделаем следующее:

  1. Выделим Touch Area для каждой кнопки.
  2. Создадим кнопку, которая сможет управлять своей Touch Area.
  3. Проверим работоспособность созданной кнопки.




Создание Touch Area.
Теперь разобьем область срабатывания кнопки на несколько прямоугольных областей таким образом, чтобы они перекрывались (синие рамки на изображении). Каждой из этих областей мы назначим координаты положения и размер. В итоге мы получим четыре параметра для каждой области (x, y, width, height) и набор таких областей для каждой кнопки. Добавим эти наборы в наше Android приложение в файл arrays.xml. Значения мы разместим в порядке  x, y, width, height и зазделим их одним пробелом.

И так, начнем. Допустим, на надо реализовать интерфейс изображенный на рисунке выше. У каждой кнопки область срабатывания выделена черной рамкой.

<resources>
  <array name="touch_area_btntop">
        <item>90 18 73 22</item>
        <item>58 33 135 34</item>
        <item>44 48 45 38</item>
        <item>167 48 45 38</item>
   </array>
   <array name="touch_area_btnleft">
        <item>25 22 49 69</item>
        <item>45 82 45 42</item>
        <item>72 94 51 53</item>
   </array>
   <array name="touch_area_btnright">
        <item>55 22 46 71</item>
        <item>58 33 48 34</item>
        <item>5 81 47 51</item>
   </array>
</resources>

На этом этапе подготовительную работу можно считать завершенной. Переходим к самому интересному.

Создание кнопки.
Устанавливать Touch Area кнопке мы будем с помощю атрибутов в XML файле:

toucharea:toucharea="@array/touch_area_btntop"

Для этого создадим новый аттрибут и опишем его в arrts.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="TouchAreaButton">
        <attr name="toucharea" format="string"/>
    </declare-styleable>
</resources>


Теперь создадим свою кнопку. Она должна уметь прочитать атрибут Touch Area, создать эту область на кнопке и следить за тем, попало ли касание в эту область. Если касание не пришлось на область - не выполнять никаких действий с этой кнопкой.

Ниже привожу код кнопки. Все важные момент описаны в комментариях к коду.

public class RoundButton extends Button {
    private Region touchRegion = new Region();

    public RoundButton(Context context) {
        super(context);
    }

    public RoundButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initStyleButton(attrs);
    }

    public RoundButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        initStyleButton(attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            if (!touchRegion.contains((int) event.getX(), (int) event.getY())) {
                return false;
            }
        }
        return super.onTouchEvent(event);
    }

    private void initStyleButton(AttributeSet attrs) {
        TypedArray a = getContext().obtainStyledAttributes(attrs,
                R.styleable.TouchAreaButton);

        int areaId = a.getResourceId(R.styleable.TouchAreaButton_toucharea, 0);

        if (areaId == 0) {
            return;
        }

        Resources res = getResources();
        TypedArray areas = res.obtainTypedArray(areaId);

        for (int i = 0; i < areas.length(); i++) {
            String touchArea = areas.getString(i);
            Log.d(getClass().getCanonicalName(), "touchArea = " + touchArea);
            String[] data = touchArea.split(" ");
            if (data.length < 4)
                continue;
            
            // koef for convert px to dp
            float pxToDp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, res.getDisplayMetrics());
            
            int x = (int)(Integer.parseInt(data[0]) * pxToDp);
            int y = (int)(Integer.parseInt(data[1]) * pxToDp);
            int width = (int)(Integer.parseInt(data[2]) * pxToDp);
            int height = (int)(Integer.parseInt(data[3]) * pxToDp);
            Rect rect = new Rect(x, y, x + width, y + height);
            if (i == 0) {
                touchRegion.set(rect);
            } else {
                touchRegion.op(rect, Op.UNION);
            }
        }
    }
}

А вот так выглядит использование этой кнопки в приложении:
<name.sheremetat.sample.RoundButton
            android:id="@+id/button_top"
            toucharea:toucharea="@array/touch_area_btntop"
            android:layout_marginLeft="0dp"
            android:layout_marginTop="0dp"
            android:layout_width="250dp"
            android:layout_height="103dp"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:text="Up"
            android:textStyle="bold"
            android:textSize="24dp"
            android:background="@drawable/btn_top" />


На этом откланяюсь. Код проекта выложен на GitHub.