ViewPager2とTabLayoutを簡単に実装する

#はじめに
ViewPager2を実装する際、今までは複数のViewに対してその同数レイアウトファイルを作成していました。
今回は一つのレイアウトファイルで表示できたので紹介します。
ViewPager2実装するのが初めての方にも`TabLayoutMediator`と`FragmentManager`を理解する助けになればと思います。

全体を見たい方向けにサンプルのリポジトリ載せときます。
https://github.com/tomoya-s-101/viewpager2

#環境
OS: macOS Catalina v10.15.6
IDE： Android Studio v4.0.1

#導入
TabLayoutを使用できるようにするために、build.gradleにmaterial-componentsライブラリを追加します。

```build.gradle
implementation "androidx.viewpager2:viewpager2:1.1.0-alpha01"
implementation 'com.google.android.material:material:1.2.0'
```

#実装
今回は一つのフラグメントレイアウトで画像の色とテキストの切り替えをViewPager2を使い表示します。
![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665360/8efc25c8-ee9b-d654-677b-eab5ed14c2d6.gif)


##レイアウト
bindingの実装をしたいので、layoutタグで囲います。
tabIndicatorGravityでインジケーターの位置を指定できます。
[TabLayoutリファレンス](https://developer.android.com/reference/com/google/android/material/tabs/TabLayout)

```activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
    <androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tab_layout"
            android:layout_width="match_parent"
            android:layout_height="48dp"
            app:tabIndicatorGravity="stretch"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent">
        </com.google.android.material.tabs.TabLayout>

        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/viewpager2"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintBottom_toTopOf="@id/tab_layout"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
```
---
ViewPager2で表示するレイアウトを作ります。
stringとcolorのリソースも準備してください。

```fragment_color.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/fragment_color"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/tab_text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TabTextView"
            android:textColor="@color/white"
            android:textSize="48sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
```

##Fragment
表示させたいリストのインスタンスを生むことで、一つのレイアウトで切り替えが可能になります。

```ColorFragment.kt
class ColorFragment: Fragment() {
    companion object {
        private const val ARGS_POSITION = "position"
        fun newInstance (position: Int) = ColorFragment().apply {
            arguments = Bundle().apply {
                putInt(ARGS_POSITION, position)
            }
        }
    }

    private lateinit var binding: FragmentColorBinding

    private val position: Int by lazy {
        arguments?.let {
            it[ARGS_POSITION] as Int
        }?:0
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_color, container, false)
        return binding.root
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        binding.apply {
            when(position) {
                0 -> {
                    tabTextView.setText(R.string.text_red)
                    fragmentColor.setBackgroundResource(R.color.red)
                }
                1 -> {
                    tabTextView.setText(R.string.text_blue)
                    fragmentColor.setBackgroundResource(R.color.blue)
                }
                2 -> {
                    tabTextView.setText(R.string.text_green)
                    fragmentColor.setBackgroundResource(R.color.green)
                }
                else -> {
                    tabTextView.setText(R.string.text_yellow)
                    fragmentColor.setBackgroundResource(R.color.yellow)
                }
            }
        }
    }
}
```

##Adapter
FragmentActivityを使用するため、`FragmentManager`を呼び出します。
[FragmentManagerリファレンス](https://developer.android.com/reference/kotlin/androidx/fragment/app/FragmentManager?hl=ja)

```ViewPager2Adapter.kt
class ViewPager2Adapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
    FragmentStateAdapter(fragmentManager, lifecycle) {

    private val fragments: ArrayList<Fragment> = arrayListOf(
        ColorFragment.newInstance(0),
        ColorFragment.newInstance(1),
        ColorFragment.newInstance(2),
        ColorFragment.newInstance(3)
    )

    override fun getItemCount(): Int {
        return fragments.size
    }

    override fun createFragment(position: Int): Fragment {
        return fragments[position]
    }
}
```

##Activity
`TabLayoutMediator`でTabLaoutとViewPager2を引数で渡すことで、スクロールとインジケーターがリンクします。
[TabLayoutMediatorリファレンス](https://developer.android.com/reference/com/google/android/material/tabs/TabLayoutMediator)

```MainActivity.kt
class MainActivity : AppCompatActivity() {
    companion object {
        fun callingIntent(context: Context) = Intent(context, MainActivity::class.java)
    }

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView<ActivityMainBinding>(
            this, R.layout.activity_main
        ).apply {
            viewpager2.adapter = ViewPager2Adapter(supportFragmentManager, lifecycle)

            TabLayoutMediator(tabLayout, viewpager2) { tab, position ->
            }.attach()
        }
    }
}
```

#おわりに
他にもViewPager2では、縦方向のスクロールなどアクティブな実装が簡単にできるみたいです。
[ドキュメント](https://developer.android.com/training/animation/vp2-migration?hl=ja)

閲覧ありがとうございました。
2020年７月からエンジニアとして働き始めた見習いですので、拙い点ございます。
間違いやより適切な方法ありましたら、コメントお願いします。
今後も記事を投稿していくので、是非よろしくお願い致します。

#参考
https://developer.android.com/reference/com/google/android/material/tabs/TabLayout
https://developer.android.com/reference/kotlin/androidx/fragment/app/FragmentManager?hl=ja
https://developer.android.com/reference/com/google/android/material/tabs/TabLayoutMediator
https://developer.android.com/training/animation/vp2-migration?hl=ja