안드로이드 : onInterceptTouchEvent와 dispatchTouchEvent의 차이점은 무엇입니까?


249

안드로이드 onInterceptTouchEvent와 의 차이점은 무엇입니까 dispatchTouchEvent?

안드로이드 개발자 가이드에 따르면, 두 가지 방법 모두 터치 이벤트를 가로채는 데 사용될 수 MotionEvent있지만 ( ) 차이점은 무엇입니까?

어떻게 onInterceptTouchEvent, dispatchTouchEvent그리고 onTouchEvent뷰의 계층 구조 (내 상호 작용 함께 ViewGroup)?

답변:


272

이것을 이해하기 가장 좋은 곳은 소스 코드입니다. 문서는 이것을 설명하는 데 부적절합니다.

dispatchTouchEvent는 실제로 활동,보기 및보기 그룹에서 정의됩니다. 터치 이벤트를 라우팅하는 방법을 결정하는 컨트롤러라고 생각하십시오.

예를 들어, 가장 간단한 경우는 터치 이벤트가 정의 된 경우 OnTouchListener.onTouch 또는 확장 메소드 onTouchEvent로 터치 이벤트를 라우팅하는 View.dispatchTouchEvent 입니다 .

들어 ViewGroup.dispatchTouchEvent 가지 방법이 더 복잡하다. child.dispatchTouchEvent를 호출하여 이벤트를 가져와야하는 자식 뷰 중 하나를 파악해야합니다. 이것은 기본적으로 어떤 자식 뷰의 경계 사각형에 터치 포인트 좌표가 있는지 알아내는 적중 테스트 알고리즘입니다.

그러나 이벤트를 적절한 자식보기로 전달하기 전에 부모는 이벤트를 함께 감시 및 / 또는 가로 챌 수 있습니다. 이것이 onInterceptTouchEvent 가있는 것입니다. 따라서 적중 테스트를 수행하기 전에이 메소드를 먼저 호출하고 이벤트가 납치 된 경우 (onInterceptTouchEvent에서 true를 리턴 하여) 터치 이벤트 처리 (이전 터치 이벤트에서)를 포기한 다음 그 이후부터 ACTION_CANCEL 을 하위보기로 보냅니다. 부모 수준의 모든 터치 이벤트는 onTouchListener.onTouch (정의 된 경우) 또는 onTouchEvent ()로 전달됩니다. 이 경우에도 onInterceptTouchEvent는 다시 호출되지 않습니다.

[Activity | ViewGroup | View] .dispatchTouchEvent를 재정의 하시겠습니까? 사용자 지정 라우팅을 수행하지 않는 한 아마해서는 안됩니다.

부모 확장에서 터치 이벤트를 감시 및 / 또는 가로 채기를 원하는 경우 기본 확장 방법은 ViewGroup.onInterceptTouchEvent이며, 메인 이벤트 처리를 위해 View.onTouchListener / View.onTouchEvent입니다.

지나치게 복잡한 디자인 imo이지만 안드로이드 API는 단순성보다 유연성에 더 의존합니다.


10
이것은 훌륭하고 간결한 답변입니다. 자세한 예는 "ViewGroup에서 터치 이벤트 관리"
TalkLittle

1
@numan salati : "그 이후부터 부모 수준의 모든 터치 이벤트가 onTouchListener.onTouch로 전달됩니다."-뷰 그룹의 디스패치 터치 메소드를 무시하고 이벤트를 onTouchListener로 전달하려고합니다. 그러나 나는 그것이 어떻게 이루어질 수 있는지 알지 못한다. View.getOnTouchListener (). onTouch ()와 같은 API는 없습니다. setOnTouchListener () 메소드는 있지만 getOnTouchListener () 메소드는 없습니다. 그러면 어떻게 할 수 있습니까?
Ashwin

@ Ashwin 내 생각은 정확히 setOnInterceptTouchEvent도 없습니다. 레이아웃에서 사용하기 위해 서브 클래스 뷰를 재정의하거나 코드에 추가 할 수 있지만 프래그먼트 / 액티비티 자체를 서브 클래 싱하지 않고 뷰를 서브 클래 싱 할 수 없기 때문에 프래그먼트 / 액티비티 레벨 rootViews로 혼동 할 수 없습니다. ProgressActivitiy 구현). API를 단순화하려면 setOnInterceptTouchEvent가 필요합니다. 모든 사람들은 semi-complex 앱의 어느 시점에서 rootViews에서 interceptTouch를 사용합니다
leRobot

244

이것이 Google의 첫 번째 결과이기 때문입니다. Youtube에서 Dave Smith의 훌륭한 대화를 나누고 싶습니다 . Android Touch System 마스터 링 및 슬라이드는 여기에서 사용할 수 있습니다 . Android Touch System에 대해 깊이 이해했습니다.

액티비티 가 터치를 처리 하는 방법 :

  • Activity.dispatchTouchEvent()
    • 항상 먼저 호출
    • Window에 연결된 루트 뷰로 이벤트를 보냅니다.
    • onTouchEvent()
      • 조회수가 이벤트를 소비하지 않으면 호출
      • 항상 마지막으로 호출

View 가 터치를 처리 하는 방법 :

  • View.dispatchTouchEvent()
    • 존재하는 경우 먼저 리스너에게 이벤트를 보냅니다.
      • View.OnTouchListener.onTouch()
    • 소비하지 않으면 터치 자체를 처리합니다.
      • View.onTouchEvent()

ViewGroup 이 터치를 처리 하는 방법 :

  • ViewGroup.dispatchTouchEvent()
    • onInterceptTouchEvent()
      • 어린이를 대체해야하는지 확인
      • 패스 ACTION_CANCEL 액티브 아이
      • 한 번 true를 반환 ViewGroup하면 모든 후속 이벤트를 사용합니다.
    • 각 하위 뷰에 대해 (역순으로 추가됨)
      • 터치가 적절한 경우 (내부보기) child.dispatchTouchEvent()
      • 이전에 의해 처리되지 않으면 다음보기로 디스패치하십시오.
    • 어린이가 이벤트를 처리하지 않으면 청취자는 기회를 얻습니다.
      • OnTouchListener.onTouch()
    • 리스너가 없거나 처리되지 않은 경우
      • onTouchEvent()
  • 차단 된 이벤트가 하위 단계 위로 이동

또한 github.com/devunwired/ 에서 사용자 정의 터치 코드 예제를 제공합니다. .

답 : 기본적으로는 dispatchTouchEvent()모든 View레이어에서 호출되어 View진행중인 제스처에 관심이 있는지 확인합니다 . A의 ViewGroupViewGroup자신의 터치 이벤트를 도용 할 수있는 능력이있다 dispatchTouchEvent()가 부를 것이다 전에, -method dispatchTouchEvent()아이들에 있습니다. 는 ViewGroup경우에만 파견을 중지 할 ViewGroup onInterceptTouchEvent()-method true를 반환합니다. 차이dispatchTouchEvent()파견되어 MotionEventsonInterceptTouchEvent이 차단할지 여부를 알려줍니다 (파견하지 MotionEvent아이들에게) 또는하지 (아이들에게 파견) .

ViewGroup코드 가이 작업을 수행 하는 것을 상상할 수 있습니다 (매우 단순화 됨).

public boolean dispatchTouchEvent(MotionEvent ev) {
    if(!onInterceptTouchEvent()){
        for(View child : children){
            if(child.dispatchTouchEvent(ev))
                return true;
        }
    }
    return super.dispatchTouchEvent(ev);
}

64

보충 답변

다음은 다른 답변에 대한 시각적 보충 자료입니다. 내 정답은 여기에 있습니다 .

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

dispatchTouchEvent()의 방법 ViewGroup사용은 onInterceptTouchEvent()즉시 (와 터치 이벤트를 처리할지 여부를 선택합니다 onTouchEvent()) 또는 통지 계속 dispatchTouchEvent()그 아이의 방법.


활동에서도 onInterceptTouchEvent를 호출 할 수 있습니까 ?? ViewGroup에서만 가능하다고 생각합니까? @Suragch
Federico Rizzo

@FedericoRizzo, 당신이 맞아요! 대단히 감사합니다! 다이어그램과 답변을 업데이트했습니다.
Suragch

20

이 방법들에 대해 많은 혼란이 있지만 실제로 그렇게 복잡하지는 않습니다. 혼란의 대부분은 다음과 같습니다.

  1. 귀하의 경우 View/ViewGroup그 아이의 또는 사실에 반환하지 않습니다 onTouchEvent, dispatchTouchEvent그리고 onInterceptTouchEvent만 요구됩니다 MotionEvent.ACTION_DOWN. 의 true가 없으면 onTouchEvent상위 뷰는 뷰에 MotionEvents가 필요하지 않다고 가정합니다.
  2. onTouchEvent에서 ViewGroup의 자식 중 어느 것도 true를 반환 MotionEvent.ACTION_DOWN하지 않으면 ViewGroup이에서 true를 반환하더라도 onInterceptTouchEvent 만 호출 됩니다 onTouchEvent.

처리 순서는 다음과 같습니다.

  1. dispatchTouchEvent 호출됩니다.
  2. onInterceptTouchEventMotionEvent.ACTION_DOWNViewGroup의 자식 중 하나에서 true를 반환하거나 호출 할 때onTouchEvent .
  3. onTouchEvent는 ViewGroup의 자식에서 처음 호출되며 자식이 true를 반환하지 않으면에서 호출됩니다 View/ViewGroup.

TouchEvents/MotionEvents자녀의 이벤트를 비활성화하지 않고 미리 보려면 두 가지 작업을 수행해야합니다.

  1. dispatchTouchEvent이벤트를 미리보고 돌아가려면 재정의super.dispatchTouchEvent(ev) .
  2. 재정의 onTouchEvent하고 true를 반환하면 그렇지 않으면를 MotionEvent 제외하고 얻을 수 없습니다 MotionEvent.ACTION_DOWN.

제스처를 감지하지 않는 한 자녀의 다른 이벤트를 비활성화하지 않고 스 와이프 이벤트와 같은 제스처를 감지하려면 다음과 같이 할 수 있습니다.

  1. 위에서 설명한대로 MotionEvents를 미리보고 제스처를 감지하면 플래그를 설정하십시오.
  2. onInterceptTouchEvent자녀가 MotionEvent 처리를 취소하도록 플래그를 설정하면 true를 반환합니다 . onInterceptTouchEvent는 next까지 다시 호출되지 않기 때문에 플래그를 재설정하기에 편리한 위치이기도합니다 MotionEvent.ACTION_DOWN.

의 재정의 예 FrameLayout(내 예제는 Xamarin Android로 프로그래밍 할 때 C #이지만 논리는 Java에서 동일합니다) :

public override bool DispatchTouchEvent(MotionEvent e)
{
    // Preview the touch event to detect a swipe:
    switch (e.ActionMasked)
    {
        case MotionEventActions.Down:
            _processingSwipe = false;
            _touchStartPosition = e.RawX;
            break;
        case MotionEventActions.Move:
            if (!_processingSwipe)
            {
                float move = e.RawX - _touchStartPosition;
                if (move >= _swipeSize)
                {
                    _processingSwipe = true;
                    _cancelChildren = true;
                    ProcessSwipe();
                }
            }
            break;
    }
    return base.DispatchTouchEvent(e);
}

public override bool OnTouchEvent(MotionEvent e)
{
    // To make sure to receive touch events, tell parent we are handling them:
    return true;
}

public override bool OnInterceptTouchEvent(MotionEvent e)
{
    // Cancel all children when processing a swipe:
    if (_cancelChildren)
    {
        // Reset cancel flag here, as OnInterceptTouchEvent won't be called until the next MotionEventActions.Down:
        _cancelChildren = false;
        return true;
    }
    return false;
}

3
왜 이것이 더 많은 투표를하지 않는지 모르겠습니다. 좋은 답변 (IMO)이며 도움이되는 것으로 나타났습니다.
Mark Ormesher

8

이 웹 페이지 http://doandroids.com/blogs/tag/codeexample/ 에서 매우 직관적 인 설명을 얻었습니다 . 거기에서 찍은 :

  • boolean onTouchEvent (MotionEvent ev)-이 뷰가 대상인 터치 이벤트가 감지 될 때마다 호출됩니다.
  • boolean onInterceptTouchEvent (MotionEvent ev)-이 ViewGroup 또는 자식을 대상으로 터치 이벤트가 감지 될 때마다 호출됩니다. 이 함수가 true를 반환하면 MotionEvent가 인터셉트되어 자식에 전달되지 않고이 View의 onTouchEvent에 전달됩니다.

2
질문은 onInterceptTouchEvent 및 dispatchTouchEvent에 관한 것입니다. 둘 다 onTouchEvent 전에 호출됩니다. 그러나이 예에서는 dispatchTouchEvent를 볼 수 없습니다.
Dayerman

8

dispatchTouchEvent는 onInterceptTouchEvent 전에 처리합니다.

이 간단한 예를 사용하면 :

   main = new LinearLayout(this){
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            System.out.println("Event - onInterceptTouchEvent");
            return super.onInterceptTouchEvent(ev);
            //return false; //event get propagated
        }
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            System.out.println("Event - dispatchTouchEvent");
            return super.dispatchTouchEvent(ev);
            //return false; //event DONT get propagated
        }
    };

    main.setBackgroundColor(Color.GRAY);
    main.setLayoutParams(new LinearLayout.LayoutParams(320,480));    


    viewA = new EditText(this);
    viewA.setBackgroundColor(Color.YELLOW);
    viewA.setTextColor(Color.BLACK);
    viewA.setTextSize(16);
    viewA.setLayoutParams(new LinearLayout.LayoutParams(320,80));
    main.addView(viewA);

    setContentView(main);

로그는 다음과 같습니다.

I/System.out(25900): Event - dispatchTouchEvent
I/System.out(25900): Event - onInterceptTouchEvent

따라서이 두 핸들러로 작업하는 경우 dispatchTouchEvent를 사용하여 첫 번째 인스턴스에서 이벤트를 처리하면 onInterceptTouchEvent로 이동합니다.

또 다른 차이점은 dispatchTouchEvent가 'false'를 반환하면 이벤트가 자식,이 경우 EditText로 전파되지 않는 반면 onInterceptTouchEvent에서 false를 반환하면 이벤트가 여전히 EditText로 디스패치된다는 것입니다


5

짧은 대답 : 우선dispatchTouchEvent() 호출 됩니다.

짧은 조언 :dispatchTouchEvent() 제어하기가 어렵 기 때문에 재정의해서는 안되며 때로는 성능이 저하 될 수 있습니다. IMHO, 재정의하는 것이 좋습니다 onInterceptTouchEvent().


대부분의 답변은 활동 /보기 그룹 /보기의 흐름 터치 이벤트에 대해 매우 명확하게 언급하기 때문에 ViewGroup(무시 dispatchTouchEvent()) 에서 이러한 메소드에 대한 코드에 대해서만 자세히 설명합니다 .

onInterceptTouchEvent()ACTION 이벤트가 각각 아래로-> 이동-> 위로 호출됩니다. 2 가지 경우가 있습니다 :

  1. 당신이 경우 false를 반환 삼가지 경우 (ACTION_DOWN, ACTION_MOVE, ACTION_UP)에서, 그것은으로 고려할 것 부모가이 터치 이벤트를 필요로하지 않습니다 때문에, onTouch()결코 호출하지 않는 부모 , 그러나 onTouch()아이들 대신 호출 할 것이다 ; 그러나 주목하십시오 :

    • onInterceptTouchEvent()아이들이 전화하지 않는 한 여전히 터치 이벤트가 계속 수신됩니다.requestDisallowInterceptTouchEvent(true) .
    • 해당 이벤트를받는 어린이가없는 경우 (2 가지 경우가 발생할 수 있습니다. 사용자가 접촉 한 위치에 어린이가 없거나 어린이가 있지만 ACTION_DOWN에서 false를 반환하는 경우) 부모는 해당 이벤트를 onTouch()부모에게 다시 보냅니다 .
  2. 그 반대의 경우, true반환 하면 부모는이 터치 이벤트를 즉시 도용 하고 부모onInterceptTouchEvent() 대신 onTouch()호출 됩니다. 모든 onTouch()어린이가 마지막 조치 이벤트 -ACTION_CANCEL을 받습니다 . 따라서 부모를 의미합니다. 터치 이벤트를 훔쳤으며 어린이는 그때부터 처리 할 수 ​​없습니다. onInterceptTouchEvent()return false 의 흐름 은 정상이지만 return true 경우와 약간의 혼동이 있으므로 여기에 나열합니다.

    • ACTION_DOWN에서 true를 반환 onTouch()하면 부모 중 ACTION_DOWN을 다시 받고 조치 (ACTION_MOVE, ACTION_UP)를받습니다.
    • ACTION_MOVE에서 true를 반환 onTouch()하면 부모의 다음 ACTION_MOVE (에서 동일한 ACTION_MOVE가 onInterceptTouchEvent()아님) 및 후속 조치 (ACTION_MOVE, ACTION_UP)가 수신됩니다.
    • ACTION_UP에서 true를 반환 onTouch()하면 부모가 터치 이벤트를 훔치기에는 너무 늦었 기 때문에 부모가 전혀 전화 하지 않습니다 .

한 가지 더 중요한 것은 이벤트의 ACTION_DOWN이 onTouch()뷰가 해당 이벤트에서 더 많은 작업을 수신할지 여부를 결정한다는 것입니다. 에서 ACTION_DOWN에 뷰가 true를 반환 onTouch()하면 뷰가 해당 이벤트에서 더 많은 작업을 수신 할 의향이 있음을 의미합니다. 그렇지 않으면 ACTION_DOWN에서 false를 반환 onTouch()하면 뷰가 해당 이벤트에서 더 이상 조치를받지 않음을 의미합니다.



3

ViewGroup 서브 클래스 내의 다음 코드는 상위 컨테이너가 터치 이벤트를 수신하지 못하게합니다.

  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
    // Normal event dispatch to this container's children, ignore the return value
    super.dispatchTouchEvent(ev);

    // Always consume the event so it is not dispatched further up the chain
    return true;
  }

백그라운드 뷰가 터치 이벤트에 응답하지 않도록 커스텀 오버레이와 함께 사용했습니다.


1

주요 차이점 :

• Activity.dispatchTouchEvent (MotionEvent)-액티비티가 모든 터치 이벤트를 창으로 보내기 전에 가로 챌 수 있습니다.
• ViewGroup.onInterceptTouchEvent (MotionEvent)-ViewGroup이 자식 뷰로 전달 될 때 이벤트를 볼 수 있습니다.


1
예, 나는 안드로이드 개발자 가이드 에서이 답변을 알고 있습니다. 그럼에도 불구하고 불분명합니다. dispatchTouchEvent 메소드는 활동뿐만 아니라 ViewGroup에도 존재합니다. 내 질문은 dispatchTouchEvent, onInterceptTouchEvent 및 onTouchEvent의 세 가지 방법이 ViewGroups의 계층 구조 (예 : RelativeLayouts) 내에서 어떻게 상호 작용하는지였습니다.
Anne Droid

1

ViewGroup onInterceptTouchEvent()은 항상 가장 ACTION_DOWN먼저 발생하는 이벤트 의 진입 점입니다 .

ViewGroup이이 동작을 처리하도록하려면에서 true를 반환하십시오 onInterceptTouchEvent(). true를 반환하면 ViewGroup onTouchEvent()은 다음 ACTION_UP또는 까지의 모든 후속 이벤트를 수신 ACTION_CANCEL하며 대부분의 경우 ACTION_DOWNand ACTION_UP또는 ACTION_CANCELare 사이의 터치 이벤트 ACTION_MOVE는 일반적으로 스크롤 / 플링 제스처로 인식됩니다.

에서 false를 반환 onInterceptTouchEvent()하면 대상 뷰 onTouchEvent()가 호출됩니다. 에서 true를 반환 할 때까지 후속 메시지에 대해 반복됩니다 onInterceptTouchEvent().

출처 : http://neevek.net/posts/2013/10/13/implementing-onInterceptTouchEvent-and-onTouchEvent-for-ViewGroup.html


0

Activity와 View에는 dispatchTouchEvent () 및 onTouchEvent 메서드가 있습니다. ViewGroup에도이 메서드가 있지만 onInterceptTouchEvent라는 다른 메서드가 있습니다. 해당 메소드의 리턴 유형은 부울이며 리턴 값을 통해 디스패치 경로를 제어 할 수 있습니다.

Android에서 이벤트 발송은 활동->보기 그룹->보기에서 시작됩니다.


0
public boolean dispatchTouchEvent(MotionEvent ev){
    boolean consume =false;
    if(onInterceptTouchEvent(ev){
        consume = onTouchEvent(ev);
    }else{
        consume = child.dispatchTouchEvent(ev);
    }
}

1
설명을 추가해 주시겠습니까?
Paul Floyd

3
이 코드 스 니펫은 문제를 해결할 수 있지만 설명을 포함하면 게시물의 품질을 향상시키는 데 실제로 도움이됩니다. 앞으로 독자에게 질문에 대한 답변을 제공하고 있으며 해당 사람들이 코드 제안의 이유를 모를 수도 있습니다.
Rosário Pereira Fernandes

-2

작은 답변 :

onInterceptTouchEvent는 setOnTouchListener보다 먼저 온다.

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