프로그래밍/Android

[안드로이드] View를 터치 입력에 따라 회전하기 (핸들 구현)

Lou Park 2022. 8. 24. 01:32

touch listener를 이용한 핸들구현

이번에 만들어볼 것은 사용자의 터치 입력에따라 회전하는 핸들뷰다. 다양하게 활용할 수 있는데, 회사에서 뽑기 기계를 만들어보자는 아이디어에서 삼각함수로 뽀리면 되겠는데?! 라는 생각에서 만들어서 검증해보았다.

 

 

xml은 터치 입력을 받을 view_touch와 ImageView인 handle 단 두개로 이루어져있다.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <View
        android:id="@+id/view_touch"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:background="#eee"
        />

    <ImageView
        android:id="@+id/handle"
        android:layout_width="160dp"
        android:layout_height="160dp"
        android:src="@drawable/handle"/>
</FrameLayout>

 

center에는 touch view의 가운데 지점을 저장하고, handle 이미지 뷰를 정 중앙으로 옮긴다.

val viewTouch = findViewById<View>(R.id.view_touch)
val handle = findViewById<ImageView>(R.id.handle)

val center = PointF()
viewTouch.viewTreeObserver.addOnGlobalLayoutListener(object: ViewTreeObserver.OnGlobalLayoutListener {
    override fun onGlobalLayout() {
        center.x = viewTouch.x + viewTouch.width / 2
        center.y = viewTouch.y + viewTouch.height / 2

        handle.x = center.x - handle.width / 2
        handle.y = center.y - handle.height / 2

        viewTouch.viewTreeObserver.removeOnGlobalLayoutListener(this)
    }
})

 

 

우리가 궁금한건 center가 원점, 사용자가 view를 터치한 좌표가 x,y라고 할때 중심으로부터 x,y로 향하는 선과 x축이 이루는 각도 θ의 값이다. 이 각도를 degree로 변환하여 rotation에 할당하면 된다.

 

이럴때 atan2 함수를 사용할 수 있는데, atan2는 역탄젠트 값(radian)을 반환하는데, 이것은 아래 그림처럼 정확히 우리가 원하는 θ의 값이다.

var prevDegree = 0.0
viewTouch.setOnTouchListener { v, event ->
    val dX = event.x - center.x.toDouble()
    val dY = event.y - center.y.toDouble()

    var degree = Math.toDegrees(atan2(dY, dX))

    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            prevDegree = degree
        }
        MotionEvent.ACTION_MOVE -> {
            handle.rotation = (handle.rotation + (degree - prevDegree)).toFloat()
            prevDegree = degree
        }
    }
    return@setOnTouchListener true
}

dX, dY는 위 그림에서 x,y에 해당된다.

atan2(dY, dX)의 결과값 단위가 -PI와 PI사이의 Radian 값이기 때문에 이를 각도로 변환하는 과정이 한번 더 필요하다.

 

터치를 하기 시작했을때(ACTION_DOWN) 현재 각도를 저장, 드래그하며 움직일때(ACTION_MOVE) 이전각도와 현재각도의 차이만큼 회전값(rotation)에 더해주면 핸들이 자연스럽게 손가락을 따라 움직이기 시작할 것이다.