Simple&Natural

[Android] ViewPager2 와 RecyclerView 사용 시 부드럽게 스크롤하기 본문

안드로이드(Android)/이것저것 만들어보기

[Android] ViewPager2 와 RecyclerView 사용 시 부드럽게 스크롤하기

Essense 2023. 3. 9. 17:52
728x90

가로 방향의 ViewPager2 내 세로 방향의 RecyclerView를 가지고 있는 fragment pager 를 사용하던 중 개선사항이 하나 들어왔다.
 
세로 방향으로 스크롤했는데 가끔씩 가로로 스크롤이 된다는것.
 
이 원인은 recyclerView와 viewPager2가 터치 이벤트를 서로 경쟁적으로 뺏어가기 때문이다.
세로로 스크롤을 했음에도 viewPager2가 터치 이벤트를 가로채버리는 것.
이를 해결하기 위해서는 recyclerView에서 먼저 스크롤 이벤트를 감지한 후
세로 방향의 스크롤이 더 크면 recyclerView에서 터치 이벤트를 처리하도록 하면 된다
 
계산해보면 수직 45도 각도를 기준으로 이보다 각도가 작게 스크롤하면 viewPager2가,
이보다 각도가 커지면 recyclerView가 터치이벤트를 처리한다.
 
이는 안드로이드에서 제공하는 requestDisallowInterceptTouchEvent 속성을 활용하면 된다.
https://developer.android.com/reference/android/view/ViewGroup#requestDisallowInterceptTouchEvent(boolean) 

ViewGroup  |  Android Developers

developer.android.com

 
아래는 소스코드.
이를 recyclerView에 감싸서 사용하면 된다.
끗.
 
SmoothVerticalScrollingHost.kt

import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.widget.FrameLayout
import com.cobak.android.AppLog
import kotlin.math.abs

class SmoothVerticalScrollingHost: FrameLayout {
    constructor(context: Context): super(context)
    constructor(context: Context, attrs: AttributeSet?): super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int): super(context, attrs, defStyleAttr, defStyleRes)

    private var dx = 0f
    private var dy = 0f
    private var oldX = 0f
    private var oldY = 0f

    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        ev?.let {
            when (it.action) {
                MotionEvent.ACTION_DOWN -> {
                    dx = 0f
                    dy = 0f
                    oldX = it.x
                    oldY = it.y
                    parent.requestDisallowInterceptTouchEvent(true)
                }
                MotionEvent.ACTION_MOVE -> {
                    dx += abs(ev.x - oldX)
                    dy += abs(ev.y - oldY)
                    oldX = ev.x
                    oldY = ev.y
                    parent.requestDisallowInterceptTouchEvent(dy > dx)
                    AppLog.d("ACTION_MOVE -> dx: $dx, dy: $dy, oldX: $oldX, oldY: $oldY ${dy > dx}")
                }
            }
        }
        return super.onInterceptTouchEvent(ev)
    }
}

 

728x90