[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를 만들었다.