Android에서 TouchDelegate를 사용하여보기의 클릭 대상 크기를 늘리는 방법에 대한 예가 있습니까?


82

내 이해는보기가 너무 작아서 쉽게 만질 수없는 경우 TouchDelegate를 사용하여 해당보기의 클릭 가능 영역을 늘려야한다는 것입니다.

그러나 Google에서 사용 예를 검색하면 여러 사람이 질문을하지만 답변은 거의 없습니다.

클릭 가능한 영역을 모든 방향으로 4 픽셀 늘릴 수 있도록 뷰에 대한 터치 델리게이트를 설정하는 적절한 방법을 아는 사람이 있습니까?


3
다음은 TouchDelegate 사용 방법에 대한 추가 좋은 게시물입니다. groups.google.com/group/android-developers/browse_thread/thread/…
Mason Lee

답변:


100

Google의 친구에게 물어 보니 TouchDelegate 사용 방법을 알아낼 수있었습니다. 우리가 생각 해낸 것은 다음과 같습니다.

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
한 화면에이 작업을 수행하려는 버튼이 두 개 있으면 어떻게됩니까? TouchDelegate를 다시 설정하면 이전 터치 델리게이트가 지워집니다.
cottonBallPaws 2011 년

2
이것은 나를 위해 작동하지 않았습니다 ... 대리자가 TouchDelegate를 추가하려는 뷰라고 가정합니다.
Kevin Parker

20
@AmokraneChentir 나는 같은 문제가 있었다. TouchDelegates를 사용하는 대신 버튼을 확장하는 클래스가 있습니다. 이 클래스에서는 getHitRect를 재정의합니다. 히트 사각형을 상위 뷰의 크기로 확장 할 수 있습니다. public void getHitRect (Rect outRect) {outRect.set (getLeft ()-mPadding, getTop ()-mPadding, getRight () + mPadding, getTop () + mPadding); }
Brendan Weinstein 2012

5
@Amokrane : 제가 제공 한 솔루션이 ICS에서 작동하지 않는 것으로 나타났습니다. 두 가지 다른 Touch Delegate 전략에 대한 블로그 게시물을 작성했습니다 brendanweinstein.me/2012/06/26/… 두 전략에 대한 샘플 코드는 github.com/brendanw/Touch-Delegates/blob/master/src/com/
Brendan Weinstein

8
@ZsoltSafrany에 따르면이 작업을 수행하는 더 안전한 방법은 OnGlobalLayoutListener를 통해 위임자 onGlobalLayout ()을 할당하는 것입니다.
greg7gkb

20

이 블로그 게시물 에서 주로 그림을 그리는 한 화면에서 여러보기 (확인란)를 사용하여이 작업을 수행 할 수있었습니다 . 기본적으로 emmby의 솔루션을 사용하여 각 버튼과 해당 부모에 개별적으로 적용합니다.

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

제 경우에는 체크 박스가 위에 겹쳐진 이미지 뷰의 gridview가 있었고 다음과 같이 메소드를 호출했습니다.

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

나를 위해 잘 작동합니다.


11

이 솔루션은 @BrendanWeinstein이 의견에 게시했습니다.

를 보내는 대신 (확장하는 경우) 메서드를 TouchDelegate재정 의 할 수 있습니다 .getHitRect(Rect)View

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

흥미로운 아이디어. 그러나보다보기를 할 수 있다는 것을 의미하지 않을 수 없습니다 레이아웃 만들 수? 아니, 기다려요. 새 뷰 클래스를 디자인하고 레이아웃에서 사용하면됩니다. 작동 할 수 있습니다. 시도할만한 가치가 있습니다.
Martin

10
@Martin 그게 올바른 아이디어입니다. 불행히도 getHitRect는 dispatchTouchEvent grepcode.com/file/repository.grepcode.com/java/ext/…에 의해 ICS에서 더 이상 호출되지 않습니다. getHitRect를 재정의하면 gingerbread 이하에서만 작동합니다.
Brendan Weinstein 2014

6

emmby의 approch는 나를 위해 작동하지 않았지만 약간의 변경 후에 작동했습니다.

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

누군가의 시간을 절약 할 수 있습니다.


이것이 나를 위해 일한 것입니다. 실제로 전체 부모가 자식에게 클릭 이벤트를 보내도록합니다. 직계 부모 일 필요도 없습니다 (부모의 부모 일 수 있음).
안드로이드 개발자

위임 할 뷰가 하나 있으면 작동 할 것입니다. 터치 영역을 향상시키기 위해 50 개의 뷰가 있다면 어떨까요? —이 스크린 샷과 같이 : plus.google.com/u/0/b/112127498414857833804/… — TouchDelegate가 여전히 적절한 솔루션입니까?
Martin

2

@Mason Lee의 의견에 따르면 이것은 내 문제를 해결했습니다. 내 프로젝트에는 상대적인 레이아웃과 하나의 버튼이 있습니다. 그래서 부모는-> 레이아웃이고 자식은-> 버튼입니다.

다음은 Google 링크 예제 Google 코드입니다.

그의 매우 귀중한 답변을 삭제하는 경우 여기에 그의 답변을 넣었습니다.

최근에 TouchDelegate를 사용하는 방법에 대해 질문을 받았습니다. 나는 이것에 대해 약간 녹슬었고 그것에 대한 좋은 문서를 찾을 수 없었습니다. 다음은 약간의 시행 착오를 거쳐 작성한 코드입니다. touch_delegate_view는 id가 touch_delegate_root 인 간단한 RelativeLayout입니다. 레이아웃의 단일 자식 인 delegated_button 버튼으로 정의했습니다. 이 예에서는 버튼의 클릭 가능한 영역을 버튼 상단의 200 픽셀 위로 확장합니다.

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

건배, Justin Android 팀 @ Google

나로부터 몇 마디 : 왼쪽으로 확장하려면 마이너스로 값을 제공하고 객체의 오른쪽으로 확장하려면 플러스로 값을 제공합니다. 이것은 상단과 하단에서 동일하게 작동합니다.


여기서 마법의 힌트는 하나의 버튼 인 것 같습니다. 어떻게 든 TouchDelegate를 여러 버튼에 사용할 수 없다는 느낌이 들었습니다. 확인할 수 있습니까?
Martin

1

특정 구성 요소 (Button)에 Padding을 제공하는 것이 더 나은 아이디어가 아닙니까?


1
그러면 더 크게 볼 수 있습니다. 버튼 위에 클릭 할 수없는 텍스트 텍스트가 있고 해당 텍스트 영역을 사용하여 버튼의 클릭 정확도를 높이려면 어떻게해야합니까? —이 스크린 샷과 같이 : plus.google.com/u/0/b/112127498414857833804/…
Martin

1

파티에 조금 늦었지만 많은 조사 끝에 지금 다음을 사용하고 있습니다.

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

이는 허용되는 솔루션보다 낫습니다. TouchDelegate를 상위 뷰뿐만 아니라 모든 조상 뷰에서 설정할 수도 있기 때문입니다.

허용 된 솔루션과 달리, 상위 뷰의 레이아웃이 변경 될 때마다 TouchDelegate도 업데이트합니다.


0

프로그래밍 방식으로하지 않으려면 이미지 주변에 투명한 영역을 만듭니다. 이미지를 단추 (보기)의 배경으로 사용하는 경우.

회색 영역은 투명하여 터치 영역을 늘릴 수 있습니다.

여기에 이미지 설명 입력


1
다른 용도로 사용할 수없는 뷰 주변에 빈 공간을 만들지 않겠습니까? 정보 텍스트를 표시하려면 단추를 닫고 텍스트 영역을 사용하여 단추 클릭 영역을 향상 시키려면 어떻게해야합니까?
Martin

나는 그것의 최고이자 마지막 접근이라고 말하지 않는다. 그러나 작은 버튼 이미지를 표시하고 터치 영역을 확장하려는 많은 시나리오에 적합합니다. 귀하의 경우에는 다른 뷰 (textview)를 클릭 할 때 버튼에 우선권을 부여하는 것이 어려우므로 모두 함께하는 것은 다른 도전입니다.
Vinayak Bevinakatti 2015 년

0

대부분의 경우 다른 헤드리스 뷰 (인공 투명 뷰)에서 더 큰 터치 영역이 필요한 뷰를 래핑하고 래퍼 뷰에 패딩 / 여백을 추가하고 클릭 / 터치를 원래 뷰 대신 래퍼 뷰에 부착 할 수 있습니다. 터치 영역이 더 커야합니다.


다른 용도로 사용할 수없는 뷰 주변에 빈 공간을 만들지 않겠습니까? 정보 텍스트를 표시하려면 단추를 닫고 텍스트 영역을 사용하여 단추 클릭 영역을 향상 시키려면 어떻게해야합니까?
Martin

@Martin, 그것은 당신이 그것을하는 방법에 달려 있습니다. 그리고 원하는 것은 무엇이든 쉽게 할 수 있어야합니다. 원하는 모든보기 (원하는보기 수에 관계없이)를 이러한 모든보기 위에 투명보기로 감싸고 해당 영역에도 클릭을 할당하면됩니다.
inteist

0

거의 제한없이 터치 영역을 일반적으로 확장하려면 다음 코드를 사용하십시오.

view주어진 ancestor뷰 내에서 주어진 expansion픽셀 단위로 주어진 터치 영역을 확장 할 수 있습니다 . 주어진 뷰가 조상 레이아웃 트리에있는 한 조상을 선택할 수 있습니다.

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

적용되는 제한 사항 :

  • 조상은 터치 이벤트를 뷰로 전달하기 위해 잡을 수 있어야하므로 터치 영역은 뷰의 조상 경계를 초과 할 수 없습니다.
  • 하나는 TouchDelegateA를 설정할 수 있습니다 ViewGroup. 여러 터치 델리게이트로 작업하려면 다른 조상을 선택하거나 여러 TouchDelegate 사용 방법에 설명 된대로 구성 터치 델리게이트 를 사용 합니다.

0

TouchDelegate 직사각형의 새로운 크기를 얻기 위해 레이아웃 패스를 기다리는 아이디어가 마음에 들지 않았기 때문에 다른 솔루션을 선택했습니다.

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

그런 다음 레이아웃에서 :

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

아이디어는 TouchSizeIncreaser FrameLayout이 Spinner를 래핑하고 (자식보기가 될 수 있음) 히트 사각형에서 캡처 된 모든 터치 이벤트를 자식보기로 전달한다는 것입니다. 클릭에 대해 작동하며, 범위 밖에서 클릭해도 스피너가 열립니다. 다른 복잡한 경우에 어떤 영향을 미치는지 확실하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.