구현하려고 했던 것

ViewPager2(이하 뷰페이저)를 이용한 Carousel 뷰를 구현하려고 하였음.

뷰페이저의 어댑터는 RecyclerView.Adapter를 상속받은 클래스를 사용하였음.

양 옆의 뷰가 좌우에서 조금씩 나오게 하기 위해 setPageTransformer로 각 뷰의 translationX를 조정하였음.

 

 

문제 상황

 

뷰페이저의 아이템을 클릭하면 새로운 액티비티를 띄우는데, 해당 액티비티를 종료 후 현재 액티비티로 돌아오면 뷰페이저의 아이템 뷰의 좌우 간격이 이상해지는 현상이 발생함.

 

 

원인

 

리사이클러뷰 어댑터를 사용하며 뷰를 재사용하는 과정에서, 기존 뷰에 설정한 translationX 값이 그대로 남아있었음.

예를 들어 나는 3번째 뷰를 표시한다고 생각했지만 실제로는 뷰를 재사용하면서 0번째 뷰의 translationX 값이 적용된 것.

 

setPageTransformer로 설정했던 것은 onResume 시에는 호출되지 않는다.

 

어떻게 보면 리사이클러뷰 아이템 뷰에서 체크박스 사용할 때 체크가 제대로 안 되는 문제와 동일한 원인이기도 함.

 

 

해결 방법

 

1. 어댑터에 아이템 뷰들의 translationX 값을 저장할 변수(이하 txList) 추가

- txList는 List가 갱신되거나 하면 이에 맞게 초기화되어야 함

 

2. 어댑터의 onBindViewHolder에서, 해당 itemView의 translationX 값을 txList에서 가져와서 적용

holder.itemView.translationX = txList[position]

 

2. 어댑터에 선택된 페이지를 기준으로, 나머지 translationX 값을 계산해서 txList 변경하는 메소드 추가

fun resetViewTranslationXList(center: Int) {
    txList = txList.mapIndexed { index, tx ->
        when(index-center) {
            -1 -> -pageTranslationX //현재 보이는 뷰 바로 앞 포지션
            1 -> pageTranslationX   //현재 보이는 뷰 바로 뒤 포지션
            else -> 0f  //현재 보이는 뷰 또는 화면에 보이지 않는 뷰
        }
    }.toMutableList()
}

 

 

3. 액티비티의 onPause() 메소드 내에 2에서 추가한 메소드를 호출하도록 함.

override fun onPause() {
    super.onPause()
    pagerAdapter.resetViewTranslationXList(requireBinding().pager.currentItem)
}

 

 

---- 또는 아래와 같이도 가능할 듯 ----

 

1. 어댑터에 currentItem 변수 추가 (뷰페이저가 선택한 아이템의 인덱스를 저장)

class BSSMapAdapter: RecyclerView.Adapter<BSSMapHolder>() {
    ...
    var currentItem = 0	//현재 뷰페이저에 표시되는 뷰
    ...
}

 

2. 어댑터의 onBindViewHolder에 현재 position에 따라 translationX 설정하는 코드 추가

when(holder.layoutPosition-currentItem) {
    -1 -> -pageMargin
    1 -> pageMargin
    else -> 0f
}.let {
    holder.itemView.translationX = it
}

 

3. onPause() 시 뷰페이저의 currentItem 값을 어댑터에 저장

pagerAdapter.currentItem = requireBinding().pager.currentItem

 

 

수정 후 결과

 

사용자가 페이지를 넘길 때는 setPageTransformer로 설정한 대로 각 아이템뷰의 translationX가 설정됨.

액티비티가 pause 상태가 될 때 각 뷰에 설정되어야 할 translationX 수치가 어댑터 내부 변수에 저장됨.

 

이후 다른 액티비티를 다녀오면 리사이클러의 onBindViewHolder가 실행되며,

저장했던 translationX 수치가 각 아이템 뷰에 적용됨.

 

* 일단 다른 액티비티를 다녀올 때만 문제가 발생해서 onPause()에 resetViewTransitionXList()를 호출하도록 하였으나,

뷰페이저의 페이지 변경 콜백에서 페이지가 선택된 이후에 호출하게 할 수도 있을 것임. 필요한 경우에 맞게 선택하면 될 듯.

+ Recent posts