터치 및 포인터 움직임 추적

Compose 방식 사용해 보기
Jetpack Compose는 Android에 권장되는 UI 도구 키트입니다. Compose에서 터치 및 입력을 사용하는 방법을 알아보세요.
<ph type="x-smartling-placeholder"></ph> 동작 → 를 통해 개인정보처리방침을 정의할 수 있습니다.

이 과정에서는 터치 이벤트의 움직임을 추적하는 방법을 설명합니다.

새로운 onTouchEvent() 드림 인코더-디코더가 ACTION_MOVE 일정 현재 터치 접촉 위치, 압력 또는 크기가 변경될 때마다. 따라서 일반 동작 감지에 설명된 대로, 이러한 이벤트는 다음 매개변수의 MotionEvent 매개변수 onTouchEvent()

손가락 기반 터치가 항상 가장 정확한 상호작용 형태는 아니기 때문에 터치 이벤트 감지는 단순한 접촉보다는 움직임에 더 기반하는 경우가 많습니다. 앱에서 동작 기반 동작 (예: 스와이프)과 움직임이 없는 동작 (예: 한 번 탭)으로, Android에는 터치 슬롭 터치 슬롭은 사용자가 터치할 수 있는 거리(픽셀 단위)를 나타냄 이동에 기반하는 동작으로 해석되기 전에 여러 번 돌아다녀야 합니다. 자세한 내용은 자세한 내용은 화면의 터치 이벤트 관리 ViewGroup과 일치하는 항목이 있습니다.

동작에서 움직임을 추적하는 방법에는 여러 가지가 있으며 애플리케이션의 니즈를 충족할 수 있습니다 예를 들어 다음과 같습니다.

  • 포인터의 시작 및 종료 위치(예: 화면 이동) 점 A에서 B 지점으로 이동합니다.
  • 포인터가 이동하는 방향(X 및 Y에 의해 결정됨) 좌표입니다.
  • 기록. 동작 기록의 크기를 확인하려면 메서드 MotionEventgetHistorySize() 그런 다음 각 조건의 위치, 규모, 시간 및 압력을 이전 이벤트를 사용하여 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. It's 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. It's 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):

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 is
    // 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 is
  // 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 is
    // 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 is
    // successfully processed.
    return true;
  }
});

사용자 지정 뷰를 사용하든 리스너를 등록하든 간에 뷰는 상대적 움직임(예: X)을 지정하는 포인터 좌표가 있는 MotionEvent 또는 Y 델타를 전달할 수 있습니다. 다음과 같은 작업을 할 수 있습니다. 를 사용하여 좌표를 검색할 수 있습니다. getX()getY()

포인터 캡처 해제하기

앱의 뷰는 다음을 호출하여 포인터 캡처를 해제할 수 있습니다. releasePointerCapture()님, 다음과 같습니다.

Kotlin

override fun onClick(view: View) {
    view.releasePointerCapture()
}

자바

@Override
public void onClick(View view) {
    view.releasePointerCapture();
}

개발자가 명시적으로 하지 않아도 시스템은 캡처를 뷰에서 해제할 수 있습니다. releasePointerCapture()를 호출하는 이유는 일반적으로 뷰 계층 구조가 캡처가 포커스를 잃는 뷰가 포함됩니다.