이 과정에서는 터치 이벤트의 움직임을 추적하는 방법을 설명합니다.
새 onTouchEvent()
는 현재 터치의 접촉 위치, 압력 또는 크기가 변경될 때마다 ACTION_MOVE
이벤트를 통해 트리거됩니다. 일반 동작 감지하기에 설명된 대로, 이러한 이벤트는 모두 onTouchEvent()
의 MotionEvent
매개변수에 기록됩니다.
손가락 기반의 터치가 가장 정확한 상호작용 방식이 아닌 경우도 있으므로 단순한 접촉이 아닌 움직임을 기반으로 터치 이벤트를 감지하는 경우가 많습니다. 앱이 움직임 기반의 동작(예: 스와이프)과 움직임 기반이 아닌 동작(예: 한 번 탭하기)을 구별하는 데 도움이 되도록 Android에서는 '터치 슬롭'이라는 개념을 포함했습니다. 터치 슬롭은 사용자의 터치 동작이 이동 기반 동작으로 해석되기 전까지 배회할 있는 거리(픽셀 단위)를 말합니다. 이 주제를 자세히 알아보려면 ViewGroup에서 터치 이벤트 관리하기를 참고하세요.
동작에서 움직임을 추적하는 방법은 애플리케이션의 요구사항에 따라 몇 가지가 있습니다. 예를 들면 다음과 같습니다.
- 포인터의 시작 위치와 종료 위치(예: 화면에서 A 지점에서 B 지점으로 개체 이동)
- 포인터가 이동하는 방향. x 좌표와 y 좌표에 의해 결정됩니다.
- 기록.
MotionEvent
메서드getHistorySize()
를 호출하여 동작 기록의 크기를 찾을 수 있습니다. 그런 다음 모션 이벤트의getHistorical<Value>
메서드를 사용하여 각 기록 이벤트의 위치, 크기, 시간, 압력을 구할 수 있습니다. 기록은 터치 그리기와 같은 용도로 사용자 손가락의 자취를 렌더링할 때 유용합니다. 자세한 내용은MotionEvent
참조를 확인하세요. - 포인터가 터치스크린에서 움직일 때의 속도
추적 속도
단순히 포인터가 이동한 거리 또는 방향을 기준으로 하는 움직임 기반 동작이 있을 수 있습니다. 하지만 속도는 동작의 특징을 추적하는 데 또는 동작이 발생했는지 확인하는 데 결정적인 요소인 경우가 많습니다. 속도를 더 쉽게 계산할 수 있도록 Android에서는 VelocityTracker
클래스를 제공합니다. VelocityTracker
는 터치 이벤트의 속도를 추적하는 데 도움이 되며 속도가 동작의 기준 중 하나인 동작(예: 살짝 튕기기)에 유용합니다.
다음은 VelocityTracker
API에서 메서드의 목적을 보여주는 간단한 예입니다.
Kotlin
private const val DEBUG_TAG = "Velocity" class MainActivity : Activity() { private var mVelocityTracker: VelocityTracker? = null override fun onTouchEvent(event: MotionEvent): Boolean { when (event.actionMasked) { MotionEvent.ACTION_DOWN -> { // Reset the velocity tracker back to its initial state. mVelocityTracker?.clear() // If necessary retrieve a new VelocityTracker object to watch the // velocity of a motion. mVelocityTracker = mVelocityTracker ?: VelocityTracker.obtain() // Add a user's movement to the tracker. mVelocityTracker?.addMovement(event) } MotionEvent.ACTION_MOVE -> { mVelocityTracker?.apply { val pointerId: Int = event.getPointerId(event.actionIndex) addMovement(event) // When you want to determine the velocity, call // computeCurrentVelocity(). Then call getXVelocity() // and getYVelocity() to retrieve the velocity for each pointer ID. computeCurrentVelocity(1000) // Log velocity of pixels per second // Best practice to use VelocityTrackerCompat where possible. Log.d("", "X velocity: ${getXVelocity(pointerId)}") Log.d("", "Y velocity: ${getYVelocity(pointerId)}") } } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { // Return a VelocityTracker object back to be re-used by others. mVelocityTracker?.recycle() mVelocityTracker = null } } return true } }
자바
public class MainActivity extends Activity { private static final String DEBUG_TAG = "Velocity"; ... private VelocityTracker mVelocityTracker = null; @Override public boolean onTouchEvent(MotionEvent event) { int index = event.getActionIndex(); int action = event.getActionMasked(); int pointerId = event.getPointerId(index); switch(action) { case MotionEvent.ACTION_DOWN: if(mVelocityTracker == null) { // Retrieve a new VelocityTracker object to watch the // velocity of a motion. mVelocityTracker = VelocityTracker.obtain(); } else { // Reset the velocity tracker back to its initial state. mVelocityTracker.clear(); } // Add a user's movement to the tracker. mVelocityTracker.addMovement(event); break; case MotionEvent.ACTION_MOVE: mVelocityTracker.addMovement(event); // When you want to determine the velocity, call // computeCurrentVelocity(). Then call getXVelocity() // and getYVelocity() to retrieve the velocity for each pointer ID. mVelocityTracker.computeCurrentVelocity(1000); // Log velocity of pixels per second // Best practice to use VelocityTrackerCompat where possible. Log.d("", "X velocity: " + mVelocityTracker.getXVelocity(pointerId)); Log.d("", "Y velocity: " + mVelocityTracker.getYVelocity(pointerId)); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: // Return a VelocityTracker object back to be re-used by others. mVelocityTracker.recycle(); break; } return true; } }
포인터 캡처 사용하기
게임, 원격 데스크톱, 가상화 클라이언트와 일부 앱은 마우스 포인터를 제어할 수 있게 되면 큰 이점을 누릴 수 있습니다. 포인터 캡처는 모든 마우스 이벤트를 앱의 포커스 뷰에 제공함으로써 이러한 제어를 제공하는 기능으로, Android 8.0(API 레벨 26) 이상에서 사용할 수 있습니다.
포인터 캡처 요청하기
앱의 뷰는 뷰가 포함된 뷰 계층 구조에 포커스가 있을 때만 포인터 캡처를 요청할 수 있습니다. 이러한 이유로, 뷰에 특정 사용자 작업이 있을 때(예: onClick()
이벤트 중이거나 활동의 onWindowFocusChanged()
이벤트 핸들러에 있을 때) 포인터 캡처를 요청해야 합니다.
포인터 캡처를 요청하려면 뷰에서 requestPointerCapture()
메서드를 호출합니다. 다음 코드 예는 사용자가 뷰를 클릭할 때 포인터 캡처를 요청하는 방법을 보여줍니다.
Kotlin
fun onClick(view: View) { view.requestPointerCapture() }
자바
@Override public void onClick(View view) { view.requestPointerCapture(); }
포인터 캡처 요청이 성공하면 Android는 onPointerCaptureChange(true)
를 호출합니다.
캡처를 요청한 뷰와 동일한 뷰 계층 구조인 경우 앱의 포커스 뷰에 마우스 이벤트가 전달됩니다. 다른 앱은 ACTION_OUTSIDE
이벤트를 포함하여 캡처가 해제될 때까지 마우스 이벤트 수신을 중지합니다. Android는 일반적으로 마우스 이외의 소스에서 발생한 포인터 이벤트를 전달하지만, 마우스 포인트가 더 이상 표시되지 않습니다.
캡처한 포인터 이벤트 처리하기
뷰가 포인터 캡처를 획득하고 나면 Android는 마우스 이벤트를 전달하기 시작합니다. 포커스 뷰는 다음 작업 중 하나를 실행하여 이벤트를 처리할 수 있습니다.
- 맞춤 뷰를 사용하는 경우
onCapturedPointerEvent(MotionEvent)
를 재정의합니다. - 그 외의 경우에는
OnCapturedPointerListener
를 등록합니다.
다음 코드 예는 onCapturedPointerEvent(MotionEvent)
를 구현하는 방법을 보여줍니다.
Kotlin
override fun onCapturedPointerEvent(motionEvent: MotionEvent): Boolean { // Get the coordinates required by your app val verticalOffset: Float = motionEvent.y // Use the coordinates to update your view and return true if the event was // successfully processed return true }
자바
@Override public boolean onCapturedPointerEvent(MotionEvent motionEvent) { // Get the coordinates required by your app float verticalOffset = motionEvent.getY(); // Use the coordinates to update your view and return true if the event was // successfully processed return true; }
다음 코드 예는 OnCapturedPointerListener
를 등록하는 방법을 보여줍니다.
Kotlin
myView.setOnCapturedPointerListener { view, motionEvent -> // Get the coordinates required by your app val horizontalOffset: Float = motionEvent.x // Use the coordinates to update your view and return true if the event was // successfully processed true }
자바
myView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() { @Override public boolean onCapturedPointer (View view, MotionEvent motionEvent) { // Get the coordinates required by your app float horizontalOffset = motionEvent.getX(); // Use the coordinates to update your view and return true if the event was // successfully processed return true; } });
맞춤 뷰를 사용하든 리스너를 등록하든 상관없이 뷰는 트랙볼 기기가 전달한 좌표와 비슷하게 상대적 움직임(예: X/Y 델타)을 지정하는 포인터 좌표가 있는 MotionEvent
를 수신합니다. getX()
및 getY()
를 사용하여 좌표를 검색할 수 있습니다.
포인터 캡처 해제하기
앱의 뷰는 다음 코드 예와 같이 releasePointerCapture()
를 호출하여 포인터 캡처를 해제할 수 있습니다.
Kotlin
override fun onClick(view: View) { view.releasePointerCapture() }
자바
@Override public void onClick(View view) { view.releasePointerCapture(); }
가장 일반적으로 캡처를 요청한 뷰가 포함된 뷰 계층 구조에 포커스가 없어졌을 때 시스템에서는 개발자가 명시적으로 releasePointerCapture()
를 호출하지 않아도 캡처를 뷰에서 해제할 수 있습니다.