답변:
이것을 이해하기 가장 좋은 곳은 소스 코드입니다. 문서는 이것을 설명하는 데 부적절합니다.
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는 단순성보다 유연성에 더 의존합니다.
이것이 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의 ViewGroup
는 ViewGroup
자신의 터치 이벤트를 도용 할 수있는 능력이있다 dispatchTouchEvent()
가 부를 것이다 전에, -method dispatchTouchEvent()
아이들에 있습니다. 는 ViewGroup
경우에만 파견을 중지 할 ViewGroup
onInterceptTouchEvent()
-method true를 반환합니다. 차이 즉 dispatchTouchEvent()
파견되어 MotionEvents
와 onInterceptTouchEvent
이 차단할지 여부를 알려줍니다 (파견하지 MotionEvent
아이들에게) 또는하지 (아이들에게 파견) .
ViewGroup 의 코드 가이 작업을 수행 하는 것을 상상할 수 있습니다 (매우 단순화 됨).
public boolean dispatchTouchEvent(MotionEvent ev) {
if(!onInterceptTouchEvent()){
for(View child : children){
if(child.dispatchTouchEvent(ev))
return true;
}
}
return super.dispatchTouchEvent(ev);
}
다음은 다른 답변에 대한 시각적 보충 자료입니다. 내 정답은 여기에 있습니다 .
dispatchTouchEvent()
의 방법 ViewGroup
사용은 onInterceptTouchEvent()
즉시 (와 터치 이벤트를 처리할지 여부를 선택합니다 onTouchEvent()
) 또는 통지 계속 dispatchTouchEvent()
그 아이의 방법.
이 방법들에 대해 많은 혼란이 있지만 실제로 그렇게 복잡하지는 않습니다. 혼란의 대부분은 다음과 같습니다.
View/ViewGroup
그 아이의 또는 사실에 반환하지 않습니다
onTouchEvent
, dispatchTouchEvent
그리고 onInterceptTouchEvent
만 요구됩니다 MotionEvent.ACTION_DOWN
. 의 true가 없으면
onTouchEvent
상위 뷰는 뷰에 MotionEvents가 필요하지 않다고 가정합니다.MotionEvent.ACTION_DOWN
하지 않으면 ViewGroup이에서 true를 반환하더라도 onInterceptTouchEvent 만 호출 됩니다 onTouchEvent
.처리 순서는 다음과 같습니다.
dispatchTouchEvent
호출됩니다.onInterceptTouchEvent
MotionEvent.ACTION_DOWN
ViewGroup의 자식 중 하나에서 true를 반환하거나 호출 할 때onTouchEvent
.onTouchEvent
는 ViewGroup의 자식에서 처음 호출되며 자식이 true를 반환하지 않으면에서 호출됩니다
View/ViewGroup
.TouchEvents/MotionEvents
자녀의 이벤트를 비활성화하지 않고 미리 보려면 두 가지 작업을 수행해야합니다.
dispatchTouchEvent
이벤트를 미리보고 돌아가려면
재정의super.dispatchTouchEvent(ev)
.onTouchEvent
하고 true를 반환하면 그렇지 않으면를 MotionEvent
제외하고 얻을 수 없습니다
MotionEvent.ACTION_DOWN
.제스처를 감지하지 않는 한 자녀의 다른 이벤트를 비활성화하지 않고 스 와이프 이벤트와 같은 제스처를 감지하려면 다음과 같이 할 수 있습니다.
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;
}
이 웹 페이지 http://doandroids.com/blogs/tag/codeexample/ 에서 매우 직관적 인 설명을 얻었습니다 . 거기에서 찍은 :
- boolean onTouchEvent (MotionEvent ev)-이 뷰가 대상인 터치 이벤트가 감지 될 때마다 호출됩니다.
- boolean onInterceptTouchEvent (MotionEvent ev)-이 ViewGroup 또는 자식을 대상으로 터치 이벤트가 감지 될 때마다 호출됩니다. 이 함수가 true를 반환하면 MotionEvent가 인터셉트되어 자식에 전달되지 않고이 View의 onTouchEvent에 전달됩니다.
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로 디스패치된다는 것입니다
짧은 대답 : 우선dispatchTouchEvent()
호출 됩니다.
짧은 조언 :dispatchTouchEvent()
제어하기가 어렵 기 때문에 재정의해서는 안되며 때로는 성능이 저하 될 수 있습니다. IMHO, 재정의하는 것이 좋습니다 onInterceptTouchEvent()
.
대부분의 답변은 활동 /보기 그룹 /보기의 흐름 터치 이벤트에 대해 매우 명확하게 언급하기 때문에 ViewGroup
(무시 dispatchTouchEvent()
) 에서 이러한 메소드에 대한 코드에 대해서만 자세히 설명합니다 .
onInterceptTouchEvent()
ACTION 이벤트가 각각 아래로-> 이동-> 위로 호출됩니다. 2 가지 경우가 있습니다 :
당신이 경우 false를 반환 삼가지 경우 (ACTION_DOWN, ACTION_MOVE, ACTION_UP)에서, 그것은으로 고려할 것 부모가이 터치 이벤트를 필요로하지 않습니다 때문에, onTouch()
결코 호출하지 않는 부모 , 그러나 onTouch()
아이들 대신 호출 할 것이다 ; 그러나 주목하십시오 :
onInterceptTouchEvent()
아이들이 전화하지 않는 한 여전히 터치 이벤트가 계속 수신됩니다.requestDisallowInterceptTouchEvent(true)
.onTouch()
부모에게 다시 보냅니다 .그 반대의 경우, true 를 반환 하면 부모는이 터치 이벤트를 즉시 도용 하고 부모onInterceptTouchEvent()
대신 onTouch()
호출 됩니다. 모든 onTouch()
어린이가 마지막 조치 이벤트 -ACTION_CANCEL을 받습니다 . 따라서 부모를 의미합니다. 터치 이벤트를 훔쳤으며 어린이는 그때부터 처리 할 수 없습니다. onInterceptTouchEvent()
return false 의 흐름 은 정상이지만 return true 경우와 약간의 혼동이 있으므로 여기에 나열합니다.
onTouch()
하면 부모 중 ACTION_DOWN을 다시 받고 조치 (ACTION_MOVE, ACTION_UP)를받습니다.onTouch()
하면 부모의 다음 ACTION_MOVE (에서 동일한 ACTION_MOVE가 onInterceptTouchEvent()
아님) 및 후속 조치 (ACTION_MOVE, ACTION_UP)가 수신됩니다.onTouch()
하면 부모가 터치 이벤트를 훔치기에는 너무 늦었 기 때문에 부모가 전혀 전화 하지 않습니다 .한 가지 더 중요한 것은 이벤트의 ACTION_DOWN이 onTouch()
뷰가 해당 이벤트에서 더 많은 작업을 수신할지 여부를 결정한다는 것입니다. 에서 ACTION_DOWN에 뷰가 true를 반환 onTouch()
하면 뷰가 해당 이벤트에서 더 많은 작업을 수신 할 의향이 있음을 의미합니다. 그렇지 않으면 ACTION_DOWN에서 false를 반환 onTouch()
하면 뷰가 해당 이벤트에서 더 이상 조치를받지 않음을 의미합니다.
이 비디오 https://www.youtube.com/watch?v=SYoN-OvdZ3M&list=PLonJJ3BVjZW6CtAMbJz1XD8ELUs1KXaTD&index=19 와 다음 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;
}
백그라운드 뷰가 터치 이벤트에 응답하지 않도록 커스텀 오버레이와 함께 사용했습니다.
주요 차이점 :
• Activity.dispatchTouchEvent (MotionEvent)-액티비티가 모든 터치 이벤트를 창으로 보내기 전에 가로 챌 수 있습니다.
• ViewGroup.onInterceptTouchEvent (MotionEvent)-ViewGroup이 자식 뷰로 전달 될 때 이벤트를 볼 수 있습니다.
ViewGroup onInterceptTouchEvent()
은 항상 가장 ACTION_DOWN
먼저 발생하는 이벤트 의 진입 점입니다 .
ViewGroup이이 동작을 처리하도록하려면에서 true를 반환하십시오 onInterceptTouchEvent()
. true를 반환하면 ViewGroup onTouchEvent()
은 다음 ACTION_UP
또는 까지의 모든 후속 이벤트를 수신 ACTION_CANCEL
하며 대부분의 경우 ACTION_DOWN
and ACTION_UP
또는 ACTION_CANCEL
are 사이의 터치 이벤트 ACTION_MOVE
는 일반적으로 스크롤 / 플링 제스처로 인식됩니다.
에서 false를 반환 onInterceptTouchEvent()
하면 대상 뷰 onTouchEvent()
가 호출됩니다. 에서 true를 반환 할 때까지 후속 메시지에 대해 반복됩니다 onInterceptTouchEvent()
.
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume =false;
if(onInterceptTouchEvent(ev){
consume = onTouchEvent(ev);
}else{
consume = child.dispatchTouchEvent(ev);
}
}