Có ví dụ về cách sử dụng TouchDelegate trong Android để tăng kích thước mục tiêu nhấp chuột của chế độ xem không?


82

Tôi hiểu là khi bạn có một chế độ xem quá nhỏ để dễ dàng chạm vào, bạn phải sử dụng TouchDelegate để tăng vùng có thể nhấp cho chế độ xem đó.

Tuy nhiên, việc tìm kiếm các ví dụ sử dụng trên Google sẽ khiến nhiều người đặt câu hỏi nhưng lại có rất ít câu trả lời.

Có ai biết cách thích hợp để thiết lập ủy quyền cảm ứng cho chế độ xem, chẳng hạn như tăng vùng có thể nhấp của nó lên 4 pixel theo mọi hướng?


3
Dưới đây là một tốt thêm bài về cách sử dụng TouchDelegate: groups.google.com/group/android-developers/browse_thread/thread/...
Mason Lee

Câu trả lời:


100

Tôi đã hỏi một người bạn tại Google và họ có thể giúp tôi tìm ra cách sử dụng TouchDelegate. Đây là những gì chúng tôi nghĩ ra:

final View parent = (View) delegate.getParent();
parent.post( new Runnable() {
    // Post in the parent's message queue to make sure the parent
    // lays out its children before we call getHitRect()
    public void run() {
        final Rect r = new Rect();
        delegate.getHitRect(r);
        r.top -= 4;
        r.bottom += 4;
        parent.setTouchDelegate( new TouchDelegate( r , delegate));
    }
});

16
Điều gì sẽ xảy ra nếu có hai nút trên một màn hình mà bạn muốn thực hiện việc này? Nếu bạn đặt lạiTouchDelegate thì nó sẽ xóa đại biểu cảm ứng trước đó.
cottonBallPaws

2
Cái này không phù hợp với tôi ... Tôi giả sử ủy quyền là dạng xem mà tôi đang cố gắng thêm TouchDelegate vào.
Kevin Parker

20
@AmokraneChentir Tôi đã gặp vấn đề tương tự. Thay vì sử dụng TouchDelegates, tôi có một lớp mở rộng nút. Trong lớp này, tôi ghi đè getHitRect. Bạn có thể mở rộng hình chữ nhật lần truy cập đến kích thước của chế độ xem chính. public void getHitRect (Rect outRect) {outRect.set (getLeft () - mPadding, getTop () - mPadding, getRight () + mPadding, getTop () + mPadding); }
Brendan Weinstein

5
@Amokrane: Hóa ra giải pháp tôi đưa ra không hoạt động trong ICS. Tôi đã viết một bài đăng trên blog về hai chiến lược Touch Delegate khác nhau brendanweinstein.me/2012/06/26/… và bạn có thể nhận mã mẫu cho cả hai chiến lược tại github.com/brendanw/Touch-Delegates/blob/master/src/com/ brendan /…
Brendan Weinstein

8
Theo @ZsoltSafrany, một cách an toàn hơn để làm điều này là thông qua OnGlobalLayoutListener và chỉ định đại biểu onGlobalLayout ().
greg7gkb

20

Tôi đã có thể thực hiện điều này với nhiều chế độ xem (hộp kiểm) trên một bản vẽ màn hình phần lớn từ bài đăng blog này . Về cơ bản, bạn sử dụng giải pháp của emmby và áp dụng nó cho từng nút và phụ huynh của nó.

public static void expandTouchArea(final View bigView, final View smallView, final int extraPadding) {
    bigView.post(new Runnable() {
        @Override
        public void run() {
            Rect rect = new Rect();
            smallView.getHitRect(rect);
            rect.top -= extraPadding;
            rect.left -= extraPadding;
            rect.right += extraPadding;
            rect.bottom += extraPadding;
            bigView.setTouchDelegate(new TouchDelegate(rect, smallView));
        }
   });
}

Trong trường hợp của tôi, tôi có một khung lưới các chế độ xem hình ảnh với các hộp kiểm được phủ lên trên và gọi phương thức như sau:

CheckBox mCheckBox = (CheckBox) convertView.findViewById(R.id.checkBox1);
final ImageView imageView = (ImageView) convertView.findViewById(R.id.imageView1);

// Increase checkbox clickable area
expandTouchArea(imageView, mCheckBox, 100);

Làm việc tuyệt vời cho tôi.


11

Giải pháp này được đăng bởi @BrendanWeinstein trong phần bình luận.

Thay vì gửi một phương thức, TouchDelegatebạn có thể ghi đè getHitRect(Rect)phương thức của mình View(trong trường hợp bạn đang mở rộng một phương thức).

public class MyView extends View {  //NOTE: any other View can be used here

    /* a lot of required methods */

    @Override
    public void getHitRect(Rect r) {
        super.getHitRect(r); //get hit Rect of current View

        if(r == null) {
            return;
        }

        /* Manipulate with rect as you wish */
        r.top -= 10;
    }
}

Ý tưởng thú vị. Nhưng không có nghĩa là không thể tạo chế độ xem từ một bố cục? Không, chờ đã - nếu tôi thiết kế một lớp chế độ xem mới và sử dụng lớp đó trong bố cục. Điều đó có thể hoạt động. Đáng để thử.
Martin

10
@Martin đó là ý kiến ​​đúng. Thật không may, getHitRect không còn được gọi như ICS nữa bởi sendTouchEvent grepcode.com/file/repository.grepcode.com/java/ext/… Ghi đè getHitRect sẽ chỉ hoạt động cho bánh gừng trở xuống.
Brendan Weinstein

6

cách tiếp cận của emmby không hiệu quả với tôi nhưng sau một chút thay đổi, nó đã làm được:

private void initApplyButtonOnClick() {
    mApplyButton.setOnClickListener(onApplyClickListener);
    final View parent = (View)mApplyButton.getParent();
    parent.post(new Runnable() {

        @Override
        public void run() {
            final Rect hitRect = new Rect();
            parent.getHitRect(hitRect);
            hitRect.right = hitRect.right - hitRect.left;
            hitRect.bottom = hitRect.bottom - hitRect.top;
            hitRect.top = 0;
            hitRect.left = 0;
            parent.setTouchDelegate(new TouchDelegate(hitRect , mApplyButton));         
        }
    });
}

Có lẽ nó có thể tiết kiệm thời gian của ai đó


Đây là những gì làm việc cho tôi. nó thực sự khiến toàn bộ cha mẹ gửi các sự kiện nhấp chuột cho con. nó thậm chí không cần phải là cha mẹ trực tiếp (có thể là cha mẹ của cha mẹ).
nhà phát triển android

Điều đó có thể hoạt động nếu bạn có một chế độ xem để ủy quyền. điều gì sẽ xảy ra nếu bạn có, giả sử, 50 lượt xem để nâng cao vùng tiếp xúc? - Giống như trong Ảnh chụp màn hình này: plus.google.com/u/0/b/112127498414857833804/… - Vậy TouchDelegate có còn là giải pháp thích hợp không?
Martin

2

Theo bình luận của @Mason Lee, điều này đã giải quyết được vấn đề của tôi. Dự án của tôi có bố cục tương đối và một nút. Vì vậy, cha mẹ là -> bố cục và con là -> một nút.

Đây là một ví dụ liên kết google mã google

Trong trường hợp xóa câu trả lời rất có giá trị của anh ấy, tôi đặt ở đây câu trả lời của anh ấy.

Gần đây tôi đã được hỏi về cách sử dụng TouchDelegate. Bản thân tôi cũng hơi mệt mỏi về điều này và tôi không thể tìm thấy bất kỳ tài liệu nào tốt về nó. Đây là đoạn mã tôi đã viết sau một thời gian thử và sai. touch_delegate_view là một RelativeLayout đơn giản với id touch_delegate_root. Tôi đã xác định bằng một nút con duy nhất của bố cục, đó là nút Symbolated_button. Trong ví dụ này, tôi mở rộng vùng có thể nhấp của nút lên 200 pixel phía trên đầu nút của tôi.

public class TouchDelegateSample extends Activity {

  Button mButton;   
  @Override   
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.touch_delegate_view);
    mButton = (Button)findViewById(R.id.delegated_button);
    View parent = findViewById(R.id.touch_delegate_root);

    // post a runnable to the parent view's message queue so its run after
    // the view is drawn
    parent.post(new Runnable() {
      @Override
      public void run() {
        Rect delegateArea = new Rect();
        Button delegate = TouchDelegateSample.this.mButton;
        delegate.getHitRect(delegateArea);
        delegateArea.top -= 200;
        TouchDelegate expandedArea = new TouchDelegate(delegateArea, delegate);
        // give the delegate to an ancestor of the view we're delegating the
        // area to
        if (View.class.isInstance(delegate.getParent())) {
          ((View)delegate.getParent()).setTouchDelegate(expandedArea);
        }
      }
    });   
  } 
}

Chúc mừng, Justin Android Team @ Google

Vài lời từ tôi: nếu bạn muốn mở rộng phía bên trái, bạn cho giá trị bằng dấu trừ và nếu bạn muốn mở rộng phía bên phải của đối tượng, bạn cho giá trị bằng dấu cộng. Điều này hoạt động tương tự với trên và dưới.


Gợi ý kỳ diệu ở đây dường như là một nút - Bằng cách nào đó tôi có cảm giác rằng TouchDelegate không sử dụng được cho nhiều nút. Bạn có thể xác nhận điều đó?
Martin

1

Đó không phải là Ý tưởng tốt hơn khi đưa Padding cho thành phần cụ thể đó (Nút).


1
Điều đó sẽ làm cho xem lớn hơn. Điều gì sẽ xảy ra nếu bạn có một số văn bản không phải là nhãn nhấp chuột phía trên một nút và bạn muốn vùng văn bản đó được sử dụng để cải thiện độ chính xác của lần nhấp cho nút. - Thích trong Ảnh chụp màn hình này: plus.google.com/u/0/b/112127498414857833804/…
Martin

1

Đến bữa tiệc hơi muộn, nhưng sau nhiều nghiên cứu, bây giờ tôi đang sử dụng:

/**
 * Expand the given child View's touchable area by the given padding, by
 * setting a TouchDelegate on the given ancestor View whenever its layout
 * changes.
 */*emphasized text*
public static void expandTouchArea(final View ancestorView,
    final View childView, final Rect padding) {

    ancestorView.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                TouchDelegate delegate = null;

                if (childView.isShown()) {
                    // Get hitRect in parent's coordinates
                    Rect hitRect = new Rect();
                    childView.getHitRect(hitRect);

                    // Translate to ancestor's coordinates
                    int ancestorLoc[] = new int[2];
                    ancestorView.getLocationInWindow(ancestorLoc);

                    int parentLoc[] = new int[2];
                    ((View)childView.getParent()).getLocationInWindow(
                        parentLoc);

                    int xOffset = parentLoc[0] - ancestorLoc[0];
                    hitRect.left += xOffset;
                    hitRect.right += xOffset;

                    int yOffset = parentLoc[1] - ancestorLoc[1];
                    hitRect.top += yOffset;
                    hitRect.bottom += yOffset;

                    // Add padding
                    hitRect.top -= padding.top;
                    hitRect.bottom += padding.bottom;
                    hitRect.left -= padding.left;
                    hitRect.right += padding.right;

                    delegate = new TouchDelegate(hitRect, childView);
                }

                ancestorView.setTouchDelegate(delegate);
            }
        });
}

Điều này tốt hơn so với giải pháp được chấp nhận vì nó cũng cho phép một TouchDelegate được đặt trên bất kỳ Chế độ xem tổ tiên nào, không chỉ Chế độ xem gốc.

Không giống như giải pháp được chấp nhận, nó cũng cập nhật TouchDelegate bất cứ khi nào có sự thay đổi trong bố cục của Chế độ xem tổ tiên.


0

Nếu không muốn làm điều đó theo chương trình thì chỉ cần tạo vùng trong suốt xung quanh hình ảnh, Nếu bạn đang sử dụng hình ảnh làm nền cho nút (chế độ xem).

Vùng xám có thể trong suốt để tăng diện tích cảm ứng.

nhập mô tả hình ảnh ở đây


1
Điều đó sẽ không tạo ra không gian trống xung quanh sau đó xem mà không thể được sử dụng cho bất cứ điều gì khác? Điều gì sẽ xảy ra nếu bạn muốn hiển thị văn bản thông tin, hãy đóng một nút và sử dụng vùng văn bản để nâng cao vùng bấm vào nút?
Martin

Tôi không nói đó là cách tiếp cận tốt nhất và cuối cùng. Nhưng nó sẽ phù hợp với nhiều trường hợp bạn muốn hiển thị hình ảnh nút nhỏ nhưng vùng cảm ứng được mở rộng. Trong trường hợp của bạn, rất khó để ưu tiên cho nút khi bạn nhấp vào các chế độ xem khác (textview), tất cả cùng là thách thức khác nhau.
Vinayak Bevinakatti

0

Trong hầu hết các trường hợp, bạn có thể bao bọc chế độ xem yêu cầu vùng cảm ứng lớn hơn trong một chế độ xem không đầu khác (chế độ xem trong suốt nhân tạo) và thêm đệm / lề vào chế độ xem trình bao bọc và đính kèm nhấp / chạm ngay cả vào chế độ xem trình bao bọc thay vì chế độ xem ban đầu phải có một khu vực cảm ứng lớn hơn.


Điều đó sẽ không tạo ra không gian trống xung quanh sau đó xem mà không thể được sử dụng cho bất cứ điều gì khác? Điều gì sẽ xảy ra nếu bạn muốn hiển thị văn bản thông tin, hãy đóng một nút và sử dụng vùng văn bản để nâng cao vùng bấm vào nút?
Martin

@Martin, Nó phụ thuộc vào cách bạn làm điều đó. Và sẽ dễ dàng làm bất cứ điều gì bạn muốn - chỉ cần bọc bất cứ thứ gì bạn muốn (tuy nhiên bạn muốn) trong một chế độ xem trong suốt trên tất cả các chế độ xem này và chỉ định một cú nhấp chuột cho khu vực đó.
inteist

0

Để mở rộng khu vực cảm ứng một cách chung chung với khá ít hạn chế, hãy sử dụng mã sau.

Nó cho phép bạn mở rộng vùng cảm ứng của cái đã cho viewtrong ancestorchế độ xem đã cho expansionbằng pixel đã cho. Bạn có thể chọn bất kỳ tổ tiên nào miễn là chế độ xem đã cho nằm trong cây bố cục tổ tiên.

    public static void expandTouchArea(final View view, final ViewGroup ancestor, final int expansion) {
    ancestor.post(new Runnable() {
        public void run() {
            Rect bounds = getRelativeBounds(view, ancestor);
            Rect expandedBounds = expand(bounds, expansion);
            // LOG.debug("Expanding touch area of {} within {} from {} by {}px to {}", view, ancestor, bounds, expansion, expandedBounds);
            ancestor.setTouchDelegate(new TouchDelegate(expandedBounds, view));
        }

        private Rect getRelativeBounds(View view, ViewGroup ancestor) {
            Point relativeLocation = getRelativeLocation(view, ancestor);
            return new Rect(relativeLocation.x, relativeLocation.y,
                            relativeLocation.x + view.getWidth(),
                            relativeLocation.y + view.getHeight());
        }

        private Point getRelativeLocation(View view, ViewGroup ancestor) {
            Point absoluteAncestorLocation = getAbsoluteLocation(ancestor);
            Point absoluteViewLocation = getAbsoluteLocation(view);
            return new Point(absoluteViewLocation.x - absoluteAncestorLocation.x,
                             absoluteViewLocation.y - absoluteAncestorLocation.y);
        }

        private Point getAbsoluteLocation(View view) {
            int[] absoluteLocation = new int[2];
            view.getLocationOnScreen(absoluteLocation);
            return new Point(absoluteLocation[0], absoluteLocation[1]);
        }

        private Rect expand(Rect rect, int by) {
            Rect expandedRect = new Rect(rect);
            expandedRect.left -= by;
            expandedRect.top -= by;
            expandedRect.right += by;
            expandedRect.bottom += by;
            return expandedRect;
        }
    });
}

Các hạn chế áp dụng:

  • Vùng chạm không được vượt quá giới hạn của tổ tiên của chế độ xem vì tổ tiên phải có thể nắm bắt sự kiện chạm để chuyển tiếp nó đến chế độ xem.
  • Chỉ một TouchDelegatecó thể được đặt thành a ViewGroup. Nếu bạn muốn làm việc với nhiều đại biểu cảm ứng, hãy chọn các tổ chức khác nhau hoặc sử dụng đại biểu cảm ứng soạn thảo như được giải thích trong Cách sử dụng nhiều đại diện cảm ứng .

0

Bởi vì tôi không thích ý tưởng chờ đợi thông qua bố cục chỉ để có được kích thước mới của hình chữ nhật TouchDelegate, tôi đã tìm một giải pháp khác:

public class TouchSizeIncreaser extends FrameLayout {
    public TouchSizeIncreaser(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final View child = getChildAt(0);
        if(child != null) {
            child.onTouchEvent(event);
        }
        return true;
    }
}

Và sau đó, trong một bố cục:

<ch.tutti.ui.util.TouchSizeIncreaser
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="10dp">

    <Spinner
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>
</ch.tutti.ui.util.TouchSizeIncreaser>

Ý tưởng là TouchSizeIncreaser FrameLayout sẽ bọc Spinner (có thể là bất kỳ Chế độ xem con nào) và chuyển tiếp tất cả các sự kiện cảm ứng được ghi lại trong đó được nhấn trực tiếp đến Chế độ xem con. Nó hoạt động cho các lần nhấp chuột, con quay mở ra ngay cả khi được nhấp vào bên ngoài giới hạn của nó, không chắc chắn tác động của các trường hợp khác phức tạp hơn là gì.

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.