Android/XML + Android

RecyclerView와 SnapHelper - SnapHelper 커스텀

chattymin 2023. 12. 6. 12:56
728x90
반응형

아래 두 동영상의 차이점이 뭘까?

 

호로롤롤롤 vs 뙇!딲! 뙇!

 

왼쪽은 각 항목들이 호롤롤로롤로로 힘없이 넘어간다.

반면 오른쪽 동영상은 한칸씩 딱! 딱! 맞춰서 넘어간다.

이걸 구현하기 위해서 대부분은 ViewPager를 사용할 것이다. 이 상황에서는 맞는 방법이다.

 

 

하지만 이런 UI라면?

 

Grid처럼 한 페이지에 여러 값이 존재하고, 그게 한칸씩 딱딱 맞춰서 넘어가야한다.

그리고 중요한점.

가운데 정렬이 아닌, 왼쪽 정렬이다

이거땜에 개빡쳤었다.

 

 

 

💡 이럴때는 ViewPager를 쓰는것도 나쁘지 않지만, RecyclerView도 활용해보자.

 

왜 굳이 ViewPager를 안쓰고 RecyclerView를 쓸까?

 

구현하기가 너무 귀찮았다그래서 RecyclerView를 Grid레이아웃으로 작성하고, Custom해서 VIewPager효과를 내자! 생각했다.

 

ViewPager 내부 코드를 뜯어보면 애초에 RecyclerView를 커스텀해서 만들었다 ㅋㅋㅋ

즉, ViewPager를 직접 만들어보면서 원리를 익히기 아 주 조 아

 

그리고 중요한점!

ViewPager 내부에 RecyclerView를 넣을 생각을 하니까 벌써부터 구조가 보기 싫어지고 손발이 벌벌 떨렸다.

 

 

👀 어떻게 RecyclerView를 ViewPager처럼 쓸까?

 

grid레이아웃은 다들 할 수 있을거라고 믿는다. 화이팅!

 

RecyclerView에는 SnapHelper라는게 존재한다.

쉽게 말해서 ViewPager처럼 한칸씩 움직이게 해주는 그친구다.

그래서 SnapHelper를 선언해주고, RecyclerView를 연결해주면 끝이다. 참 쉽죠?

private lateinit var attentionAdapter: TodayAttentionAdapter
private lateinit var attentionSnapHelper: LinearSnapHelper // 이친구가 오늘의 주인공

private fun initAttentionAdapter() {
        attentionAdapter = TodayAttentionAdapter(requireContext())
        attentionAdapter.submitList(viewModel.mockTodayAttentionList)

        binding.rvTodayAttention.addItemDecoration(
            EdgeMarginItemDecoration(
                edgeMargin = 24.dpToPx,
                itemMargin = 12.dpToPx
            )
        )
        
        attentionSnapHelper = LinearSnapHelper() // 이케 선언하고
        attentionSnapHelper.attachToRecyclerView(binding.rvTodayAttention) // 이케 리사이클러뷰랑 연결
        binding.rvTodayAttention.adapter = attentionAdapter
    }

이걸 연결하면 끝!

한번 실행시켜 본다면 제일 위에 올려둔 것 처럼 가운데 정렬로 화면에 나타날 것이다.

 

😢 그러면 왼쪽으로 정렬은 어케해요…?

 

SnapHelper를 커스텀 하면 됩니다!

class CustomSnapHelper : LinearSnapHelper() {

    override fun calculateDistanceToFinalSnap(layoutManager: RecyclerView.LayoutManager, targetView: View): IntArray? {
        // 왼쪽 정렬을 위해 거리를 계산
        val out = IntArray(2)
        if (layoutManager.canScrollHorizontally()) {
            out[0] = layoutManager.getDecoratedLeft(targetView) - layoutManager.paddingStart
        }
        return out
    }

    override fun findTargetSnapPosition(layoutManager: RecyclerView.LayoutManager?, velocityX: Int, velocityY: Int): Int {
        // 기본적인 타겟 포지션을 찾는 로직은 유지
        return super.findTargetSnapPosition(layoutManager, velocityX, velocityY)
    }
}

calculateDistanceToFinalSnap을 커스텀 → 정렬 위치 조정!

728x90
반응형