カレンダーをいい感じに実装する方法

#はじめに
Androidの標準カレンダー、非常に使いづらい上に、拡張性なくて辛いと感じたことはありませんか？
最近になっていい感じに実装可能なカレンダーのライブラリを発見したので、紹介したいと思います。

#CalendarView
今回ご紹介させていただくのは、以下ライブラリです。
https://github.com/kizitonwose/CalendarView

このライブラリは3年ほど前に作成されたライブラリのようですが、拡張性が高く、内部もKotlinで実装されており、Kotlinとの親和性が高い点信用ができます。

上記githubにサンプルがあるので詳細に関しましてはそちらをみていただければと思いますが、簡単に実装方法をまとめたいと思います。

#1. ライブラリの追加

まずはじめにappレベルのbuild.gradleに以下を追加する必要があります。

```
dependencies {
	implementation 'com.github.kizitonwose:CalendarView:<latest-version>'
}
```

#2. レイアウト
ライブラリを追加したら、任意のレイアウトに以下を追加します。

```
<com.kizitonwose.calendarview.CalendarView
            android:id="@+id/calendar"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:cv_dayViewResource="@layout/calendar_day"
            app:cv_monthHeaderResource="@layout/calendar_header"
            app:cv_orientation="horizontal"
            app:cv_scrollMode="paged"　/>
```

基本的には上記設定が基本的な設定になるかと思います。
上記パラメータを簡単に説明します。

```
cv_dayViewResource
```
これはカレンダーの日にちに設定したいレイアウトを指定します。
基本的にはテキストのみで良いと思いますが、選択したり等日にちに対して細かいUIを指定したい場合はここに指定しているレイアウトに設定すると良いと思います。

```
app:cv_monthHeaderResource
```
この設定はカレンダーのヘッダーに設定したいレイアウトを指定します。
曜日等をこちらに指定するケースが多いと思います。
ここで設定したヘッダーは月単位で設定されます。

```
app:cv_orientation
```
この設定はどの方向にカレンダーを動作させるかの設定です。
horizontal、verticalのどちらかを設定します。

```
app:cv_scrollMode
```
この設定は月を替えた際の動きに関する設定です。
pager、continuousと二種類の設定があり、ページめくりかスクロールかの二択になります。

#3. DayContainer
レイアウトを作成しましたら、日付のViewを格納する、ViewHolderの役割を担うクラスを作成します。

```CalendarDayContainer.kt
class CalendarDayContainer(view: View) :
    ViewContainer(view) {
    val binding = ItemCalendarDayBinding.bind(view)
}
```

ここはViewHolderとほぼ同様の実装になります、セルに当てたいレイアウトを保持する形になります。

#4.DayBinder

DayContainerを作成しましたら、次はContainerを使用するDayBinderの作成を行います。
DayBinderはRecyclerViewのAdapterのような役割になります。

```CalendarDayBinder.kt
class CalendarDayBinder() :
    DayBinder<CalendarDayContainer> {

    override fun create(view: View): CalendarDayContainer =
        CalendarDayContainer(
            view = view
        )

    override fun bind(container: CalendarDayContainer, day: CalendarDay) {
        val startDate = uiData?.startDate
        val endDate = uiData?.endDate

        container.binding.day.apply {
            text = day.date.dayOfMonth.toString()
        }
    }
}

```

基本的にcreateにてセル単位のContainerを作成し、bindでContainerを使用してセルのUIを設定していきます。
ここでは基本的な日付の表示しか調整していませんが、当月のみ日付を見えるようにしたり、日付を選択した際の動きなどに関してもここで設定するのが良いかと思います。

#5.HeaderBinder
セルの設定を行いましたら、次はヘッダーの設定を行います。
こちらはxmlにてヘッダーの設定を行った場合のみ調整を行えば問題ありません。

基本的には日付と同様ですが、継承すべきクラスが異なります。

```CalendarHeaderBinder.kt
class CalendarHeaderBinder :
    MonthHeaderFooterBinder<CalendarHeaderContainer> {
    override fun create(view: View) = CalendarHeaderContainer(view)
    override fun bind(
        container: CalendarHeaderContainer,
        month: CalendarMonth
    ) = Unit
}
```
ヘッダーを実装する際は、MonthHeaderFooterBinderを継承したBinderクラスを作成します。
ここでは曜日表示のみを行う想定なので、bindの中身は特に何もしていないですが、ヘッダーに動きをつけたい場合には、bindを適切に実装する必要があります。
CalendarHeaderContainarに関しましては、CalendarDayContainerと同様の作成方法にて問題ありませんので、説明は省きます。

#6.Fragment
ここまで日付やヘッダーに関する設定を行いましたが、次はCalendarに関して設定を行います。

```CalendarFragment.kt
    binding.calendar.apply {
        dayBinder = CalendarDayBinder()
        monthHeaderBinder = CalendarHeaderBinder()

        val currentMonth = YearMonth.now()
        val firstMonth = YearMonth.of(1990, 1)
        val firstDayOfWeek = WeekFields.of(Locale.getDefault()).firstDayOfWeek
        setup(firstMonth, currentMonth, firstDayOfWeek)
        scrollToMonth(currentMonth)
}
```
まず、calendarに対して、先ほど作成したBinderを設定します。
次に、カレンダーの月情報を設定していきます。
setup関数が上記に該当しますが、カレンダーの開始月、現在の月、曜日の始まりを設定します。
最後に最初に見えているべき月をscrollToMonthに設定することで、設定完了です。

#さいごに
基本的なカレンダーの実装であれば、とても簡単に実装可能なのがわかるかと思います。
対象ライブラリのサンプルには６種類ほどのカレンダーの実装がありますが、とても柔軟に実装できることがよくわかるかと思いますので、気になる方は目を通してみると面白いと思います。
Androidにはまだまだ使いづらいUIがたくさんありますが、こういった形でサードパーティのライブラリを入れることで、簡単に実現できるのは素晴らしいと思います。
他にも便利なライブラリがたくさんありますので、時間ができたら紹介していこうと思います。