터치 동작은 사용자가 터치스크린에 손가락을 한 개 이상 올려놓았을 때 앱이 이러한 터치 패턴을 동작으로 해석할 때 발생합니다. 동작 감지에는 두 단계가 있습니다.
- 터치 이벤트 데이터를 수집하는 중입니다.
- 데이터를 해석하여 앱에서 지원하는 동작의 기준을 충족하는지 확인
AndroidX 클래스
이 문서의 예에서는 GestureDetectorCompat
및 MotionEventCompat
클래스를 사용합니다. 이러한 클래스는 AndroidX 라이브러리에 있습니다. 가능한 경우 AndroidX 클래스를 사용하여 이전 기기와의 호환성을 제공하세요.
MotionEventCompat
은 MotionEvent
클래스를 대체하지 않습니다. 그 이벤트와 관련된 작업을 수신하기 위해 MotionEvent
객체를 전달하는 정적 유틸리티 메서드를 제공합니다.
데이터 수집
사용자가 화면에 손가락을 한 개 이상 올려놓으면 터치 이벤트를 수신하는 뷰에서 콜백 onTouchEvent()
가 트리거됩니다. 동작으로 식별되는 터치 이벤트(위치, 압력, 크기, 다른 손가락 추가 등)의 각 시퀀스에는 onTouchEvent()
가 여러 번 실행됩니다.
동작은 사용자가 화면을 처음 터치할 때 시작되어 시스템이 사용자의 손가락 또는 손가락 위치를 추적할 때 계속 진행되며, 사용자가 화면을 떠나는 마지막 손가락의 마지막 이벤트를 캡처하여 끝납니다.
이 상호작용 전반에 걸쳐 onTouchEvent()
에 전달된 MotionEvent
는 모든 상호작용의 세부정보를 제공합니다. 앱은 MotionEvent
에서 제공하는 데이터를 사용하여 관심 있는 동작이 발생하는지 확인할 수 있습니다.
활동 또는 뷰의 터치 이벤트 캡처
Activity
또는 View
에서 터치 이벤트를 가로채려면 onTouchEvent()
콜백을 재정의합니다.
다음 코드 스니펫은 getAction()
를 사용하여 event
매개변수에서 사용자가 실행하는 작업을 추출합니다.
이렇게 하면 관심 있는 동작이 발생하는지 확인하는 데 필요한 원시 데이터가 제공됩니다.
Kotlin
class MainActivity : Activity() { ... // This example shows an Activity. You can use the same approach if you are // subclassing a View. override fun onTouchEvent(event: MotionEvent): Boolean { return when (event.action) { MotionEvent.ACTION_DOWN -> { Log.d(DEBUG_TAG, "Action was DOWN") true } MotionEvent.ACTION_MOVE -> { Log.d(DEBUG_TAG, "Action was MOVE") true } MotionEvent.ACTION_UP -> { Log.d(DEBUG_TAG, "Action was UP") true } MotionEvent.ACTION_CANCEL -> { Log.d(DEBUG_TAG, "Action was CANCEL") true } MotionEvent.ACTION_OUTSIDE -> { Log.d(DEBUG_TAG, "Movement occurred outside bounds of current screen element") true } else -> super.onTouchEvent(event) } } }
Java
public class MainActivity extends Activity { ... // This example shows an Activity. You can use the same approach if you are // subclassing a View. @Override public boolean onTouchEvent(MotionEvent event){ switch(event.getAction()) { case (MotionEvent.ACTION_DOWN) : Log.d(DEBUG_TAG,"Action was DOWN"); return true; case (MotionEvent.ACTION_MOVE) : Log.d(DEBUG_TAG,"Action was MOVE"); return true; case (MotionEvent.ACTION_UP) : Log.d(DEBUG_TAG,"Action was UP"); return true; case (MotionEvent.ACTION_CANCEL) : Log.d(DEBUG_TAG,"Action was CANCEL"); return true; case (MotionEvent.ACTION_OUTSIDE) : Log.d(DEBUG_TAG,"Movement occurred outside bounds of current screen element"); return true; default : return super.onTouchEvent(event); } }
이 코드는 사용자가 탭하고, 길게 터치하고, 드래그할 때 Logcat에서 다음과 같은 메시지를 생성합니다.
GESTURES D Action was DOWN GESTURES D Action was UP GESTURES D Action was MOVE
맞춤 동작의 경우 이러한 이벤트를 자체적으로 처리하여
처리해야 하는 동작을 나타내는지 확인할 수 있습니다. 그러나 앱에서 두 번 탭하기, 길게 터치, 살짝 튕기기 등의 일반적인 동작을 사용하는 경우 GestureDetector
클래스를 활용할 수 있습니다. GestureDetector
를 사용하면 개별 터치 이벤트를 직접 처리하지 않고도 일반적인 동작을 더 쉽게 감지할 수 있습니다. 동작 감지에서 자세히 설명합니다.
단일 뷰의 터치 이벤트 캡처
onTouchEvent()
의 대안으로, setOnTouchListener()
메서드를 사용하여 View.OnTouchListener
객체를 View
객체에 연결할 수 있습니다. 이렇게 하면 다음 예와 같이 기존 View
를 서브클래스로 분류하지 않고도 터치 이벤트를 수신할 수 있습니다.
Kotlin
findViewById<View>(R.id.my_view).setOnTouchListener { v, event -> // Respond to touch events. true }
Java
View myView = findViewById(R.id.my_view); myView.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { // Respond to touch events. return true; } });
ACTION_DOWN
이벤트에 false
를 반환하는 리스너를 만들 때 주의하세요.
이렇게 하면 이벤트의 후속 ACTION_MOVE
및 ACTION_UP
시퀀스에 대해 리스너가 호출되지 않습니다. 이는 ACTION_DOWN
가 모든 터치 이벤트의 시작점이기 때문입니다.
맞춤 뷰를 만드는 경우 앞에서 설명한 대로 onTouchEvent()
를 재정의할 수 있습니다.
동작 감지
Android는 일반적인 동작을 감지하는 GestureDetector
클래스를 제공합니다. 지원되는 동작으로는 onDown()
, onLongPress()
, onFling()
등이 있습니다.
GestureDetector
를 앞에서 설명한 onTouchEvent()
메서드와 함께 사용할 수 있습니다.
지원되는 모든 동작 감지
GestureDetectorCompat
객체를 인스턴스화할 때 이 객체가 사용하는 매개변수 중 하나는 GestureDetector.OnGestureListener
인터페이스를 구현하는 클래스입니다. GestureDetector.OnGestureListener
는 특정 터치 이벤트가 발생하면 사용자에게 알립니다. GestureDetector
객체가 이벤트를 수신할 수 있게 하려면 뷰 또는 활동의 onTouchEvent()
메서드를 재정의하고 관찰된 모든 이벤트를 감지기 인스턴스에 전달합니다.
다음 스니펫에서 개별 on<TouchEvent>
메서드의 반환 값 true
는 터치 이벤트가 처리됨을 나타냅니다. 반환 값 false
는 터치가 처리될 때까지 뷰 스택 전체에 이벤트를 전달합니다.
테스트 앱에서 다음 스니펫을 실행하면 터치 스크린과 상호작용할 때 작업이 트리거되는 방식과 각 터치 이벤트의 MotionEvent
콘텐츠를 파악할 수 있습니다. 그런 다음 간단한 상호작용을 위해 생성되는 데이터의 양을 확인합니다.
Kotlin
private const val DEBUG_TAG = "Gestures" class MainActivity : Activity(), GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener { private lateinit var mDetector: GestureDetectorCompat // Called when the activity is first created. public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Instantiate the gesture detector with the // application context and an implementation of // GestureDetector.OnGestureListener. mDetector = GestureDetectorCompat(this, this) // Set the gesture detector as the double-tap // listener. mDetector.setOnDoubleTapListener(this) } override fun onTouchEvent(event: MotionEvent): Boolean { return if (mDetector.onTouchEvent(event)) { true } else { super.onTouchEvent(event) } } override fun onDown(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDown: $event") return true } override fun onFling( event1: MotionEvent, event2: MotionEvent, velocityX: Float, velocityY: Float ): Boolean { Log.d(DEBUG_TAG, "onFling: $event1 $event2") return true } override fun onLongPress(event: MotionEvent) { Log.d(DEBUG_TAG, "onLongPress: $event") } override fun onScroll( event1: MotionEvent, event2: MotionEvent, distanceX: Float, distanceY: Float ): Boolean { Log.d(DEBUG_TAG, "onScroll: $event1 $event2") return true } override fun onShowPress(event: MotionEvent) { Log.d(DEBUG_TAG, "onShowPress: $event") } override fun onSingleTapUp(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onSingleTapUp: $event") return true } override fun onDoubleTap(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDoubleTap: $event") return true } override fun onDoubleTapEvent(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDoubleTapEvent: $event") return true } override fun onSingleTapConfirmed(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onSingleTapConfirmed: $event") return true } }
Java
public class MainActivity extends Activity implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener{ private static final String DEBUG_TAG = "Gestures"; private GestureDetectorCompat mDetector; // Called when the activity is first created. @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Instantiate the gesture detector with the // application context and an implementation of // GestureDetector.OnGestureListener. mDetector = new GestureDetectorCompat(this,this); // Set the gesture detector as the double-tap // listener. mDetector.setOnDoubleTapListener(this); } @Override public boolean onTouchEvent(MotionEvent event){ if (this.mDetector.onTouchEvent(event)) { return true; } return super.onTouchEvent(event); } @Override public boolean onDown(MotionEvent event) { Log.d(DEBUG_TAG,"onDown: " + event.toString()); return true; } @Override public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) { Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString()); return true; } @Override public void onLongPress(MotionEvent event) { Log.d(DEBUG_TAG, "onLongPress: " + event.toString()); } @Override public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, float distanceY) { Log.d(DEBUG_TAG, "onScroll: " + event1.toString() + event2.toString()); return true; } @Override public void onShowPress(MotionEvent event) { Log.d(DEBUG_TAG, "onShowPress: " + event.toString()); } @Override public boolean onSingleTapUp(MotionEvent event) { Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString()); return true; } @Override public boolean onDoubleTap(MotionEvent event) { Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString()); return true; } @Override public boolean onDoubleTapEvent(MotionEvent event) { Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString()); return true; } @Override public boolean onSingleTapConfirmed(MotionEvent event) { Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString()); return true; } }
지원되는 동작 하위 집합 감지
몇 가지 동작만 처리하려면 GestureDetector.OnGestureListener
인터페이스를 구현하는 대신 GestureDetector.SimpleOnGestureListener
를 확장하면 됩니다.
GestureDetector.SimpleOnGestureListener
는 모든 on<TouchEvent>
메서드에 관해 false
를 반환하여 메서드 구현을 제공합니다. 이를 통해 관심 있는 메서드만 재정의할 수 있습니다. 예를 들어 다음 코드 스니펫은 GestureDetector.SimpleOnGestureListener
를 확장하고 onFling()
및 onDown()
를 재정의하는 클래스를 만듭니다.
GestureDetector.OnGestureListener
를 사용하든 GestureDetector.SimpleOnGestureListener
를 사용하든 true
를 반환하는 onDown()
메서드를 구현하는 것이 좋습니다. 모든 동작이 onDown()
메시지로 시작하기 때문입니다. GestureDetector.SimpleOnGestureListener
가 기본적으로 하는 것처럼 onDown()
에서 false
를 반환하는 경우 시스템은 개발자가 나머지 동작을 무시한다고 가정하고 GestureDetector.OnGestureListener
의 다른 메서드는 호출되지 않습니다. 이로 인해 앱에 예기치 않은 문제가 발생할 수 있습니다. 전체 동작을 무시하려는 경우에만 onDown()
에서 false
를 반환합니다.
Kotlin
private const val DEBUG_TAG = "Gestures" class MainActivity : Activity() { private lateinit var mDetector: GestureDetectorCompat public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) mDetector = GestureDetectorCompat(this, MyGestureListener()) } override fun onTouchEvent(event: MotionEvent): Boolean { mDetector.onTouchEvent(event) return super.onTouchEvent(event) } private class MyGestureListener : GestureDetector.SimpleOnGestureListener() { override fun onDown(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDown: $event") return true } override fun onFling( event1: MotionEvent, event2: MotionEvent, velocityX: Float, velocityY: Float ): Boolean { Log.d(DEBUG_TAG, "onFling: $event1 $event2") return true } } }
Java
public class MainActivity extends Activity { private GestureDetectorCompat mDetector; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mDetector = new GestureDetectorCompat(this, new MyGestureListener()); } @Override public boolean onTouchEvent(MotionEvent event){ if (this.mDetector.onTouchEvent(event)) { return true; } return super.onTouchEvent(event); } class MyGestureListener extends GestureDetector.SimpleOnGestureListener { private static final String DEBUG_TAG = "Gestures"; @Override public boolean onDown(MotionEvent event) { Log.d(DEBUG_TAG,"onDown: " + event.toString()); return true; } @Override public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) { Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString()); return true; } } }