Làm thế nào để tăng diện tích nhấn của nút Android mà không cần mở rộng nền?


82

Tôi có một nút có thể kéo tùy chỉnh.

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

Có thể vẽ là một hình tam giác với nền trong suốt.

|\
| \
|__\

Tôi thấy nút này khó nhấn. Đầu tiên, nó tương đối nhỏ. Thứ hai, các pixel trong suốt không thể chạm được. Tôi muốn giữ nguyên kích thước có thể vẽ được, nhưng làm cho vùng đánh trúng có kích thước hình vuông gấp đôi kích thước của hình tam giác.

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

1
Margin cũng không làm tăng diện tích đánh.
JoJo

Hãy thử sử dụng Padding. Nó chắc chắn sẽ hoạt động.
Arjun Vyas

Câu trả lời:


68

bạn có thể sử dụng 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)); 
    } 
}); 

1
Làm thế nào để một giá trị được đặt thành trực tiếp ở đây? button.getHitRect (trực tràng) không có giá trị trả về!
amitavk

@ amitav13 rectđang được chuyển dưới dạng tham số.
not_again_stackoverflow

Này, giá trị 100 mà bạn đang sử dụng, nó có phải là giá trị dp mà tôi nhận được từ thư mục kích thước không? Giá trị sẽ thay đổi tùy thuộc vào mật độ phải không?
j2emanue 23/07/17

@ j2emanue Đúng vậy. Bạn cũng có thể tính toán nó theo chương trình stackoverflow.com/a/8399445/2523667
Nicolas

45

Bạn muốn "đệm" Nó sẽ đặt không gian bên trong khung nhìn. Margin sẽ đặt không gian bên ngoài, điều này sẽ không làm tăng diện tích đánh.

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

Tôi đã thử nghiệm ở đây trên thiết bị và trình giả lập, nó không hoạt động. bạn đã thử nghiệm cái này chưa?
LiangWang

18
Padding được thêm vào bên trong layout_width / layout_height, không phải bên ngoài. Do đó, Nút này vẫn lớn 22x23. Vì vậy, cái này không giúp được gì.
Simon Warta,

1
Điều này hoạt động tuyệt vời. Phần đệm đã mở rộng vùng có thể chỉnh sửa như mong muốn và giữ nguyên kích thước hình ảnh của tôi, giống như JoJo (tác giả gốc) đã yêu cầu.
Alyoshak

Tại sao câu trả lời này lại nhận được rất nhiều phiếu tán thành, rõ ràng là nội dung đệm lót bên trong đường viền chứ không phải bên ngoài ?!
Farid

1
Tiếp theo nhận xét trước đây của tôi, tôi nghĩ điều này cũng sẽ mở rộng nền. Nếu vậy, có lẽ bạn có thể đặt nền bên trong một cái có thể vẽ được. Tái bút. Tôi thích cái này hơn TouchDelegate, vì TouchDelegate cần phải gây rối với tổ tiên và nó sẽ phức tạp hơn nếu chế độ xem trẻ em đang di chuyển ...
Henry

24

Bạn cần sử dụng a TouchDelegate, được định nghĩa trong tài liệu API là "Lớp người trợ giúp để xử lý các tình huống trong đó bạn muốn một chế độ xem có vùng cảm ứng lớn hơn giới hạn chế độ xem thực tế của nó"

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


@mxcl, tôi không thấy bất kỳ điều gì trong nhận xét của bạn khiến câu trả lời này không hợp lệ hoặc "sai".
dbm

7
Giải thích về TouchDelegatevà một đoạn mã: developer.android.com/training/gestures/viewgroup.html#delegate
Brais Gabin

16

Tôi thích giải pháp này hơn:

Chỉ cần thêm một phần đệm tích cực và một phần đệm tiêu cực bên trong tệp tài nguyên bố cục:

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

Đây là một thay đổi rất nhỏ so với việc sử dụng TouchDelegates. Tôi cũng không muốn chạy Runnable để thêm vùng có thể nhấp vào bên trong mã Java của mình.

Một điều bạn có thể cân nhắc nếu chế độ xem phải là pixel hoàn hảo: phần đệm không phải lúc nào cũng bằng với lề . Phần đệm phải luôn ở đó chính xác như đã chỉ định. Các lề phụ thuộc vào quan điểm xung quanh và sẽ được bù đắp bằng lợi nhuận giá trị quan điểm khác.


yêu này một: D
Ayman Salah

Không thể làm cho điều này hoạt động. Tôi có hai văn bản bên cạnh nhau trong LinearLayout và muốn văn bản bên phải có vùng có thể chạm được lớn hơn văn bản một chút ... nhưng việc sử dụng 2 dòng thừa này làm cho văn bản của tôi bị lệch, không giống như tôi. đi cho
Kibi

@Kibi Việc chèn một chế độ xem (tại đây TextView) vào một chế độ xem LinearLayoutkhông cần thiết. Tuy nhiên, bạn đã thử thêm hai dòng này vào của mình LinearLayoutchưa? Như đã nói trước khi ký quỹ phụ thuộc vào quan điểm xung quanh.
Timo Bähr

Cảm ơn @ TimoBähr nhưng (ban đầu) tôi không muốn toàn bộ LinearLayout có thể nhấp được, vì văn bản ở bên trái không được cho là clickablr ... cuối cùng tôi đã phát điên lên và phát điên lên toàn bộ LinearLayout Clickable và tất nhiên là làm.
Kibi

8

Dựa trên câu trả lời, tôi đã tạo một hàm mở rộng kotlin để trợ giúp việc này.

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

đơn giản bằng cách thêm phần đệm vào nút

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

Vì vậy, bằng cách làm điều này .. nút của bạn sẽ lấy chiều cao và chiều rộng của hình ảnh tam giác và sẽ thêm phần đệm 20dp vào nút mà không kéo dài hình ảnh. và nếu bạn muốn một số chiều rộng tối thiểu hoặc một số chiều cao tối thiểu, bạn có thể sử dụng thẻ minWidthminHeight


5

Trong trường hợp bạn đang sử dụng Data Binding. Bạn có thể sử dụng bộ chuyển đổi ràng buộc.

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

và sau đó bạn có thể sử dụng thuộc tính trên các chế độ xem như thế này

increaseTouch="@{8dp}"

2

Tôi không biết chắc bạn muốn nói gì khi nói "mà không tăng nền có thể vẽ được." Nếu bạn muốn nói rằng bạn không muốn bản vẽ của mình bị kéo dài ra thì một tùy chọn bạn có là lấy png nền của bạn và thêm một đường viền pixel trong suốt vào đó. Điều đó sẽ làm tăng khu vực truy cập của bạn nhưng sẽ không kéo dài hình ảnh của bạn.

Tuy nhiên, nếu ý bạn là bạn không muốn thay đổi cái có thể vẽ đó thì tôi nghĩ lựa chọn duy nhất của bạn là sử dụng các giá trị được mã hóa cứng lớn hơn cho chiều cao và chiều rộng.


Ý tôi là tôi cần đồ họa nút xuất hiện X x theo Y nhưng có vùng truy cập là X + 10 x Y + 10. Tôi không nghĩ rằng các điểm ảnh trong suốt thêm vào vùng nhấn. Hình ảnh của tôi là một hình tam giác. Khi tôi nhấn vào các khu vực trong suốt, onClick không kích hoạt.
JoJo

Tôi biết chắc rằng các pixel trong suốt trong img nền sẽ thêm vào vùng truy cập. Tôi đã sử dụng nó trước đây để tạo một nút ẩn. Có lẽ nó không phải là thêm kích thước vì bạn đã khó mã hóa chiều cao và chiều rộng? Có thể thử wrap_content cho những cái đó.
FoamyGuy

Thật là kỳ lạ. Tôi đã làm nổ nút để lấp đầy toàn bộ màn hình của mình chỉ để thử nghiệm. Khi tôi nhấn vào các pixel mờ, LogCat sẽ in ra nhật ký onClick của tôi. Khi tôi nhấn vào các khu vực trong suốt, nó không đăng ký.
JoJo

Ngoài ra, nếu bạn đặt nhiều áp lực lên màn hình để diện tích bề mặt ngón tay của bạn lớn hơn diện tích của nút, onClick sẽ không kích hoạt. Tôi phải cẩn thận nhấn vào nút để kích hoạt onClick. Có tính chất nào điều chỉnh "hội chứng ngón tay béo" này không?
JoJo

2

thử với cái này

  <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" />

Thích ideia này .. sạch .. đơn giản .. bạn có thể làm việc với đệm và drawable để phù hợp tại trung tâm .. đẹp ..
Guilherme Oliveira


1

Điều này đã làm việc cho tôi:

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

Mặc dù thực sự, bạn nên chuyển đổi 30 thành nhúng hoặc làm một phần trăm chiều cao của nút.


Cần giải thích. OP đã đăng một đoạn mã của bố cục xml, cho Nút (sẽ là một phần của xml bố cục của một số chế độ xem). Nhưng tôi cho rằng phương thức trên cần được liên kết với Nút, không phải với lớp xem có chứa nút? Làm thế nào để bạn kết hợp phương pháp trên với nút đó?
ToolmakerSteve

1

Tôi chỉ cần giống nhau: khu vực có thể nhấp lớn hơn hình ảnh của nút. Tôi gói nó vào FrameLayout có kích thước lớn hơn so với nền có thể vẽ được và thay đổi thẻ Nút bên trong thành TextView. Hơn là tôi đã chỉ đạo một trình xử lý nhấp chuột làm việc với FrameLayout này, không phải với TextView bên trong. Tất cả đều hoạt động tốt!


Mặc dù rất kinh khủng nhưng đây là giải pháp tốt nhất mà Android cung cấp.
i_am_jorf

1

Bạn có thể sử dụng nút trong suốt như thế này ...

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

bạn có thể tăng hoặc giảm kích thước nút theo yêu cầu của mình và sử dụng enter code hereImageView lên nút ....

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

1

Tôi đã tạo hàm cấp cao nhất cho điều này trong 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
Bạn có thể tạo chức năng mở rộng cho Viewthay
Vadim Kotov

0

Vì mục đích này, tôi đã sử dụng ImageButton. Trong quá trình bảo vệ xml, bạn sẽ thấy hai thuộc tính sau:

  • android: background - Có thể vẽ để sử dụng làm nền.
  • android: scr - Đặt một nội dung có thể vẽ được làm nội dung của ImageView này.

Vì vậy, chúng tôi có hai có thể kéo: nền một và nguồn. Trong bạn ví dụ nguồn sẽ là triagle (drawable / trianlge):

|\
| \
|__\

Và nền là hình vuông (có thể vẽ / hình vuông):

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

Đây là ví dụ về ImageButton xml:

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

Kết quả:

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

Cũng có thể vẽ hình vuông, có thể có một số trạng thái khác nhau (được nhấn, tập trung). Và bạn có thể mở rộng kích thước có thể vẽ backgroud bằng cách sử dụng đệm.

Để đề phòng, đây là ví dụ về nền có thể vẽ từ Thanh tác vụ 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>

Vì lý do nào đó, src của tôi không được sử dụng, ngay cả khi nền chỉ trong suốt.
MLProgrammer-CiM

0

Bạn có thể đặt phần đệm thành dạng xem tức là nút của bạn.

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

Hy vọng điều này làm việc cho bạn.


0

Vì vậy, câu trả lời đúng, thêm phần đệm để vùng có thể chạm trở nên lớn hơn VÀ thay đổi hình ảnh của bạn từ nền thành srcCompact .

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

Để sử dụng app: scrCompat, bạn cần thêm xmlns:app="http://schemas.android.com/apk/res-auto"vào phần tử chính / cha của mình trong xml.

hoàn thành ví dụ:

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

Chỉ thêm phần đệm đã khiến hộp kiểm của tôi không được căn giữa. Điều sau đây phù hợp với tôi, tuy nhiên tôi đã chỉ định kích thước cố định cho hộp kiểm (theo thông số giao diện người dùng của tôi). Ý tưởng là thêm phần đệm vào bên trái chứ không phải bên phải. Tôi cũng đã thêm phần đệm vào đầu và cuối để làm cho mục tiêu cảm ứng lớn hơn và vẫn giữ hộp kiểm ở giữa.

<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

Tôi nghĩ câu trả lời chính xác nên sử dụng ImageButton thay vì Button. Đặt hình tam giác có thể vẽ làm "src" của ImageButton và đặt kích thước (layout_width & layout_height) như bạn muốn để dễ dàng chạm hơn và đặt ScaleType thành trung tâm để giữ lại kích thước hình tam giác.


0

Tôi thành thật nghĩ rằng cách dễ nhất là: -

Giả sử kích thước hình ảnh của bạn là 26dp * 26dp và bạn muốn diện tích lần truy cập là 30dp * 30dp, chỉ cần thêm khoảng đệm 2dp vào ImageButton / Button VÀ đặt kích thước thành 30dp * 30dp


Nguyên

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

Khu vực đánh lớn hơn

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

0

Giải pháp đơn giản nhất sẽ là tăng width and heighthạng mục của bạn (ví dụ: Hình ảnh, Nút, v.v.) và thêm padding; Vì vậy, khu vực có thể nhấp được lớn hơn nhưng trông ở kích thước ban đầu.

Thí dụ:

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

Xin chúc mừng, bạn đã đạt được cùng kích thước nút nhưng gấp đôi kích thước có thể nhấp!

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

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

Kích thước của nền vẫn là 22x26dp. Chỉ cần thêm lợi nhuận.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.