본문 바로가기
SW Programming/Android

[Android] 중첩된 리스트 사용시 스크롤 민감도 조절해보기(가로, 세로 스크롤)

by Crystal.k 2022. 5. 20.

[문제 상황]
1. 세로 리사이클러 리스트 뷰 + 가로 리사이클러 뷰(View Pager)
2. 세로 스크롤뷰 + 가로 리사이클러 리스트 뷰
3. 세로 스크롤 바텀시트 + 가로 리사이클러뷰(View Pager)

가로, 세로 스크롤이 중첩되면서 원하지 않는 액션이 되는 경우가 생김
즉 가로 슬라이드를 하는데, 세로가 스크롤 되는 문제가 발생함ㅜ_ㅜ


[조치 방향]
사람이 손가락으로 가로 슬라이드 하다보면 y축에 수평으로만 슬라이드 하진 않더군요ㅋㅋ
사선방향으로, 대각선으로 슬라이드 하다보니 이정도 대각선까지는 가로슬라이드로 먹었으면 좋겠다 싶더라고요
구글링하다보니 선행해서 고민하신분들이 있었고, 조치방법도 찾을 수 있었습니다.

가로스크롤을 하면 자식이 먹게 하자. 근데 buffer를 세로스크롤 일지라도, buffer만큼 가로스크롤을 더 먹게 하자.

/**
 * recyclerView를 가로 스크롤시 부모 리사이클러뷰가 세로로 스크롤 되는 현상을 줄이기 위해 사용하고 있음.
 *  ybuffer가 클수록 부모 리사이클러뷰가 세로 스크롤 먹지 되지 않게 된다.
 */
class RecyclerViewGestureListener(val recyclerView: RecyclerView,yBuffer:Int =10) : GestureDetector.SimpleOnGestureListener() {    private val Y_BUFFER = yBuffer
  
    override fun onScroll(
        e1: MotionEvent,
		e2: MotionEvent,
		distanceX: Float,
		distanceY: Float
    ): Boolean {
        if (abs(distanceX) >abs(distanceY)) {
            //onScroll true
            // Detected a horizontal scroll, disallow the recyclerview interceptor
            recyclerView.parent.requestDisallowInterceptTouchEvent(true)

        } else if (abs(distanceY) > Y_BUFFER) {
            // Detected a vertical scroll, allow the recyclerview interceptor
            //onScroll false
            recyclerView.parent.requestDisallowInterceptTouchEvent(false)
        }
        return super.onScroll(e1,e2,distanceX,distanceY)
    }
    
     override fun onDown(e: MotionEvent): Boolean {
        // Prevent ViewPager from intercepting touch events as soon as a DOWN is detected.
        // If we don't do this the next MOVE event may trigger the ViewPager to switch
        // tabs before this view can intercept the event.
        //onDown true
        recyclerView.parent.requestDisallowInterceptTouchEvent(true)
        return super.onDown(e)
    }
}

스크롤시 x와 y를 비교해서 가로 스크롤이 될 것인지 세로스크롤 먹게 할 것인지, 부모의 리사이클러뷰에 스크롤 이벤트를 전달할지를 설정해주도록 했습니다.

비슷한 케이스가 여러번 발생하길래 아래 처럼 RecyclerView의 확장 함수를 만들어서 자식리사이클러 뷰(가로스크롤)에 제스쳐 리스너를 설정해서 사용했습니다.

fun RecyclerView.setScrollSensitivity(yBuffer:Int = 10) {
        val gestureDetector 
        = GestureDetector(this.context, RecyclerViewGestureListener(this, yBuffer))
            this.addOnItemTouchListener(
                object: RecyclerView.OnItemTouchListener{
                    override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
                        gestureDetector.onTouchEvent(e)
                        return false
                    }
                    override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
                    }
                    override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
                    }
                }
            )
    }


10~30 정도의 버퍼를 줘서 사용하니깐 좀 더 단단하게 고정된 세로리스트 느낌을 받을 수 있엇고, 가로 스크롤하기가 수월해졌습니다.




[참고]
https://medium.com/mobile-app-development-publication/understanding-android-touch-flow-control-bcc413e6a57e
https://stackoverflow.com/questions/38466413/disable-viewpager-paging-when-child-recyclerview-scrolls-to-last-item
https://localcoder.org/vertical-viewpager2-with-recyclerview-scrolling-issue

반응형

댓글