본문 바로가기

이상/Andrioid

[Android/Kotlin] ViewPager2 - FragmentStateAdapter 사용하기 (with TabLayout)

반응형

이전 글에서 RecyclerView.Adapter을 이용하여 ViewPager2를 만드는 방법에 대해 알아봤다.

 

이번에는 FragmentStateAdapter를 이용하여 ViewPager2를 만들고 TabLayout을 붙여보려고 한다.

 

ViewPager에서 봤듯 FragmentStatePagerAdapter는 deprecated 되었으며

 

FragmentStateAdapter 로 대체되었다.

 

FragmentStatePagerAdapter -> FragmentStateAdapter

PagerAdapter -> RecyclerView.Adapter

addPageChangeListener -> registerOnPageChangeCallback

 

때문에 FragmentStatePagerAdapter와 비슷하게 작동한다.

 

그리고 Docs에서 FragmentStateAdapter를 찾아보면

 

FragmentStateAdapter의 부모 클래스가 RecyclerView.Adapter라고 나와있다.

 

RecyclerView의 Paging 버전 같은 느낌이다.

 

Docs의 FragmentStateAdapter

 

 

1. FragmentStateAdapter

 

FragmentStateAdapter는 RecyclerView.Adapter로 구현할 때와 다르게 인자 값으로 Fragment를 넘겨줘야 한다.

 

때문에 Fragment를 하나의 View로써 사용하는 RecyclerView와 같은 느낌을 받았다.

 

 

Adapter 내부에 Fragment를 담을 ArrayList<Fragment> 변수를 만들고 이를 통해 Fragment들을 핸들링한다.

 

FragmentStateAdapter의 부모 클래스가 RecyclerView.Adapter이기 때문에

 

fragments에 대해 add 또는 remove 할 때 적절한 notifyItem 메소드를 호출해준다.

 

PagerFragmentStateAdapter.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class PagerFragmentStateAdapter(fragmentActivity: FragmentActivity): FragmentStateAdapter(fragmentActivity) {
 
    var fragments : ArrayList<Fragment> = ArrayList()
 
    override fun getItemCount(): Int {
        return fragments.size
    }
 
    override fun createFragment(position: Int): Fragment {
        return fragments[position]
    }
 
    fun addFragment(fragment: Fragment) {
        fragments.add(fragment)
        notifyItemInserted(fragments.size-1)
    }
 
    fun removeFragment() {
        fragments.removeLast()
        notifyItemRemoved(fragments.size)
    }
 
}
cs

 

 

 

2. ViewPagerFragment

 

ViewPagerFragment에 ViewPager2를 꽉 차게 두고 아래 3개의 Fragment를 띄우려고 한다.

 

ViewPager2의 Item Fragment

 

fragment_view_pager.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    tools:context=".ViewPagerFragment">
 
    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
 
</LinearLayout>
 
 
cs

 

 

pagerAdapter 변수를 PagerFragmentStateAdapter() 로 생성하여

 

3개의 Fragment에 addFragment() 한 후 viewPager.adapter에 지정한다.

 

ViewPagerFragment.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 
class ViewPagerFragment : Fragment() {
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }
 
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_view_pager, container, false)
    }
 
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
    
        val pagerAdapter = PagerFragmentStateAdapter(requireActivity())
        // 3개의 Fragment Add
        pagerAdapter.addFragment(FirstFragment())
        pagerAdapter.addFragment(SecondFragment())
        pagerAdapter.addFragment(ThirdFragment())
        // Adapter 
        viewPager.adapter = pagerAdapter
 
        viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
            override fun onPageSelected(position: Int) {
                super.onPageSelected(position)
                Log.e("ViewPagerFragment""Page ${position+1}")
            }
        })
    }
 
}
 
cs

 

 

 

ViewPager2+PagerFragmentStateAdapter 결과

 

참고로 ViewPager2를 Right-to-Left로 설정하고 싶을 경우

 

xml에서 ViewPager2의 layoutDirection값을 rtl로 지정하면 된다.

layoutDirection 설정
ViewPager2 Right-to-Left 설정

 

 

3. TabLayout

 

TabLayout은 HorizontalScrollView를 확장하여 만들어졌으며

 

때문에 기본적으로 Horizontal로 Scroll할 수 있다.

 

화면에 TabLayout을 추가하여 ViewPager2와 연동해보자.

 

 

xml에 TabLayout을 추가한다.

 

fragment_view_pager.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    tools:context=".ViewPagerFragment">
 
    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="40dp"/>
 
    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
 
</LinearLayout>
 
 
cs

 

기존의 ViewPager에서는 내부에 childView로 TabLayout을 넣고 gravity를 지정하여 연동할 수 있었는데

 

ViewPager2에서는 그렇게 할 경우 ViewPager2 does not support direct child views 에러가 뜨면서 앱이 죽는다.

 

 

추가한 TabLayout에 대해 구현하고 attach()한다.

 

ViewPagerFragment.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class ViewPagerFragment : Fragment() {
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }
 
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_view_pager, container, false)
    }
 
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
    
        val pagerAdapter = PagerFragmentStateAdapter(requireActivity())
        // 3개의 Fragment Add
        pagerAdapter.addFragment(FirstFragment())
        pagerAdapter.addFragment(SecondFragment())
        pagerAdapter.addFragment(ThirdFragment())
        // Adapter 
        viewPager.adapter = pagerAdapter
 
        viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
            override fun onPageSelected(position: Int) {
                super.onPageSelected(position)
                Log.e("ViewPagerFragment""Page ${position+1}")
            }
        })
 
        // TabLayout attach        
        TabLayoutMediator(tabLayout, viewPager) { tab, position ->
            tab.text = "Tab ${position+1}"
        }.attach()
    }
 
}
 
 
cs

 

 

ViewPager2+TabLayout

 

 

TabLayout에 이미지를 입혀 응용하면

 

좀 더 그럴싸한 ViewPager2+TabLayout을 만들 수 있을 것 같다.

 

 

끝.

 

 

 

반응형