【Android】ViewPager2を簡単に作ってみる

## はじめに
よくチュートリアル画面などの実装に使われるViewPager2。
早速実装してみようと思い色々調べていたのですが、プログラミング初心者ゆえに意外に混乱してしまったため簡単にまとめてみたいと思います。

* 最近Android開発を始めた方
* ViewPager2について調べているけど記事によって書き方が違うので混乱してきた方

などの参考になればとても嬉しいです。
もしも間違いなどあればご指摘いただけますと幸いです。

## 実装内容
### 完成イメージ
こんな感じの横にスワイプするだけの画面を作ります。

* ページは2ページ
* 無限スワイプはしない

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1274260/3e81826c-75dc-885f-afe1-353271a4c58e.gif" img width="300px">



### 作成ファイル
* ``` MainActivity.kt``` /  ```activity_main.xml``` 
    * スワイプするページを載せるための土台
* ```HogeAdapter.kt```
    * ViewPager2を作るための設定ファイルみたいなもの。詳しくは[2.Adapterの作成](#2-Adapterの作成)で説明します
* ```FirstChildFragment.kt``` / ```first_child_fragment.xml```
    * スワイプの1ページ目用のフラグメント。サンプルgifの青緑のページ。
* ```SecondChildFragment.kt``` / ```second_child_fragment.xml```
    * スワイプの2ページ目用のフラグメント。サンプルgifの紫のページ。

### 今回使用するAdapterについて（読み飛ばしOK）
* Adapterの親クラスはFragmentStateAdapterを継承して作ります。
    * ViewPager2のAdapterはfragmentStateAdapterを継承する方法とRecyclerView.adapterを継承する方法があるのですが、今回は前者を採用します。（こちらの方が作成するファイルと記述するメソッドが少ないため、とにかくViewPager2を触ってみたい初心者の方などはこちらから始めるのが良いかもしれません）
    * ちなみにこれらの使い分けですが、fragmentStateAdapterを継承する場合は子のページをフラグメントで作成します。後者の場合はrecycler viewのように枠のみ作成し、中身はただの画像や文字などを入れ替えるようなイメージで使うようです。よくある画像のみ入れ替わるようなバナーみたいなものは後者で作ったほうが効率が良さそうですね！いつかこちらも記事にしたいです！



#作成の流れ
[1.子のフラグメント（サンプルで言うところの紫と青緑のページ）を作成](#1-子のフラグメントを作成)
[2.Adapterの作成](#2-Adapterの作成)
[3.MainActivityFragmentに1と2をはめ込む](#3-MainActivityFragmentに1と2をはめ込む)

#1. 子のフラグメントを作成
サンプルで言うところの青と紫のページを作っていきます。
ここは主役じゃないのでサクサク簡単に行きます。お好みに合わせて自由なレイアウトにしてみてください。

1ページ目

```FirstChildFragment.kt
// コンストラクタにレイアウトIDを入れるとonCreateを省略できるので、表示するだけならこれだけでOKです
class FirstChildFragment: Fragment(R.layout.first_child_fragment) {}
```

```first_child_fragmen.xml
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/teal_200">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="FirstChildFragment" />
</androidx.appcompat.widget.LinearLayoutCompat>
```

2ページ目（やってることは1ページ目と同じ）

```SecondChildFragment.kt
class SecondChildFragment(): Fragment(R.layout.second_child_fragment) {}
```


```second_child_fragment.xml
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/purple_200">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="SecontChildFragment" />

</androidx.appcompat.widget.LinearLayoutCompat>
```

#2. Adapterの作成
いよいよViewPager2の主役とも言えるAdapterを実装していきます。
そもそもAdapterとはなんぞや？と言う方も多いかもしれませんが、表示するページを作ったり枚数をカウントするメソッドを実装するクラスのことです。
ViewPager2で使うAdapterには

- FragmentStateAdapter
- RecyclerView.adapter
の2種類があります。（他にもあったら教えてください）

FragmentStateAdapterを継承する場合は
```createFragment```
```getItemCount```
の2つのメソッドを実装する必要があります。

RecyclerView.adapterを継承する場合は
```onBindViewHolder```
 ```onCreateViewHolder```
 ```getItemCount```
の3つのメソッドを実装する上、ViewHolderというページを表示するための枠組みみたいなものを作成する必要があります。(今回はこちらは使わないので詳しい説明は省きます）

ではFragmentStateAdapterを使って早速Adapterを実装していきましょう。

```HogeAdapter.kt
    // 引数にはこの後紹介するActivityが入る
class HogeAdapter(mainActivity: FragmentActivity) : FragmentStateAdapter(mainActivity) {

    // 先ほど作成したFragmentをリストにして変数に格納
    val childrenFragments = listOf(FirstChildFragment(), SecondChildFragment())

    // n=今いる立ち位置のこと（公式とか他のサイトだと「position」が使われがち）
    override fun createFragment(n: Int): Fragment {
        // n番目のchildrenFragmentsをreturnする
        return childrenFragments[n]
    }

    override fun getItemCount(): Int {
        // childrenFragmentの個数を数える
        return childrenFragments.size
    }
}
```

この章の冒頭で説明した通り、```createFragment```と```getItemCount```の2つをoverrideして実装していますね。

まず```createFragment```についてですが、その名の通りFragmentをCreateするメソッドです。そのためここでFragmentがインスタンス化されます。
ちなみに自分はcreateFragmentの引数あたりの理解に苦しんだので、今回はシンプルにnという引数を設定して、childrenFragments変数の中のn番目のFragmentが作成されるというような形にしてみました。（ちょっとまだ理解が甘いので自信ないです）

次に```getItemCount```ですが、こちらはページの枚数を数えるクラスです。今回は2ページしかないので、childrenFragmentsの要素の数を設定してあります。（直接「2」とか書いてもいいのですが、それよりは実際に表示するページ数が自動で入るようにした方がバグが減るのでこの書き方にしてあります。）

ちょっとまだよくわからないという方は

```HogeAdapter.kt
    override fun createFragment(n: Int): Fragment {
        return FirstChildFragments()
    }

    override fun getItemCount(): Int {
        return 10
    }
}
```

とかをイメージするとわかりやすいかもしれません。
この場合は```createFragments```で```FirstChildFragment()```のみをインスタンス化（＝Create）し、```getItemCount```の返り値を10にしているため、実際の挙動としてはFirstFragmentのページが10回スワイプできるような形になります。




#3. MainActivityFragmentに1と2をはめ込む
最後に、ここまで作成してきたページやアダプターを、大元の土台であるActivityに組み込みます。


```activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.viewpager2.widget.ViewPager2
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
```


 ↑```<androidx.viewpager2.widget.ViewPager2``` でViewPager2を組み込めます。
今回は全面に表示させたいので全てmatch_parentにしています。


```MainActivity.kt
class MainActivity : AppCompatActivity(R.layout.activity_main) {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)


       // xmlファイルで定義したViewPager2を変数に格納
        val viewPager2 = findViewById<ViewPager2>(R.id.hoge_view_pager2)
        
        // ViewPager2のアダプターには先ほど作成したHogeAdapterを使うようにセット
        viewPager2.adapter = HogeAdapter(this)
    }
}
```
↑```MainActivity.kt```ファイルでは

* xmlファイルのインフレート（上記の```AppCompatActivity(R.layout.activity_main)```の部分）
    * Fragmentと同じくコンストラクタにレイアウトIDを入れることで表示処理を省略できます
* xmlファイル内のViewPager2の取得
* アダプターのセット

を行っています。

ちなみにアダプターの引数にはFragmentActivity型の引数が要求されるので、アダプターをセットするときにthisとしてこのAdapterを指定しています。

## 最後に
こうしてまとめると意外に単純に見えますが、実際に活用していくとなるともっともっと設定できる項目もあるのでぜひ試してみてください！
もしも質問・指摘事項などありましたらお気軽にご連絡いただけますと幸いです。