〈kotlin〉スワイプビューを作る① TabLayoutとViewPager

Android studioでkotlinを使ってAndroidアプリ作成。

画面上部にタブを3つ用意して、
タブを押すかスワイプすることで画面遷移ができるように実装したい。

TabLayoutとViewPager2を組み合わせることで作成できる。
参考https://developer.android.com/guide/navigation/navigation-swipe-view-2?hl=ja

TabLayoutとViewPager2を配置する

xmlファイルにLinearLayoutを配置し、
その中にTabLayoutとViewPager2をこの順番で配置する。
(失敗メモ:LinearLayoutに入れ忘れてたらタブとページが被って表示してしまったし、
順番逆にしたらページの表示が上手くできなかった。)

Paletteの中のContainersからTabLayoutを配置したが、コード記述した方が早い。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

</LinearLayout>

タブに表示する画面の作成

今回はタブ3つ、それぞれにフラグメントを作成した。

class tabFragment() : Fragment() {
     override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_tab, container, false)
    }
}

フラグメントにはそれぞれ違う内容のtextViewだけとりあえず配置してみた。

<FrameLayout 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"
    tools:context=".allFragment">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/all_text" />

</FrameLayout>

Adapterの作成

用意したfragmentを取得できるようにAdapterを作成する。
ViewPager2を使用する場合、AdapterにはFragmentStateAdapterを使用する。
FragmentStateAdapterを継承する際には
FragmentActivity オブジェクトをコンストラクタパラメータにする。

createFragment(position:Int):指定された位置のFragmentインスタンスを返す(positionはタブの左から右)
getItemCount():アダプター内のアイテムの総数を返す

class PageAdapter(fa: FragmentActivity): FragmentStateAdapter(fa){

    private val tabFragment_a = tabFragmentA()
    private val tabFragment_b = tabFragmentB()
    private val tabFragment_c = tabFragmentC()

    override fun createFragment(position: Int): Fragment {
        return when (position){
            0 -> {
                tabFragment_a 
            }
            1 -> {
                tabFragment_b
            }
            else ->  {
                tabFragment_c
            }
        }
    }

    override fun getItemCount(): Int {
        return 3
    }
}

Activityで呼び出す

配置したViewPager2にAdapterを設定し、
TabLayoutMediatorでTabLayoutとViewPager2をリンクさせる。
このとき同時にTabLayoutオブジェクトのページタイトルを生成するタスクも実行される。

class MainActivity : AppCompatActivity() {
    private lateinit var viewPager: ViewPager2
    
    override fun onCreate(savedInstanceState: Bundle?) {
        //
        viewPager = findViewById(R.id.pager)

        val pagerAdapter = PageAdapter(this)
        viewPager.adapter = pagerAdapter

        val tabLayout = view.findViewById(R.id.tab_layout)
        TabLayoutMediator(tabLayout, viewPager) { tab, position ->
            tab.text = "TAB ${(position + 1)}"
        }.attach()
    }
}