¿Cómo aumentar el área de impacto del botón de Android sin escalar el fondo?

82

Tengo un botón con un dibujo personalizado.

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

El dibujable es un triángulo con fondo transparente.

|\
| \
|__\

Encuentro este botón difícil de tocar. Primero, es relativamente pequeño. En segundo lugar, los píxeles transparentes no se pueden tocar. Me gustaría mantener el dibujable del mismo tamaño, pero hacer que el área de impacto tenga una forma cuadrada del doble del tamaño del triángulo.

_____________
|            |
|    |\      |
|    | \     |
|    |__\    |
|____________|
JoJo
fuente
1
El margen tampoco aumentó el área de impacto.
JoJo
Intente usar Padding. Seguramente funcionará.
Arjun Vyas

Respuestas:

68

puede utilizar la API TouchDelegate.

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)); 
    } 
}); 
Morris Lin
fuente
1
¿Cómo se puede establecer un valor en rect aquí? button.getHitRect (rect) no tiene valor de retorno.
amitavk
@ amitav13 rectse pasa como parámetro.
not_again_stackoverflow
oye, el valor de 100 que estás usando, ¿debería ser un valor dp que obtengo de la carpeta de dimensiones? ¿Cambiará el valor en función de la densidad, verdad?
j2emanue
@ j2emanue Eso es correcto. También puede calcularlo programáticamente stackoverflow.com/a/8399445/2523667
Nicolas
45

Quieres "relleno" Colocará el espacio dentro de la vista. El margen colocará el espacio afuera, lo que no aumentará el área de impacto.

 <Button
    android:layout_width="22dip"
    android:layout_height="23dip"
    android:background="@drawable/triangle"
    android:padding="10dp" />
kwahn
fuente
Probé aquí en el dispositivo y el emulador, no funciona. has probado esto?
LiangWang
18
El relleno se agrega dentro de layout_width / layout_height, no afuera. Por tanto, este botón sigue teniendo un tamaño de 22x23. Entonces este no ayuda.
Simon Warta
1
Esto funciona muy bien. El relleno expandió el área que se puede tocar según lo deseado y dejó mi imagen del mismo tamaño, que tal como lo pidió JoJo (el autor original).
Alyoshak
¿Por qué esta respuesta obtuvo tantos votos a favor, está claro que el contenido de las almohadillas de relleno está dentro de los bordes, no afuera?
Farid
1
Siguiendo con mi comentario anterior, creo que esto también ampliará el fondo. Si es así, tal vez puedas poner el fondo dentro de un elemento de diseño "insertado". PD. Prefiero este que TouchDelegate, porque TouchDelegate necesita meterse con el antepasado y se vuelve más complicado si la vista infantil se mueve ...
Henry
24

Debe utilizar a TouchDelegate, que se define en los documentos de la API como "Clase auxiliar para manejar situaciones en las que desea que una vista tenga un área táctil más grande que sus límites de vista reales".

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

Modo de lujo
fuente
@mxcl, no veo nada en su comentario que haga que esta respuesta sea inválida o "incorrecta".
dbm
7
Una explicación TouchDelegatey un fragmento de código: developer.android.com/training/gestures/viewgroup.html#delegate
Brais Gabin
16

Prefiero esta solución:

Simplemente agregue un relleno positivo y un margen negativo dentro del archivo de recursos de diseño:

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

Este es un cambio muy pequeño en comparación con el uso de TouchDelegates. Tampoco quiero ejecutar un Runnable para agregar el área en la que se puede hacer clic dentro de mi código Java.

Una cosa que podría considerar si la vista debe ser perfecta en píxeles: el relleno no siempre puede ser igual al margen . El acolchado siempre debe estar exactamente como se especifica. El margen depende de las vistas circundantes y se compensará con los valores de margen de otras vistas.

Timo Bähr
fuente
Me encantó este: D
Ayman Salah
No se pudo hacer que esto funcione. Tengo dos textos uno al lado del otro en un LinearLayout y quiero que el de la derecha tenga un área táctil un poco más grande que el texto en sí ... pero el uso de estas 2 líneas adicionales hace que mi texto se desplace, que no es el aspecto que tengo. yendo por
Kibi
@Kibi No debería ser necesario insertar una vista única (aquí TextView) en una LinearLayout. Sin embargo, ¿ha intentado agregar estas dos líneas en su LinearLayout? Como se dijo antes, el margen depende de las vistas circundantes.
Timo Bähr
Gracias @ TimoBähr, pero (inicialmente) no quería que se pudiera hacer clic en todo el LinearLayout, ya que se suponía que el texto de la izquierda no debía ser clickablr ... al final, me levanté y enojé todo el LinearLayout Clickable y, por supuesto, eso trabajos.
Kibi
8

Basado en la respuesta, creé una función de extensión kotlin para ayudar con eso.

/**
 * 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)
    }
}

William Reed
fuente
6

simplemente agregando relleno al botón

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

Entonces, al hacer esto ... su botón tomará la altura y el ancho de la imagen del triángulo y agregará el relleno de 20dp al botón sin estirar la imagen en sí. y si desea un ancho mínimo o una altura mínima, puede usar las etiquetas minWidthyminHeight

Nitesh
fuente
5

En caso de que esté utilizando Data Binding. Puede utilizar un adaptador de encuadernación.

@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));
    });
}

y luego puede usar atributos en vistas como esta

increaseTouch="@{8dp}"
logcat
fuente
2

No sé a ciencia cierta qué quiere decir con "sin aumentar también el diseño de fondo". Si quiere decir que simplemente no quiere que su dibujable se estire, una opción que tiene es tomar su png de fondo y agregarle un borde de píxel transparente adicional. Eso aumentaría su zona de impacto pero no estiraría su imagen.

Sin embargo, si quiere decir que no desea cambiar ese elemento de diseño en absoluto, creo que su única opción es usar valores codificados más grandes para la altura y el ancho.

FoamyGuy
fuente
Lo que quiero decir es que necesito que el gráfico del botón aparezca X por Y, pero que el área de impacto sea X + 10 por Y + 10. No creo que los píxeles transparentes se sumen al área de impacto. Mi gráfico es una forma triangular. Cuando toco las áreas transparentes, el onClick no se activa.
JoJo
Sé con certeza que los píxeles transparentes en la imagen de fondo se agregarán a la zona de impacto. Lo he usado antes para hacer un botón invisible. ¿Quizás no está agregando tamaño porque ha codificado la altura y el ancho? Tal vez intente wrap_content para esos.
FoamyGuy
Es extraño. Exploté el botón para llenar toda mi pantalla solo para probar. Cuando toco los píxeles opacos, LogCat imprime mi registro de onClick. Cuando toco las áreas transparentes, no se registra.
JoJo
Además, si ejerce mucha presión en la pantalla de manera que el área de la superficie de su dedo sea mayor que la del botón, el onClick no se dispara. Tengo que tocar con cuidado el botón para activar onClick. ¿Existe alguna propiedad que se ajuste a este "síndrome del dedo gordo"?
JoJo
2

prueba con esto

  <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" />
Shailesh
fuente
Me gustó esta ideia .. limpio .. simple .. se puede trabajar con relleno y se puede dibujar para que quepa en el centro .. agradable ..
Guilherme Oliveira
1

Esto funcionó para mí:

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

Aunque realmente deberías convertir el 30 en dip o hacer un porcentaje de la altura del botón.

mxcl
fuente
Necesita una explicación. OP publicó un fragmento de diseño xml, para un botón (que sería parte del xml de diseño de algunas vistas). Pero supongo que el método anterior debe estar asociado con el botón, no con la clase de vista que contiene el botón. ¿Cómo asocia el método anterior con ese botón?
ToolmakerSteve
1

Solo necesitaba lo mismo: un área en la que se puede hacer clic más grande que la imagen del botón. Lo envolví en FrameLayout de tamaño más grande que el fondo dibujable y cambié la etiqueta interna del botón a TextView. Luego, dirigí un controlador de clic para que funcionara con este FrameLayout, no con el TextView interno. ¡Todo funciona bien!

Dmitri Novikov
fuente
A pesar de ser horrible, esta es la mejor solución que ofrece Android.
i_am_jorf
1

Puedes usar un botón transparente como este ...

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

puede aumentar o disminuir el tamaño del botón según sus necesidades y usar un enter code hereImageView en el botón ....

<ImageView
    android:layout_width="22dip"
    android:layout_height="23dip"
    android:background="@drawable/triangle" />
Yatendra Sen
fuente
1

Creé la función de nivel superior para esto en 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)
}
Igor Konyukhov
fuente
2
Se puede crear la función de extensión de Viewlugar
Vadim Kotov
0

Para este propósito, estaba usando ImageButton. En defensa xml verá estos dos atributos:

  • android: background : un elemento de diseño para usar como fondo.
  • android: scr : establece un elemento de diseño como contenido de este ImageView.

Así que tenemos dos elementos de diseño: fondo uno y fuente. En su ejemplo, la fuente será triagle (dibujable / trianlge):

|\
| \
|__\

Y el fondo es cuadrado (dibujable / cuadrado):

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

Aquí hay un ejemplo de ImageButton xml:

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

Resultado:

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

También se puede dibujar en forma cuadrada, podría tener varios estados diferentes (presionado, enfocado). Y podría expandir el tamaño de diseño de fondo usando acolchados.

Por si acaso, aquí hay un ejemplo de fondo que se puede dibujar desde la barra de acción de 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>
molokoka
fuente
Por alguna razón, mi src no se está utilizando, incluso cuando el fondo es transparente.
MLProgrammer-CiM
0

Puede configurar el relleno para la vista, es decir, su botón.

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

Espero que esto funcione para usted.

Mayank Bhatnagar
fuente
0

Entonces, la respuesta correcta, agregue relleno al área táctil, se haga más grande Y cambie su imagen de fondo a srcCompact .

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

Para usar la aplicación: scrCompat, debe agregar xmlns:app="http://schemas.android.com/apk/res-auto"a su elemento principal / padre en el xml.

ejemplo completo:

<?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>
Canato
fuente
0

El solo hecho de agregar relleno estaba haciendo que mi casilla de verificación no estuviera centrada. Lo siguiente funcionó para mí, sin embargo, especifiqué un tamaño fijo para la casilla de verificación (según mi especificación de interfaz de usuario). La idea era agregar relleno a la izquierda y no a la derecha. También agregué relleno en la parte superior e inferior para hacer que el objetivo táctil sea más grande y mantener la casilla de verificación centrada.

<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"
       />
RPM
fuente
0

Creo que la respuesta correcta debería ser usar ImageButton en lugar de Button. Establezca el triángulo dibujable como "src" del ImageButton, y establezca el tamaño (layout_width & layout_height) como desee para un toque más fácil y establezca ScaleType en el centro para retener el tamaño del triángulo.

Enrique
fuente
0

Sinceramente, creo que la forma más fácil es la siguiente: -

Digamos que el tamaño de su imagen es 26dp * 26dp y desea un área de impacto de 30dp * 30dp, simplemente agregue un relleno de 2dp a su ImageButton / Button Y establezca la dimensión en 30dp * 30dp


Original

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

Área de impacto más grande

<ImageButton
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:padding="2dp"
    android:src="@drawable/icon_transcript"/>
daisura99
fuente
0

La solución más simple sería aumentar el tamaño width and heightde su artículo (por ejemplo, imagen, botón, etc.) y agregar padding; Por lo tanto, el área en la que se puede hacer clic es más grande pero se ve en el tamaño original.

Ejemplo:

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

¡Felicitaciones, logró el mismo tamaño de botón pero el doble del tamaño en el que se puede hacer clic!

_____________
|            |
|    |\      |
|    | \     |
|    |__\    |
|____________|
unobatbayar
fuente
-2
<Button
    android:layout_width="32dp"
    android:layout_height="36dp"
    android:layout_margin="5dp"
    android:background="@drawable/foo" 
/>

El tamaño del fondo sigue siendo 22x26dp. Solo agregue margen.

Terence Lui
fuente