수평 RecyclerView와 SwipeRefreshLayout 충돌로 인해 RecyclerView가 굴러가기 어려운 문제 해결

8825 단어 RecyclerViewAndroid
본 보도는 and factory Advent Calendar 2019의 11일째 보도입니다.어제는 MatsuNaoPen선생님의 adb 명령으로 터미널 조작입니다.

문제



내가 개발한 응용 프로그램은 상술한 화면이 있다.그냥 Swipe Refresh Layout으로 어렵지 않은 Recycler View를 둘러싸는 거예요.
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    android:id="@+id/swipe_refresh_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
나는 이렇게 될 것이라고 생각한다.
그러던 어느 날 사내 점검에서'자꾸 굴러다니기 힘들다'고 지적을 받았어요.
확실히 비스듬한 방향으로 굴리면 가로로 굴리고 Swipe Refresh Layout 반응으로 사용하기 어려울 것 같아서 수정해 봤어요.

이유는 SwipeRefreshLayout입니다.


Swipe Refresh Layout이 스크롤에 지나치게 민감하게 반응하는 이유를 발견했습니다.따라서 사용자 정의 Swipe Refresh Layout은 손가락이 왼쪽으로 미끄러질 때만 반응하도록 수정됩니다.

수정 방법


코드 여기 있어요.
class OnlyVerticalSwipeRefreshLayout(context: Context, attrs: AttributeSet) :
    SwipeRefreshLayout(context, attrs) {

    private var touchSlop: Int = ViewConfiguration.get(context).scaledTouchSlop
    private var prevX: Float = 0.toFloat()
    private var declined: Boolean = false

    override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                prevX = MotionEvent.obtain(event).x
                declined = false // New action
            }

            MotionEvent.ACTION_MOVE -> {
                val eventX = event.x
                val xDiff = abs(eventX - prevX)
                if (declined || xDiff > touchSlop) {
                    declined = true // Memorize
                    return false
                }
            }
        }
        return super.onInterceptTouchEvent(event)
    }
}

요점1: scaledTouchSlop


scaledTouchSlop 공식 문서에 따라
Distance in pixels a touch can wander before we think the user is scrolling
쓰다
ユーザーがスクロールを開始したとOSが認識する前にスクロールできるピクセル距離
이렇게 해석했습니다.어쨌든 굴러가기 시작하는 순간 손가락의 이동 거리다.

포인트 2: onInterceptTouch 이벤트


onInterceptTouchEvent 상위 뷰는 하위 뷰에서 발생하는 이벤트를 모니터링합니다.또한 터치 이벤트가 부모 보기 이벤트라면true를 되돌려주고, 부모 보기가 이벤트를 빼앗지 않으면false를 되돌려줍니다.이 예에서 하위 뷰는 가로 RecyclerView이고 상위 뷰는 SwipeRefreshLayout입니다.

요점3: 손가락의 이동 거리에 따라 Swipe Refresh Layout이 사건을 빼앗을지 판단

when (event.action) {
    MotionEvent.ACTION_DOWN -> {
        prevX = MotionEvent.obtain(event).x
        declined = false // New action
    }

    MotionEvent.ACTION_MOVE -> {
        val eventX = event.x
        val xDiff = abs(eventX - prevX)
        if (declined || xDiff > touchSlop) {
            declined = true // Memorize
            return false
        }
    }
}
바로 이 부분입니다.
ACTION_DOWN에서 첫 번째 탭 위치를 유지합니다.ACTION_OVE에서 x 방향의 이동 거리를 계산하고 이동 거리가 touchSlop을 초과하면 이벤트를 RecyclerView에 넣습니다.
마지막으로 SwipeRefreshLayout을 OnlyV-ticalSwipeRefreshLayout으로 변경하면 수정이 완료됩니다.

웹 페이지 정보

좋은 웹페이지 즐겨찾기