TOUCH EVNET FLOW

Activity 를 하나 상속받아서 view 를 하나 만들었다고 하자. 이 때 그 view 위에서 touch 를 하게 되면 아래와 같은 경로로 event 가 전달된다.

말로 설명을 하자면, activity의 dispatchTouchEvent() 가 호출되면서 window 의 superDispatchTouchEvent(ev) 를 호출한다. 이 superDispatchTouchEvent() 가 최상위에 있는 Dispatch 해주는 녀석이라고 봐도 무방할 듯 하다. 아무튼 이 녀석으로부터 시작해서 child 의 dispatchTouchEvent(event) 를 호출하고, 그 child 의 child 의 dispatchTouchEvent(event)를 호출하고, 또 그 child의 child 의 child 의 dispatchTouchEvent(event)를 호출한다. 이런 식으로 쭉 밑으로 내려 가다보면, View 의 dispatchTouchEvent(event) 로 가게 되고, 여기서 최종적으로 View 의 OnTouchEvent() 를 호출하게 된다.

하지만 참고로 Window 가 가장 밑에 있는 녀석을 이야기 하는 것이다. 가장 마지막의 child 가 가장 상위의 widget 이 되는 것이다.


ViewFlowExample(Activity).dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}
getWindow().superDispatchTouchEvent(ev) --> PhoneWindow$DecorView(ViewGroup).dispatchTouchEvent

PhoneWindow$DecorView(ViewGroup).dispatchTouchEvent(MotionEvent)
  intercepted = onInterceptTouchEvent(ev);
  if (!canceled && !intercepted)
    if (actionMasked == MotionEvent.ACTION_DOWN
        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) 
      if (childrenCount != 0)
        for (int i = childrenCount - 1; i >= 0; i--)
          dispatchTransformedTouchEvent()
            handled = child.dispatchTouchEvent(event);
              
            return handled;
            
child.dispatchTouchEvent(event); ---> LinearLayout(ViewGroup).dispatchTouchEvent()

LinearLayout(ViewGroup).dispatchTouchEvent()
  intercepted = onInterceptTouchEvent(ev);
  if (!canceled && !intercepted)
  dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)
    handled = child.dispatchTouchEvent(event);

FrameLayout(ViewGroup).dispatchTouchEvent()
  intercepted = onInterceptTouchEvent(ev);
  if (!canceled && !intercepted)
    dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)
      handled = child.dispatchTouchEvent(event);

LinearLayout(ViewGroup).dispatchTouchEvent()
  intercepted = onInterceptTouchEvent(ev);
  if (!canceled && !intercepted)
    dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)
      handled = child.dispatchTouchEvent(event);

ViewFlow(ViewGroup).dispatchTouchEvent()
  intercepted = onInterceptTouchEvent(ev);
  if (!canceled && !intercepted)
    dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)
      handled = child.dispatchTouchEvent(event);

        LinearLayout(ViewGroup).dispatchTouchEvent()
          intercepted = onInterceptTouchEvent(ev);
          if (!canceled && !intercepted)
            dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)
              handled = child.dispatchTouchEvent(event);
                // i don't know why the below routine is travered.            
                TextView(View).dispatchTouchEvent()
                  TextView.onTouchEvent()
                  super.onTouchEvent()

          if (mFirstTouchTarget == null) 
            handled = dispatchTransformedTouchEvent(ev, canceled, child=null, 
                    TouchTarget.ALL_POINTER_IDS); 
          {
            if (newPointerIdBits == oldPointerIdBits) 
              if (child == null || child.hasIdentityMatrix())
                  if (child == null) 
                      handled = super.dispatchTouchEvent(event);
                      TextView(View).dispatchTouchEvent()
                     {
                       if(li.mOnTouchListener.onTouch(this, event) ...)
                       // OnTouchListener which is bined to the view.
                         return true
                       View.onTouchEvent(event)
                     }
            return handled
          }
  if (mFirstTouchTarget == null) 
          handled = dispatchTransformedTouchEvent(ev, canceled, null, 
                  TouchTarget.ALL_POINTER_IDS);

이것을 정리하면 아래와 같다. 

Activity.dispatchTouchEvent()
  ViewGroup.dispatchTouchEvent()
    ViewGroup.onInterceptTouchEvent()
    View.dispatchTouchEvent()
      View.onTouch()
      View.onTouchEvent()
    ViewGroup.onTouch()
    ViewGroup.onTouchEvent()
  Activity.onTouchEvent()

ViewGroup.onInterceptTouchEvent() 를 시작으로,
  --> View.onTouch()
  --> View.onTouchEvent()
  --> ViewGroup.onTouch()
  --> ViewGroup.onTouchEvent()
의 순서대로 event 가 전달된다고 할 수 있다.

Event 는 Action_Down 이 어디에서 return 이 되었느냐에 따라 그 이후의 event 인 Action_Move, Action_Up 을 전달 해 준다. 이것은 [ref. 1] 의 그림을 참조하는 것이 좋을 듯 하다.

ViewGroup.OnInterceptTouchEvent() 의 의미만 명확히 해 두면 나머지는 파악하기 쉬울 듯 하다.

ViewGroup.OnInterceptTouchEvent() 를 return true 로 한다는 뜻은 Action_Down event 를 intercepted 했다는 이야기이다. 하지만 이 intercept 가 ViewGoup 의 child 인 다른 ViewGroup 이나 View 에게로 Action_Down event 를 주지 않는다는 이야기이지, 자신(ViewGroup) 의 onTouchEvent() 나 onTouch() 로 주지 않겠다는 이야기는 아니다.

그러므로 ViewGroup.onInterceptTouchEvent() 에서 return true 가 되면, View.diapatchTouchEvent() 를 거치지는 않지만, 그 다음 routine 인 ViewGroup.onTouch() 나 ViewGroup.onTouchEvent() 는 실행이 된다.

여하튼 내가 정리해도, 정리가 잘 안되는 느낌이다. ^^;;; 그래서 다른 책(Pro Android 4)의 내용을 참고해서 얘기를 해준다면, event 를 사용하고 다른 view 나 child 에게 넘기지 않으려 한다면 return true 를 하고, 다른 view 나 child 에서 사용을 해야 한다면 return false 를 하라고 한다.

ONTOUCH() VS ONTOUCHEVENT()

두 개는 사실상 비슷한 녀석으로 보인다. 굳이 나눠놓은 이유를 아직 잘 모르겠지만, 구현방식은 아래와 같다.

onTouch()

onTouch() 는 setOnTouchListener() 등을 통해서 View 에 binding 시켜주면 되고,

listView.setOnTouchListener(new OnTouchListener(){
      
      @Override
      public boolean onTouch(View v, MotionEvent event) {
        // TODO Auto-generated method stub
        android.util.Log.d("test", "onTouch");
        return false;
      }
      
    });



onTouchEvent()

onTouchEvent() 는 View 를 상속한 class 에서 onTouchEvent() 함수를 override 해주면 된다.

public class ViewFlow extends AdapterView<Adapter>{
  @Override
  public boolean onTouchEvent(MotionEvent ev) {
    ...

    switch (action) {
    case MotionEvent.ACTION_DOWN:
      ...
      break;

    case MotionEvent.ACTION_MOVE:
      f...
      break;

    }
    return true;
  }
}


출처 - [컴][안드로이드] onTouch() 와 onTouchEvent() 가 호출되는 순서


Posted by 레미파
,