본문 바로가기

Study/Android

[Android/Kotlin] ViewPager2 - Infinite Loop

이 글에서는 어떻게 무한로딩 ViewPager2를 만드는지,

Infinite Loop ViewPager2를 만드는 방법에 대해 다룰 것이다.

말만 확장이지 일반적인 ViewPager2와 별 차이 없고

약간의 트릭으로 무한 루프처럼 보이게 할 뿐이다.

 

1. FragmentStateAdapter 추가

위에서 말했듯이 약간의 트릭으로 무한 루프처럼 보이게 만들어야 한다.

Adapter를 붙일 때 fragment의 수를 엄청 많이 만들어서

사용자가 페이지를 아무리 넘겨도 끝이 없는 것처럼 만드는 방법이다.

찝찝하지만 며칠동안 찾아본 예제 중에 내가 원하는대로 작동하는 예제는 이것밖에 없는 것 같다.

FragmentStateAdapter에서 getItemCount()의 반환값을 Int.MAX_VALUE로 지정해서

무한 루프 ViewPager2처럼 보이게 해준다.

 

class FirstFragmentStateAdapter(fragmentActivity: FragmentActivity)
    : FragmentStateAdapter(fragmentActivity) {

    var fragments = mutableListOf<CalendarFragment>()
    val firstFragmentPosition = Int.MAX_VALUE / 2
    
    override fun getItemCount(): Int = fragments.size

    override fun createFragment(position: Int): Fragment = 
        CalendarFragment(fragments[position.rem(fragments.size)], position)

    fun updateFragments(list: List<CalendarFragment>) {
        fragments.apply {
            clear()
            addAll(list)
        }
        notifyDataSetChanged()
    }

}​

 

Stackoverflow에서 본 방법인데

기존의 FragmentStateAdapter를 사용하는 방법처럼

Adapter를 사용할 화면(FirstFragment)에서 Adapter에 List<Fragmet>()를 넘겨

ViewPager2에 넣을 Fragment의 개수를 정하는게 아니라

Adapter의 getItemCount에서 fake 값으로 ItemCount를 반환하기 때문에

굳이 updateFragmets()는 필요없을 것 같아 없앴다.

 

class FirstFragmentStateAdapter(fragmentActivity: FragmentActivity)
   : FragmentStateAdapter(fragmentActivity) {

    private val pageCount = Int.MAX_VALUE
    val firstFragmentPosition = Int.MAX_VALUE / 2

    override fun getItemCount(): Int = Int.MAX_VALUE

    override fun createFragment(position: Int): Fragment {
        val calendarFragment = CalendarFragment()
        calendarFragment.pageIndex = position
        return calendarFragment
    }

}​

 

createFragment()에서 CalendarFragment를 반환하면서 position값을 CalendarFragment의 pageIndex로 지정한다.

pageIndex는 CalendarFragment에서 캘린더의 월을 지정하는 값으로 쓰인다.

 

 

 

2.  FragmentStateAdapter 연결

이제 FirstFragment의 ViewPager2에 FirstFragmentStateAdapter를 붙이자.

 

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        initView()
    }

    fun initView() {
       val firstFragmentStateAdapter
            = FirstFragmentStateAdapter(requireActivity())
        calendarViewPager.adapter = firstFragmentStateAdapter
        calendarViewPager.orientation = ViewPager2.ORIENTATION_VERTICAL
        calendarPagerFragmentStateAdapter.apply {
           firstFragmentStateAdapter.setCurrentItem(this.firstFragmentPosition, false)
        }
    }​

 

MainActivity - ViewPager2의 orientation이 horizontal이기 때문에

Swipe 이벤트가 겹치는걸 피하기 위해 orientation을 Vertical로 지정했다.

그리고 FirstFragment에서 ViewPager2를 위아래로 넉넉하게 이동할 수 있도록

FirstFragmentStateAdapter에서 변수로 만들어뒀던 firstFragmentPosition으로

ViewPager2의 currentItem을 지정한다.

currentItem을 firstFragmentPosition으로 지정하는 이유는 

firstFragmentPosition의 값은 Int.MAX_VALUE/2이며 이는 Int.MAX_VALUE의 딱 중간위치이기 때문이다.

 

Int.MAV_VALUE / 2의 위치

 

 

 

Doc - ViewPager의 setCurrentItem

 

참고로  setCurrentItem()은 인자값을 1개 또는 2개로 사용할 수 있는데 테스트해보니

calendarViewPager.currentItem = this.firstFragmentPosition으로 지정하면

calenderViewPager의 item인 CalendarFragment가 로드될 때

firstFragmentPosition로 이동하는 animation이 보이며,

calendarViewPager.setCurrentItem(this.firstFragmentPosition, false)로 지정하면

animation 없는 차이가 있었다. 

나는 앱을 실행하면 바로 달력화면이 보여야 하므로 animation이 안보이게 처리했다.

 

 

 

 

여기까지 사용자로하여금 ViewPager2를 무한으로 사용하는 것처럼 느끼게 해주는 트릭을 이용하여

간단하게 Infinite Loop ViewPager2를 만들었다.

 

참조 : https://furang-note.tistory.com/28