Tempo Di Valse

[Android] Custom View 에서의 롱클릭 후 드래그 허용하기 본문

개발/Android

[Android] Custom View 에서의 롱클릭 후 드래그 허용하기

TempoDiValse 2023. 6. 7. 21:38

롱클릭이야 onTouchEvent 가 아닌 GestureDetector 를 통해서 편하게 옵션을 제공해 줄 수 있지만 내가 필요했던 이벤트는 바로,

 

특정 위치를 롱클릭 한 후, 상태를 활성화 하여 드래그 하는 것이다.

 

적당한 예시가 있다면 화면을 길 게 누르면 편집상태가 되어 그 상태에서 손을 떼지 않고 그림을 그린다거나 할 때 쓸 수 있을 것 같다.

드래그 함과 동시에 받아오게 되는 x, y 좌표 값에 따라 invalidate 시킬 예정이었기 때문에 필요한 로직이었다. 여기서의 드래그는 기본적으로 제공해주는 그 Drag 가 아닌 이벤트이다. 그래서 롱클릭 후에 onScroll 이나 onTouchEvent 에서 ACTION_MOVE 를 통해서 처리하면 되지 않을까 하고 구현을 해 보았는데...

 

롱클릭을 한 후에 다른 모션으로 바로 이어지지 않았다. 롱클릭 후에도 ACTION_MOVE가 계속 들어올 수 있는 줄 알았지만 둘은 별도로 움직일 수 밖에 없어서 손을 화면에서 떼고 다시 붙여야 작동을 했었다. 그거는 일반 ACTION_DOWN 과 다른이 없기 때문에 쓸모가 없어서 어떻게 하는 지 찾아보니 나와 같은 고민들을 하는 사람들이 많았다.

 

그래서 알아본 결과 다음의 솔루션을 발견할 수 있었다.

private var isLongPressed = false

private val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
        override fun onDown(e: MotionEvent): Boolean = true
        override fun onScroll(e1: MotionEvent, e2: MotionEvent, dx: Float, dy: Float): Boolean = true
        override fun onFling(e1: MotionEvent, e2: MotionEvent, vx: Float, vy: Float): Boolean = true

        override fun onLongPress(e: MotionEvent) {
            if(!isLongPressed)
                isLongPressed = true

        }
    }


override fun onTouchEvent(event: MotionEvent?): Boolean
        = event?.run {
            if(detector.onTouchEvent(this)) true
            else{
                when(actionMasked){
                    MotionEvent.ACTION_MOVE -> {
                        // 롱 클릭 후 드래그 하기 위해서는 ACTION_MOVE 할 때,
                        // Cancel 이벤트를 MotionEvent 에 전달해주는 방식으로 트릭을 사용한다고 한다.
                        if(isLongPressed){
                            isLongPressed = false

                            val cancelEvent = MotionEvent.obtain(this).apply {
                                action = MotionEvent.ACTION_CANCEL
                            }

                            detector.onTouchEvent(cancelEvent)
                        }
                    }
                }

                super.onTouchEvent(this)
            }
        } ?: super.onTouchEvent(event)

롱클릭을 활용하기 위해서 GestureDetector 를 활용하였고, 드래그에 해당 되는 것은 GestureDetector 의 onScroll 을 통해서 관리할 수 있었다. 로직의 프로세스는 다음과 같다.

 

- 롱 클릭 이벤트를 GestureDetector 로 받는다. 롱 클릭이 들어왔다는 것을 onTouchEvent 에서도 확인해야 하기 때문에 플래그를 사용한다.

- onTouchEvent 에서 action 값이 ACTION_MOVE 인 상태를 받아준다. 단, 롱 클릭 이후에 들어온 ACTION_MOVE 를 받기 위해서 플래그를 이용하여 일반 ACTION_MOVE 와 걸러줄 수 있도록 한다.

- 걸러진 이벤트가 하나 들어오게 되면, 해당 이벤트의 action 을 ACTION_CANCEL 로 변경한 후, GestureDetector 에 전달한다. 이 다음부터 onScroll 에서 터치를 받고 움직이는 감도를 받을 수 있다.

 


DragListener 라는 것이 View 에도 존재하긴 하지만, 다른 방식의 드래그가 필요한 경우헤는 사용할 만 한 로직인 것 같아 남겨두도록 한다.

반응형
Comments