Wie kann man die Trefferfläche der Android-Schaltfläche vergrößern, ohne den Hintergrund zu skalieren?

79

Ich habe einen Knopf mit einem benutzerdefinierten Zeichen.

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

Das Zeichenbare ist ein Dreieck mit transparentem Hintergrund.

|\
| \
|__\

Ich finde es schwierig, auf diese Schaltfläche zu tippen. Erstens ist es relativ klein. Zweitens sind die transparenten Pixel nicht tippbar. Ich möchte die Zeichenfläche gleich groß halten, aber die Trefferfläche quadratisch machen, doppelt so groß wie das Dreieck.

_____________
|            |
|    |\      |
|    | \     |
|    |__\    |
|____________|
JoJo
quelle
1
Die Marge erhöhte auch die Trefferfläche nicht.
JoJo
Versuchen Sie es mit Padding. Es wird sicherlich funktionieren.
Arjun Vyas

Antworten:

66

Sie können die TouchDelegate-API verwenden.

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
quelle
1
Wie wird hier jemals ein Wert korrigiert? button.getHitRect (rect) hat keinen Rückgabewert!
Amitavk
@ amitav13 rectwird als Parameter übergeben.
not_again_stackoverflow
Hey, der 100-Wert, den du verwendest, sollte es ein DP-Wert sein, den ich aus dem Dimensionsordner erhalte? Wird sich der Wert je nach Dichte ändern?
j2emanue
@ j2emanue Das stimmt. Sie können es auch programmgesteuert berechnen stackoverflow.com/a/8399445/2523667
Nicolas
44

Sie möchten "Polsterung". Dadurch wird der Raum in der Ansicht platziert. Durch den Rand wird der Raum nach außen verschoben, wodurch die Trefferfläche nicht vergrößert wird.

 <Button
    android:layout_width="22dip"
    android:layout_height="23dip"
    android:background="@drawable/triangle"
    android:padding="10dp" />
kwahn
quelle
Ich habe hier auf Gerät und Emulator getestet, es funktioniert nicht. hast du das getestet
LiangWang
18
Das Auffüllen wird innerhalb von layout_width / layout_height hinzugefügt, nicht außerhalb. Somit ist dieser Button immer noch 22x23 groß. Also dieser hilft nicht.
Simon Warta
1
Das funktioniert super. Das Auffüllen vergrößerte den tippbaren Bereich wie gewünscht und ließ mein Bild in der gleichen Größe, wie es JoJo (der ursprüngliche Autor) verlangte.
Aljoschak
Warum diese Antwort so viele positive Stimmen bekam, ist klar, dass das Auffüllen von Pads den Inhalt innerhalb von Grenzen und nicht außerhalb von Grenzen hat?!
Farid
1
Nach meinem vorherigen Kommentar denke ich, dass dies auch den Hintergrund vergrößern wird. Wenn ja, können Sie den Hintergrund vielleicht in ein "Einschub" -Ziehelement einfügen. PS. Ich bevorzuge dieses als TouchDelegate, weil TouchDelegate sich mit dem Vorfahren anlegen muss und es komplizierter wird, wenn sich die untergeordnete Ansicht bewegt ...
Henry
24

Sie müssen a verwenden TouchDelegate, das in den API-Dokumenten als "Hilfsklasse" definiert ist, um Situationen zu behandeln, in denen eine Ansicht einen größeren Berührungsbereich als die tatsächlichen Ansichtsgrenzen haben soll.

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

Luxusmodus
quelle
@mxcl, ich sehe in Ihrem Kommentar nichts, was diese Antwort ungültig oder "falsch" machen würde.
DBM
7
Eine Erklärung TouchDelegateund ein Snippet-Code: developer.android.com/training/gestures/viewgroup.html#delegate
Brais Gabin
16

Ich bevorzuge diese Lösung:

Fügen Sie einfach einen positiven Abstand und einen negativen Rand in die Layout-Ressourcendatei ein:

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

Dies ist eine sehr kleine Änderung im Vergleich zur Verwendung von TouchDelegates. Ich möchte auch kein Runnable ausführen, um den anklickbaren Bereich in meinem Java-Code hinzuzufügen.

Eine Sache, die Sie in Betracht ziehen könnten, wenn die Ansicht pixelgenau sein sollte: Das Auffüllen kann nicht immer dem Rand entsprechen . Die Polsterung sollte immer genau wie angegeben vorhanden sein. Der Rand hängt von den umgebenden Ansichten ab und wird mit den Randwerten anderer Ansichten versetzt.

Timo Bähr
quelle
liebte dieses: D
Ayman Salah
Konnte das nicht zum Laufen bringen. Ich habe zwei Texte in einem LinearLayout nebeneinander und möchte, dass der rechte einen berührbaren Bereich hat, der etwas größer ist als der Text selbst ... aber wenn ich diese 2 zusätzlichen Zeilen verwende, wird mein Text versetzt, was nicht das Aussehen ist, das ich bin Going for
Kibi
@Kibi Das Einfügen einer einzelnen Ansicht (hier TextView) in eine LinearLayoutsollte nicht erforderlich sein. Haben Sie trotzdem versucht, diese beiden Zeilen in Ihre einzufügen LinearLayout? Wie bereits erwähnt, hängt der Rand von den umgebenden Ansichten ab.
Timo Bähr
Danke @ TimoBähr, aber (anfangs) wollte ich nicht, dass das gesamte LinearLayout anklickbar ist, da der Text auf der linken Seite nicht anklickbar sein sollte ... am Ende habe ich nur das ganze LinearLayout anklickbar gemacht und verrückt gemacht und natürlich das funktioniert.
Kibi
8

Basierend auf der Antwort habe ich eine Kotlin-Erweiterungsfunktion erstellt, um dabei zu helfen.

/**
 * 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
quelle
6

einfach durch Hinzufügen von Polstern zur Schaltfläche

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

Auf diese Weise nimmt Ihre Schaltfläche die Höhe und Breite des Dreiecksbilds an und fügt der Schaltfläche die 20-dp-Polsterung hinzu, ohne das Bild selbst zu dehnen. und wenn Sie eine minimale Breite oder eine minimale Höhe wünschen, können Sie die Tags minWidthund verwendenminHeight

Nitesh
quelle
4

Falls Sie die Datenbindung verwenden. Sie können einen Bindungsadapter verwenden.

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

und dann können Sie Attribute für solche Ansichten verwenden

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

Ich weiß nicht genau, was Sie unter "ohne auch den Hintergrund zu vergrößern" verstehen. Wenn Sie meinen, dass Sie nicht möchten, dass Ihr Zeichen gedehnt wird, können Sie auch Ihr Hintergrund-PNG verwenden und einen zusätzlichen transparenten Pixelrand hinzufügen. Das würde Ihre Trefferzone erhöhen, aber Ihr Bild nicht dehnen.

Wenn Sie jedoch meinen, dass Sie dieses Zeichen überhaupt nicht ändern möchten, besteht Ihre einzige Option darin, größere fest codierte Werte für Höhe und Breite zu verwenden.

FoamyGuy
quelle
Ich meine, ich brauche die Schaltflächengrafik, um X für Y zu erscheinen, aber die Trefferfläche muss X + 10 für Y + 10 sein. Ich glaube nicht, dass transparente Pixel zum Trefferbereich beitragen. Meine Grafik ist eine dreieckige Form. Wenn ich auf die transparenten Bereiche tippe, wird der onClick nicht ausgelöst.
JoJo
Ich weiß mit Sicherheit, dass transparente Pixel im Hintergrund img zur Trefferzone beitragen. Ich habe es schon einmal benutzt, um einen unsichtbaren Knopf zu machen. Vielleicht fügt es keine Größe hinzu, weil Sie die Höhe und Breite fest codiert haben? Vielleicht versuchen Sie wrap_content für diese.
FoamyGuy
Es ist komisch. Ich habe die Schaltfläche gesprengt, um meinen gesamten Bildschirm nur zum Testen auszufüllen. Wenn ich auf die undurchsichtigen Pixel tippe, druckt LogCat mein onClick-Protokoll aus. Wenn ich auf die transparenten Bereiche tippe, wird sie nicht registriert.
JoJo
Wenn Sie viel Druck auf den Bildschirm ausüben, sodass Ihre Fingeroberfläche größer als die der Schaltfläche ist, wird der onClick nicht ausgelöst. Ich muss vorsichtig auf die Schaltfläche tippen, um onClick auszulösen. Gibt es eine Eigenschaft, die dieses "Fettfingersyndrom" korrigiert?
JoJo
2

versuche es damit

  <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
quelle
Gefiel diese Idee .. sauber .. einfach .. Sie können mit Polsterung arbeiten und zeichnen, um in die Mitte zu passen .. schön ..
Guilherme Oliveira
1

Das hat bei mir funktioniert:

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

Eigentlich sollten Sie die 30 in Dip umwandeln oder einen Prozentsatz der Tastenhöhe machen.

mxcl
quelle
Muss erklärt werden. OP hat einen Ausschnitt aus Layout-XML für eine Schaltfläche veröffentlicht (die Teil der Layout-XML einiger Ansichten sein würde). Aber ich nehme an, dass die obige Methode mit der Schaltfläche verknüpft werden muss, nicht mit der Ansichtsklasse, die die Schaltfläche enthält? Wie ordnen Sie die obige Methode dieser Schaltfläche zu?
ToolmakerSteve
1

Ich brauchte nur das gleiche: klickbarer Bereich größer als das Bild der Schaltfläche. Ich habe es in FrameLayout eingewickelt, das größer als der Hintergrund ist, und das innere Button-Tag in TextView geändert. Dann habe ich einen Klick-Handler angewiesen, mit diesem FrameLayout zu arbeiten, nicht mit der inneren Textansicht. Alles funktioniert gut!

Dmitri Novikov
quelle
Obwohl es schrecklich ist, ist dies die beste Lösung, die Android bietet.
i_am_jorf
1

Sie können eine transparente Schaltfläche wie diese verwenden ...

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

Sie können die Größe der Schaltfläche nach Bedarf erhöhen oder verringern und eine enter code hereImageView auf der Schaltfläche verwenden.

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

Ich habe die Top-Level-Funktion dafür in Kotlin erstellt:

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
quelle
2
Sie könnten Viewstattdessen eine Erweiterungsfunktion für
Vadim Kotov
0

Zu diesem Zweck habe ich ImageButton verwendet. In der XML-Verteidigung sehen Sie diese beiden Attribute:

  • android: background - Ein Zeichen, das als Hintergrund verwendet werden kann.
  • android: scr - Legt ein Drawable als Inhalt dieser ImageView fest.

Wir haben also zwei Drawables: Hintergrund eins und Quelle. In Ihrem Beispiel ist die Quelle triagle (drawable / trianlge):

|\
| \
|__\

Und der Hintergrund ist quadratisch (zeichnbar / quadratisch):

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

Hier ist ein Beispiel für ImageButton xml:

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

Ergebnis:

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

Auch quadratisch zeichnbar, kann mehrere verschiedene Zustände haben (gedrückt, fokussiert). Und Sie können die Größe des Hintergrunds mit Hilfe von Polstern erweitern.

Nur für den Fall, hier ist ein Beispiel für einen Hintergrund, der über die Android Compat Actionbar gezeichnet werden kann:

<?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
quelle
Aus irgendeinem Grund wird mein src nicht verwendet, obwohl der Hintergrund nur transparent ist.
MLProgrammer-CiM
0

Sie können die Auffüllung auf die Ansicht einstellen, dh auf Ihre Schaltfläche.

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

Hoffe das funktioniert bei dir.

Mayank Bhatnagar
quelle
0

Wenn Sie also die richtige Antwort haben, fügen Sie dem berührbaren Bereich Polster hinzu, und vergrößern Sie Ihr Bild vom Hintergrund in srcCompact .

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

Um app: scrCompat zu verwenden, müssen Sie xmlns:app="http://schemas.android.com/apk/res-auto"Ihrem Haupt- / Elternelement in der XML hinzufügen .

Beispiel vollständig:

<?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
quelle
0

Das Hinzufügen von Polstern führte dazu, dass mein Kontrollkästchen nicht zentriert wurde. Das Folgende hat bei mir funktioniert, ich habe jedoch eine feste Größe für das Kontrollkästchen angegeben (gemäß meiner UI-Spezifikation). Die Idee war, links und nicht rechts Polsterung hinzuzufügen. Ich habe oben und unten Polster hinzugefügt, um das Touch-Ziel größer zu machen und das Kontrollkästchen weiterhin zentriert zu halten.

<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"
       />
U / min
quelle
0

Ich denke, die richtige Antwort sollte ImageButton anstelle von Button verwenden. Stellen Sie das zu zeichnende Dreieck als "src" des ImageButton ein und stellen Sie die gewünschte Größe (layout_width & layout_height) ein, um die Berührung zu vereinfachen, und setzen Sie den ScaleType auf center, um die Dreiecksgröße beizubehalten.

Henry
quelle
0

Ich denke ehrlich, der einfachste Weg ist folgender:

Angenommen, Ihre Bildgröße ist 26dp * 26dp und Sie möchten eine Trefferfläche von 30dp * 30dp. Fügen Sie Ihrem ImageButton / Button einfach einen Abstand von 2dp hinzu und stellen Sie die Dimension auf 30dp * 30dp ein


Original

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

Größerer Trefferbereich

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

Die einfachste Lösung wäre, die Anzahl width and heightIhrer Artikel (z. B. Bild, Schaltfläche usw.) zu erhöhen und hinzuzufügen padding. Der anklickbare Bereich ist also größer, sieht aber in der Originalgröße aus.

Beispiel:

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

Herzlichen Glückwunsch, Sie haben die gleiche Tastengröße erreicht, aber die doppelte anklickbare Größe!

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

Die Größe des Hintergrunds beträgt immer noch 22x26dp. Fügen Sie einfach den Rand hinzu.

Terence Lui
quelle