본문 바로가기
안드로이드 자바

[Java][Android] VelocityTracker을 사용한 드래그 속도에 따른 동작 변화

by teamnova 2024. 12. 30.
728x90

https://developer.android.com/reference/android/view/VelocityTracker?_gl=1*wd3jpp*_up*MQ..*_ga*NDY2NzAyMTYwLjE3MzU1MjUxNTk.*_ga_6HH9YJMN9M*MTczNTUyNTE1OS4xLjAuMTczNTUyNTE1OS4wLjAuNTYxMTY4OTM1

 

VelocityTracker  |  Android Developers

 

developer.android.com

 

안드로이드 개발자 공식문서 VelocityTracker 내용

 

모션 이벤트의 속도를 추적하고 플링과 같은 다른 제스처를 구현하기 위한 도우미입니다. obtain()추적을 시작할 때 클래스의 새 인스턴스를 검색하는 데 사용합니다. 수신한 모션 이벤트를 . 로 넣습니다 addMovement(android.view.MotionEvent). 속도를 결정하려면 , , 또는 computeCurrentVelocity(int)와 같은 속도 게터 메서드를 호출한 다음 호출하여 다른 축 및/또는 포인터 ID에 대한 속도를 검색합니다.getXVelocity(int)getYVelocity(int)getAxisVelocity(int, int)

 

 

왜 사용할까요?

 

VelocityTracker를 사용하면 사용자가 화면을 드래그할 때의 속도를 측정하고, 그에 따라 다양한 동작을 제어할 수 있습니다. 이 기능을 활용하면 사용자 인터페이스(UI)에서 드래그 동작에 대한 반응 속도를 동적으로 조절할 수 있어 보다 직관적이고 반응적인 사용자 경험을 제공할 수 있습니다.

 

어디에 사용하면 좋을까요?

 

  • 게임: 게임에서는 캐릭터나 오브젝트를 드래그로 조작하는 경우가 많습니다. 빠르게 드래그하면 멀리 이동하고, 느리게 드래그하면 짧게 이동하는 방식으로 게임 내 상호작용을 자연스럽게 만들 수 있습니다. 예를 들어, 속도에 따라 물리적인 상호작용을 다르게 설정하거나, 드래그 속도에 맞춰 점수를 계산할 수 있습니다.
  • UI 요소 상호작용: 이메일, 사진, 파일 등을 삭제할 때 드래그 속도에 따라 동작을 달리할 수 있습니다. 예를 들어, 빠르게 드래그하면 즉시 삭제되고, 느리게 드래그하면 삭제 전에 확인 메시지가 표시되는 방식으로 사용자에게 더 직관적인 피드백을 제공할 수 있습니다.
  • 애니메이션 및 비주얼 피드백: 애플리케이션에서 애니메이션을 구현할 때, 드래그 속도에 따라 애니메이션의 속도를 조절할 수 있습니다. 예를 들어, 빠르게 드래그하면 애니메이션이 더 빠르게 진행되고, 느리게 드래그하면 애니메이션이 느리게 진행되도록 할 수 있습니다.
  • 디자인 및 프로덕트 시뮬레이션: 디자인 또는 제품 시뮬레이션 앱에서 드래그 속도에 따라 객체의 이동 범위나 크기를 다르게 설정할 수 있습니다. 예를 들어, 사용자가 빠르게 드래그하면 큰 변화를, 느리게 드래그하면 세밀한 조정을 할 수 있습니다.

드래그 속도에 맞춰 토스트메세지를 출력하도록 했습니다.

이를 통해 드래그에 속도에 맞춰 각기 다른 작업을 할 수 있습니다.

 

MainActivity

 

import android.os.Bundle;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    TextView textView, speedTextView, thresholdTextView;
    VelocityTracker velocityTracker;
    float lastX;
    float lastY;
    float moveThreshold = 100;  // 이동 임계값 (예: 100px)
    float speedThreshold = 5000;  // 속도 임계값 (예: 5000)

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.textView);
        speedTextView = findViewById(R.id.speedTextView);  // 속도를 표시할 TextView
        thresholdTextView = findViewById(R.id.moveThresholdTextView);  // 임계값을 표시할 TextView

        // 초기값 설정
        textView.setText("드래그를 시작하세요!");
        speedTextView.setText("속도: 0");
        thresholdTextView.setText("이동 임계값: " + moveThreshold + "px, 속도 임계값: " + speedThreshold + "px/s");
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // VelocityTracker 초기화
        if (velocityTracker == null) {
            velocityTracker = VelocityTracker.obtain();
        }

        // 이벤트 처리
        velocityTracker.addMovement(event);

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 터치 시작 시 위치 저장
                lastX = event.getRawX();
                lastY = event.getRawY();
                break;

            case MotionEvent.ACTION_MOVE:
                // 드래그 중인 위치 계산
                float deltaX = event.getRawX() - lastX;
                float deltaY = event.getRawY() - lastY;

                // 텍스트 뷰의 새로운 위치로 이동
                textView.setX(textView.getX() + deltaX);
                textView.setY(textView.getY() + deltaY);

                // 속도 계산
                velocityTracker.computeCurrentVelocity(1000); // 1000은 1초 간격
                float velocityX = velocityTracker.getXVelocity();
                float velocityY = velocityTracker.getYVelocity();
                float speed = (float) Math.sqrt(velocityX * velocityX + velocityY * velocityY);

                // 속도에 따른 토스트 메시지
                if (speed > 10000) {
                    Toast.makeText(this, "속도가 10000입니다. 빠르게 드래그하고 있습니다!", Toast.LENGTH_SHORT).show();
                } else if (speed > 5000) {
                    Toast.makeText(this, "속도가 5000입니다. 조금 빠르게 드래그하고 있습니다.", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(this, "속도가 느립니다. 천천히 드래그하고 있습니다.", Toast.LENGTH_SHORT).show();
                }

                // 속도 값 텍스트 뷰에 실시간으로 표시
                speedTextView.setText("속도: " + (int) speed + " px/s");

                // 이동 임계값을 체크하고 텍스트 뷰에 실시간으로 표시
                if (Math.abs(deltaX) > moveThreshold || Math.abs(deltaY) > moveThreshold) {
                    thresholdTextView.setText("이동 임계값 초과! 속도: " + (int) speed + " px/s");
                } else {
                    thresholdTextView.setText("이동 임계값 미만. 속도: " + (int) speed + " px/s");
                }

                // 마지막 위치 갱신
                lastX = event.getRawX();
                lastY = event.getRawY();
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                // 터치 종료 시 VelocityTracker 리셋
                velocityTracker.recycle();
                velocityTracker = null;
                break;
        }

        return true;
    }
}

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Drag Me!"
        android:textSize="24sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/speedTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="속도: 0"
        android:textSize="16sp"
        android:textColor="#000"
        app:layout_constraintBottom_toTopOf="@+id/textView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/moveThresholdTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="이동 임계값: 0"
        android:textSize="16sp"
        android:textColor="#000"
        app:layout_constraintBottom_toTopOf="@+id/speedTextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

 

시연영상