Как увеличить область нажатия кнопки Android без масштабирования фона?

82

У меня есть кнопка с настраиваемым рисунком.

<Button
    android:layout_width="22dip"
    android:layout_height="23dip"
    android:background="@drawable/triangle" />

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

|\
| \
|__\

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

_____________
|            |
|    |\      |
|    | \     |
|    |__\    |
|____________|
JoJo
источник
1
Маржа тоже не увеличивала площадь попадания.
JoJo
Попробуйте использовать Padding. Это обязательно сработает.
Arjun Vyas

Ответы:

68

вы можете использовать TouchDelegate API.

final View parent = (View) button.getParent();  // button: the view you want to enlarge hit area
parent.post( new Runnable() {
    public void run() { 
        final Rect rect = new Rect(); 
        button.getHitRect(rect); 
        rect.top -= 100;    // increase top hit area
        rect.left -= 100;   // increase left hit area
        rect.bottom += 100; // increase bottom hit area           
        rect.right += 100;  // increase right hit area
        parent.setTouchDelegate( new TouchDelegate( rect , button)); 
    } 
}); 
Моррис Лин
источник
1
Как здесь можно установить значение rect? button.getHitRect (rect) не имеет возвращаемого значения!
amitavk
@ amitav13 rectпередается в качестве параметра.
not_again_stackoverflow
эй, значение 100, которое вы используете, должно ли это быть значение dp, которое я получаю из папки размеров? будет ли значение меняться в зависимости от плотности, верно?
j2emanue
@ j2emanue Верно. Вы также можете вычислить его программно stackoverflow.com/a/8399445/2523667
Николас
45

Вы хотите "отступ". Он поместит пространство внутри представления. Margin выведет пространство снаружи, что не увеличит площадь попадания.

 <Button
    android:layout_width="22dip"
    android:layout_height="23dip"
    android:background="@drawable/triangle"
    android:padding="10dp" />
Кван
источник
Я тестировал здесь на устройстве и эмуляторе, он не работает. вы это тестировали?
LiangWang
18
Заполнение добавляется внутри layout_width / layout_height, а не снаружи. Таким образом, эта кнопка по-прежнему имеет размер 22x23. Так что это не помогает.
Саймон Варта
1
Это прекрасно работает. Заполнение расширило область касания по желанию и оставило мое изображение того же размера, что и просил JoJo (оригинальный автор).
Алешак
Почему этот ответ получил так много положительных отзывов, что ясно, что заполнение прокладками внутри границ, а не снаружи ?!
Фарид
1
Продолжая мой предыдущий комментарий, я думаю, что это также расширит фон. Если да, то, возможно, вы можете поместить фон внутрь «вставки», которую можно рисовать. PS. Я предпочитаю этот, чем TouchDelegate, потому что TouchDelegate нужно испортить с предком, и это становится более сложным, если дочернее представление перемещается ...
Генри
24

Вам необходимо использовать TouchDelegate, который определен в документации API как «Вспомогательный класс для обработки ситуаций, когда вы хотите, чтобы область касания представления была больше, чем его фактические границы представления».

http://developer.android.com/reference/android/view/TouchDelegate.html

LuxuryMode
источник
@mxcl, я не вижу в вашем комментарии ничего, что могло бы сделать этот ответ недействительным или "неправильным".
dbm
7
Объяснение TouchDelegateи фрагмент кода: developer.android.com/training/gestures/viewgroup.html#delegate
Brais Gabin 08
16

Я предпочитаю это решение:

Просто добавьте положительный отступ и отрицательный отступ внутри файла ресурсов макета:

<some.View
  ..
  android:padding="12dp"
  android:margin="-12dp"
  .. />

Это очень небольшое изменение по сравнению с использованием TouchDelegates. Я также не хочу запускать Runnable для добавления интерактивной области внутри моего Java-кода.

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

Тимо Бэр
источник
любил этого: D
Айман Салах
Не удалось заставить это работать. У меня есть два текста рядом друг с другом в LinearLayout, и я хочу, чтобы у правой стороны была сенсорная область немного больше, чем сам текст ... но использование этих двух дополнительных строк заставляет мой текст смещаться, что не так, как я собирается
Киби
@Kibi Не нужно вставлять одно представление (здесь TextView) в LinearLayout. Тем не менее, пробовали ли вы добавить эти две строчки в свой LinearLayout? Как было сказано ранее, маржа зависит от окружающих видов.
Timo Bähr
Спасибо @ TimoBähr, но (изначально) я не хотел, чтобы весь LinearLayout был интерактивным, так как текст слева не должен был быть кликабельным ... в конце концов, я просто взбесил весь LinearLayout Clickable и, конечно же, что работает.
Киби
8

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

/**
 * Increase the click area of this view
 */
fun View.increaseHitArea(dp: Float) {
    // increase the hit area
    val increasedArea = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().displayMetrics).toInt()
    val parent = parent as View
    parent.post {
        val rect = Rect()
        getHitRect(rect)
        rect.top -= increasedArea
        rect.left -= increasedArea
        rect.bottom += increasedArea
        rect.right += increasedArea
        parent.touchDelegate = TouchDelegate(rect, this)
    }
}

Уильям Рид
источник
6

просто добавив отступ к кнопке

 <Button
android:layout_width="wrap_contant"
android:layout_height="wrap_contant"
android:background="@drawable/triangle"
android:padding="20dp" />

Таким образом, ваша кнопка примет высоту и ширину треугольного изображения и добавит к кнопке отступ 20dp, не растягивая само изображение. и если вы хотите какую - то минимальная ширину или высоту некоторых hinimum вы можете использовать minWidthи minHeightтег

Нитеш
источник
5

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

@BindingAdapter("increaseTouch")
fun increaseTouch(view: View, value: Float) {
    val parent = view.parent
    (parent as View).post({
        val rect = Rect()
        view.getHitRect(rect)
        val intValue = value.toInt()
        rect.top -= intValue    // increase top hit area
        rect.left -= intValue   // increase left hit area
        rect.bottom += intValue // increase bottom hit area
        rect.right += intValue  // increase right hit area
        parent.setTouchDelegate(TouchDelegate(rect, view));
    });
}

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

increaseTouch="@{8dp}"
логарифм
источник
2

Я не знаю точно, что вы имеете в виду под «без увеличения возможности рисования фона». Если вы имеете в виду, что вы просто не хотите, чтобы ваш объект для рисования растягивался, тогда один из вариантов, который у вас есть, - взять фон png и добавить к нему дополнительную прозрачную границу пикселей. Это увеличит зону попадания, но не растянет изображение.

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

FoamyGuy
источник
Я имею в виду, что мне нужно, чтобы изображение кнопки отображалось X на Y, но чтобы область нажатия была X + 10 на Y + 10. Я не думаю, что прозрачные пиксели увеличивают область попадания. Моя графика имеет треугольную форму. Когда я нажимаю на прозрачные области, onClick не срабатывает.
JoJo
Я точно знаю, что прозрачные пиксели на фоне img добавят в зону попадания. Я использовал его раньше, чтобы сделать невидимую кнопку. Возможно, дело не в добавлении размера, потому что вы жестко запрограммировали высоту и ширину? Может быть, для этого попробуйте wrap_content.
FoamyGuy
Странно. Я взорвал кнопку, чтобы заполнить весь экран просто для тестирования. Когда я касаюсь непрозрачных пикселей, LogCat распечатывает мой журнал onClick. Когда я нажимаю на прозрачные области, это не регистрируется.
JoJo
Кроме того, если вы сильно нажимаете на экран, так что площадь вашего пальца больше, чем у кнопки, onClick не срабатывает. Мне нужно осторожно нажать кнопку, чтобы запустить onClick. Есть ли какое-нибудь свойство, которое регулирует этот «синдром толстого пальца»?
JoJo
2

попробуйте с этим

  <Button
        android:id="@+id/btn_profile"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_alignParentLeft="true"
        android:background="@android:color/transparent"
        android:drawableBottom="@drawable/moreoptionicon"
        android:onClick="click"
        android:paddingBottom="15dp"
        android:visibility="visible" />
Шайлеш
источник
Понравилась эта идея .. чистая .. простая .. вы можете работать с набивкой и растягивать, чтобы поместиться в центре .. красиво ..
Гильерме Оливейра
1

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

Томсон Комер
источник
1

Это сработало для меня:

public void getHitRect(Rect outRect) {
    outRect.set(getLeft(), getTop(), getRight(), getBottom() + 30);
}

Хотя на самом деле вам следует преобразовать 30 в опускание или сделать процент от высоты кнопки.

mxcl
источник
Требует объяснения. OP опубликовал фрагмент макета xml для кнопки (который будет частью макета некоторого представления xml). Но я предполагаю, что указанный выше метод должен быть связан с кнопкой, а не с классом представления, содержащим кнопку? Как вы связываете вышеуказанный метод с этой кнопкой?
ToolmakerSteve,
1

Мне просто нужно было то же самое: кликабельная область больше изображения кнопки. Я обернул его в FrameLayout размером больше, чем фоновый рисунок, и изменил внутренний тег Button на TextView. Затем я поручил обработчику кликов работать с этим FrameLayout, а не с внутренним TextView. Все работает хорошо!

Дмитрий Новиков
источник
Несмотря на то, что это ужасно, это лучшее решение, которое предлагает Android.
i_am_jorf 03
1

Вы можете использовать прозрачную кнопку вот так ...

<Button
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:background="@android:color/transparent" /> 

вы можете увеличить или уменьшить размер кнопки по своему усмотрению и использовать enter code hereImageView до кнопки ....

<ImageView
    android:layout_width="22dip"
    android:layout_height="23dip"
    android:background="@drawable/triangle" />
Ятендра Сен
источник
1

Я создал для этого функцию верхнего уровня в kotlin:

fun increaseTouchArea(view: View, increaseBy: Int) {
    val rect = Rect()
    view.getHitRect(rect)
    rect.top -= increaseBy    // increase top hit area
    rect.left -= increaseBy   // increase left hit area
    rect.bottom += increaseBy // increase bottom hit area
    rect.right += increaseBy  // increase right hit area
    view.touchDelegate = TouchDelegate(rect, view)
}
Игорь Конюхов
источник
2
ViewВместо этого вы можете создать функцию расширения
Вадим Котов
0

Для этого я использовал ImageButton. В определении xml вы увидите эти два атрибута:

  • android: background - чертеж для использования в качестве фона.
  • android: scr - Устанавливает возможность рисования в качестве содержимого этого ImageView.

Итак, у нас есть два чертежа: фоновый и исходный. В вашем примере источником будет треугольник (drawable / trianlge):

|\
| \
|__\

И фон квадратный (вытяжной / квадратный):

_____________
|            |
|            |
|            |
|            |
|____________|

Вот пример ImageButton xml:

<ImageButton
   ...
   android:src="@drawable/triangle"
   android:background="@drawable/square">

Результат:

_____________
|            |
|    |\      |
|    | \     |
|    |__\    |
|____________|

Также квадратный, может иметь несколько разных состояний (нажат, сфокусирован). И вы можете увеличить размер фона с помощью прокладок.

На всякий случай вот пример фона, который можно рисовать из панели действий Android Compat:

<?xml version="1.0" encoding="utf-8"?>    
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
    <item android:state_focused="true" android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/abc_list_selector_disabled_holo_dark" />
    <item android:state_focused="true" android:state_enabled="false" android:drawable="@drawable/abc_list_selector_disabled_holo_dark" />
    <item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/abc_list_selector_background_transition_holo_dark" />
    <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/abc_list_selector_background_transition_holo_dark" />
    <item android:state_focused="true" android:drawable="@drawable/abc_list_focused_holo" />
    <item android:drawable="@android:color/transparent" />
 </selector>
молокока
источник
По какой-то причине мой src не используется, даже если фон просто прозрачный.
MLProgrammer-CiM
0

Вы можете установить отступ для представления, то есть вашей кнопки.

<Button
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:background="@android:color/transparent"
        android:padding="15dp" />

Надеюсь, что это работает для вас.

Маянк Бхатнагар
источник
0

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

<Button
    android:layout_width="22dip"
    android:layout_height="23dip"
    android:padding="6dp"
    app:srcCompat="@drawable/triangle"/>

Чтобы использовать app: scrCompat, вам нужно добавить xmlns:app="http://schemas.android.com/apk/res-auto"к вашему основному / родительскому элементу в xml.

пример завершен:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="@dimen/height_btn_bottom_touch_area"
android:background="@color/bg_white"
android:clipToPadding="false"
android:elevation="7dp">

<android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:stateListAnimator="@animator/selected_raise"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" >

    <ImageView
        android:id="@+id/bottom_button_bg"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:contentDescription=
            "@string/service_request_background_contentDescription"
        android:elevation="1dp"
        android:padding="6dp"
        app:srcCompat="@drawable/btn_round_blue"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" >
    </ImageView>

    <TextView
        android:id="@+id/bottom_button_text"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:elevation="1dp"
        android:gravity="center"
        android:text="@string/str_continue_save"
        android:textColor="@color/text_white"
        android:textSize="@dimen/size_text_title"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
Канато
источник
0

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

<CheckBox
        android:id="@+id/checkbox"
        android:layout_width="30dp"
        android:layout_height="45dp"
        android:paddingLeft="15dp"
        android:paddingTop="15dp"
        android:paddingBottom="15dp"
        android:button="@drawable/checkbox"
        android:checked="false"
        android:gravity="center"
       />
Об / мин
источник
0

Я думаю, что правильный ответ должен использовать ImageButton вместо Button. Задайте для вытягиваемого треугольника значение «src» ImageButton и установите размер (layout_width и layout_height), как вы хотите, для более легкого касания, и установите ScaleType в центр, чтобы сохранить размер треугольника.

Генри
источник
0

Я честно считаю, что самый простой способ: -

Скажем, размер вашего изображения составляет 26dp * 26dp, и вам нужна область попадания 30dp * 30dp, просто добавьте отступ 2dp к вашему ImageButton / Button и установите размер 30dp * 30dp


Оригинал

<ImageButton
    android:layout_width="26dp"
    android:layout_height="26dp"
    android:src="@drawable/icon"/>

Большая зона поражения

<ImageButton
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:padding="2dp"
    android:src="@drawable/icon_transcript"/>
daisura99
источник
0

Самым простым решением было бы увеличить размер width and heightвашего элемента (например, изображения, кнопки и т. Д.) И добавитьpadding ; Таким образом, интерактивная область больше, но выглядит в исходном размере.

Пример:

<Button
    android:layout_width="40dp"
    android:layout_height="40dp"
    android:padding="8dp"
    android:background="@drawable/triangle" />

Поздравляем, вы получили такой же размер кнопки, но в два раза больше, чем при нажатии!

_____________
|            |
|    |\      |
|    | \     |
|    |__\    |
|____________|
Unobatbayar
источник
-2
<Button
    android:layout_width="32dp"
    android:layout_height="36dp"
    android:layout_margin="5dp"
    android:background="@drawable/foo" 
/>

Размер фона по-прежнему 22x26dp. Просто добавьте маржу.

Теренс Луи
источник