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


82

У мене є кнопка зі спеціальним малюнком.

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

Малюнок - трикутник із прозорим фоном.

|\
| \
|__\

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

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

1
Поле також не збільшило площу потрапляння.
JoJo

Спробуйте використовувати Padding. Це, безумовно, спрацює.
Арджун

Відповіді:


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
Як значення колись встановлюється як пряме тут? button.getHitRect (rect) не має поверненого значення!
amitavk

@ amitav13 rectпередається як параметр.
not_again_stackoverflow

Гей, значення 100, яке ти використовуєш, чи має це бути значенням dp, яке я отримую з папки розмірів? чи значення буде змінюватися залежно від щільності, чи не так?
j2emanue

@ j2emanue Правильно. Ви також можете обчислити це програмно stackoverflow.com/a/8399445/2523667
Ніколас

45

Ви хочете "додати" Це дозволить розмістити простір усередині подання. Поле поставить простір зовні, що не збільшить площу потрапляння.

 <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

Вам потрібно використовувати a TouchDelegate, який у документах API визначено як "Помічник класу для обробки ситуацій, коли ви хочете, щоб вигляд мав більшу площу дотику, ніж його фактичні межі перегляду"

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


@mxcl, я не бачу у вашому коментарі нічого, що могло б зробити цю відповідь недійсною або "неправильною".
dbm

7
Пояснення щодо TouchDelegateта код фрагмента: developer.android.com/training/gestures/viewgroup.html#delegate
Brais Gabin

16

Я віддаю перевагу такому рішенню:

Просто додайте позитивне заповнення та негативне розмічання всередині файлу ресурсу макета:

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

Це дуже незначна зміна порівняно з використанням TouchDelegates. Я також не хочу запускати Runnable для додавання інтерактивної області всередині мого коду Java.

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


любив цього: D
Айман Салах

Не вдалося змусити це спрацювати. У мене є два тексти поруч один з одним у LinearLayout, і я хочу, щоб у правої руки була область, яку можна торкнутися, трохи більшу, ніж сам текст ... але використання цих 2-х додаткових рядків робить мій текст компенсованим, і це не той вигляд, яким я є збирається
Кібі

@Kibi Вставляти єдиний вигляд (тут TextView) у файл LinearLayoutне потрібно. Тим не менше, ви намагалися додати ці два рядки до свого LinearLayout? Як зазначалося раніше, поле залежить від оточуючих поглядів.
Timo Bähr

Дякую @ TimoBähr, але (спочатку) я не хотів, щоб весь LinearLayout був клікабельним, оскільки текст зліва не повинен був бути clickkablr ... врешті-решт, я роздратував і звів весь 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 відступ до кнопки, не розтягуючи саме зображення. а якщо вам потрібна якась мінімальна ширина або якась мінімальна висота, ви можете використовувати теги 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 і додати до нього додаткову прозору піксельну межу. Це збільшить зону потрапляння, але не розтягне зображення.

Якщо, однак, ви маєте на увазі, що ви взагалі не хочете змінювати те, що можна малювати, тоді я думаю, що єдиним вашим варіантом є використання більших жорстко закодованих значень висоти та ширини.


Я маю на увазі, що мені потрібна графіка кнопки, щоб вона відображалася 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" />

Сподобалась ця ідея .. чиста .. проста .. ти можеш працювати з підбивками та витяжними, щоб поміститися в центрі .. приємно ..
Guilherme Oliveira

1

Див. Чи можу я програмно збільшити кнопки на області кліку? . Це здається мені найпростішим способом напевно. Навіть включає трішки про анімацію лише власне об’єкта, який можна натиснути, дозволяючи фону отримувати кліки. Незабаром я застосую це до власного коду.


1

Це спрацювало для мене:

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

Хоча насправді вам слід перевести 30, щоб занурити або зробити відсоток від висоти кнопки.


Потрібне пояснення. OP опублікував фрагмент макета xml для кнопки (який буде частиною макета xml деякого подання). Але я припускаю, що вищезазначений метод повинен бути пов'язаний з кнопкою, а не з класом подання, який містить кнопку? Як ви пов’язуєте вищезазначений метод із цією кнопкою?
ToolmakerSteve

1

Мені просто потрібно було те саме: клікабельна область, більша за зображення кнопки. Я обернув його у FrameLayout розміром більше, ніж фон, що малюється, і змінив внутрішній тег Button на TextView. Потім я призначив обробник клацань для роботи з цим FrameLayout, а не з внутрішнім TextView. Все працює добре!


Незважаючи на жах, це найкраще рішення, яке пропонує Android.
i_am_jorf

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 зображення, яке можна малювати.

Отже, у нас є два малюнки: перший фон та джерело. У вашому прикладі джерелом буде трикутник (малювальний / триангель):

|\
| \
|__\

А фон квадратний (для малювання / квадрат):

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

Ось приклад 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 І встановіть розмір 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"/>

0

Найпростішим рішенням буде збільшення width and heightвашого товару (наприклад, зображення, кнопки тощо) та додаванняpadding ; Таким чином, клікабельна область більша, але виглядає в оригінальному розмірі.

Приклад:

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

Вітаємо! Ви досягли того самого розміру кнопки, але вдвічі більше, ніж можна натиснути!

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

-2
<Button
    android:layout_width="32dp"
    android:layout_height="36dp"
    android:layout_margin="5dp"
    android:background="@drawable/foo" 
/>

Розмір фону все ще 22x26dp. Просто додайте поля.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.