멀티터치 동작 처리하기

멀티터치 동작은 포인터(손가락) 여러 개가 동시에 화면을 터치하는 것을 말합니다. 이 과정에서는 여러 포인터가 개입된 동작을 감지하는 방법을 설명합니다.

다음 관련 리소스를 참조하세요.

여러 포인터 추적하기

여러 포인터가 동시에 화면을 터치하면 시스템에서 다음과 같은 터치 이벤트가 생성됩니다.

  • ACTION_DOWN: 화면을 터치하는 첫 번째 포인터에 해당합니다. 이 이벤트가 동작을 시작합니다. 이 포인터의 포인터 데이터는 MotionEvent에서 항상 색인 0에 있습니다.
  • ACTION_POINTER_DOWN: 첫 번째 포인터를 넘어 화면에 들어오는 추가 포인터에 해당합니다. 이 포인터의 포인터 데이터는 getActionIndex()가 반환하는 색인에 있습니다.
  • ACTION_MOVE: 누르기 동작 중에 변경사항이 발생하면 생성됩니다.
  • ACTION_POINTER_UP: 기본이 아닌 포인터가 위로 올라갈 때 전송됩니다.
  • ACTION_UP: 마지막 포인터가 화면 밖으로 나갈 때 전송됩니다.

각 포인터의 색인과 ID를 통해 MotionEvent 내에서 개별 포인터를 추적합니다.

  • 색인: MotionEvent는 각 포인터 관련 정보를 배열에 효과적으로 저장합니다. 포인터의 색인은 이 배열 내에서 포인터의 위치입니다. 포인터와 상호작용하는 데 사용하는 MotionEvent 메서드는 대부분 포인터 ID가 아니라 포인터 색인을 매개변수로 사용합니다.
  • ID: 각 포인터에는 터치 이벤트 전체에서 지속성을 유지하는 ID 매핑도 있으므로 전체 동작에서 개별 포인터를 추적할 수 있습니다.

하나의 모션 이벤트 내에서 개별 포인터가 나타나는 순서는 정의되지 않습니다. 따라서 포인터의 색인은 이벤트별로 달라질 수 있지만, 포인터의 포인터 ID는 포인터가 활성 상태를 유지하는 경우 일정하게 유지되는 것이 보장됩니다. getPointerId() 메서드를 사용하여 포인터 ID를 가져와서 동작의 모든 후속 모션 이벤트에서 포인터를 추적합니다. 그런 다음, 후속 모션 이벤트에 findPointerIndex() 메서드를 사용하여 해당 모션 이벤트의 지정된 포인터 ID에 해당하는 포인터 색인을 가져옵니다. 예를 들면 다음과 같습니다.

Kotlin

    private var mActivePointerId: Int = 0

    override fun onTouchEvent(event: MotionEvent): Boolean {
        ...
        // Get the pointer ID
        mActivePointerId = event.getPointerId(0)

        // ... Many touch events later...

        // Use the pointer ID to find the index of the active pointer
        // and fetch its position
        val (x: Float, y: Float) = event.findPointerIndex(mActivePointerId).let { pointerIndex ->
            // Get the pointer's current position
            event.getX(pointerIndex) to event.getY(pointerIndex)
        }
        ...
    }
    

자바

    private int mActivePointerId;

    public boolean onTouchEvent(MotionEvent event) {
        ...
        // Get the pointer ID
        mActivePointerId = event.getPointerId(0);

        // ... Many touch events later...

        // Use the pointer ID to find the index of the active pointer
        // and fetch its position
        int pointerIndex = event.findPointerIndex(mActivePointerId);
        // Get the pointer's current position
        float x = event.getX(pointerIndex);
        float y = event.getY(pointerIndex);
        ...
    }
    

MotionEvent의 작업 가져오기

항상 getActionMasked() 메서드(또는 호환성 버전인 MotionEventCompat.getActionMasked()이면 더 좋음)를 사용하여 MotionEvent의 작업을 검색해야 합니다. 이전 getAction() 메서드와 달리 getActionMasked()는 여러 포인터를 사용하도록 설계되었습니다. 포인터 색인 비트를 포함하지 않고 실행하는 마스킹된 작업을 반환합니다. 그러면 개발자는 getActionIndex()를 사용하여 작업과 관련된 포인터의 색인을 반환할 수 있습니다. 아래 스니펫에서 자세히 보여줍니다.

참고: 이 예에서는 MotionEventCompat 클래스를 사용합니다. 이 클래스는 지원 라이브러리에 있습니다. 다양한 플랫폼에 최상의 지원을 제공하려면 MotionEventCompat를 사용해야 합니다. MotionEventCompatMotionEvent 클래스를 대체하지 않습니다. 해당 이벤트와 관련된 원하는 작업을 수신하기 위해 MotionEvent 객체를 전달하는 정적 유틸리티 메서드를 제공합니다.

Kotlin

    val (xPos: Int, yPos: Int) = MotionEventCompat.getActionMasked(event).let { action ->
        Log.d(DEBUG_TAG, "The action is ${actionToString(action)}")
        // Get the index of the pointer associated with the action.
        MotionEventCompat.getActionIndex(event).let { index ->
            // The coordinates of the current screen contact, relative to
            // the responding View or Activity.
            MotionEventCompat.getX(event, index).toInt() to MotionEventCompat.getY(event, index).toInt()
        }
    }

    if (event.pointerCount > 1) {
        Log.d(DEBUG_TAG, "Multitouch event")

    } else {
        // Single touch event
        Log.d(DEBUG_TAG, "Single touch event")
    }

    ...

    // Given an action int, returns a string description
    fun actionToString(action: Int): String {
        return when (action) {
            MotionEvent.ACTION_DOWN -> "Down"
            MotionEvent.ACTION_MOVE -> "Move"
            MotionEvent.ACTION_POINTER_DOWN -> "Pointer Down"
            MotionEvent.ACTION_UP -> "Up"
            MotionEvent.ACTION_POINTER_UP -> "Pointer Up"
            MotionEvent.ACTION_OUTSIDE -> "Outside"
            MotionEvent.ACTION_CANCEL -> "Cancel"
            else -> ""
        }
    }
    

자바

    int action = MotionEventCompat.getActionMasked(event);
    // Get the index of the pointer associated with the action.
    int index = MotionEventCompat.getActionIndex(event);
    int xPos = -1;
    int yPos = -1;

    Log.d(DEBUG_TAG,"The action is " + actionToString(action));

    if (event.getPointerCount() > 1) {
        Log.d(DEBUG_TAG,"Multitouch event");
        // The coordinates of the current screen contact, relative to
        // the responding View or Activity.
        xPos = (int)MotionEventCompat.getX(event, index);
        yPos = (int)MotionEventCompat.getY(event, index);

    } else {
        // Single touch event
        Log.d(DEBUG_TAG,"Single touch event");
        xPos = (int)MotionEventCompat.getX(event, index);
        yPos = (int)MotionEventCompat.getY(event, index);
    }
    ...

    // Given an action int, returns a string description
    public static String actionToString(int action) {
        switch (action) {

            case MotionEvent.ACTION_DOWN: return "Down";
    	case MotionEvent.ACTION_MOVE: return "Move";
    	case MotionEvent.ACTION_POINTER_DOWN: return "Pointer Down";
    	case MotionEvent.ACTION_UP: return "Up";
    	case MotionEvent.ACTION_POINTER_UP: return "Pointer Up";
    	case MotionEvent.ACTION_OUTSIDE: return "Outside";
    	case MotionEvent.ACTION_CANCEL: return "Cancel";
        }
        return "";
    }
    

멀티터치 및 몇 가지 예를 자세히 알아보려면 드래그 및 확장 과정을 참조하세요.