📆
【Android】ライブラリCalendarを試す
Calendar
Android用の高度にカスタマイズ可能なカレンダー・ライブラリで、ビュー・システムにはRecyclerView、合成にはLazyRow/LazyColumnが採用されている。
Androidアプリ内でカレンダーを扱う際に便利な Calendar
ライブラリを軽く試した際のメモになります。
動作確認環境
$ sw_vers
ProductName: macOS
ProductVersion: 13.4
BuildVersion: 22F66
Android Studio Giraffe | 2022.3.1 Patch 1
Calendarライブラリの機能
- 単一選択、複数選択、範囲選択 - お好みの方法で日付選択を行うことができる
- 週または月モード - 週ベースのカレンダー、または通常の月カレンダーを表示
- 一部の日付を無効にして選択できないようにする
- カレンダーの日付範囲を制限する
- Custom date view/composable
- Custom calendar view/composable
- 任意の日を週の初日として使用する
- 水平または垂直スクロールカレンダー
パッケージインストール
こちらに記載がある通り、ComposeUIのVersionに合わせてCalendarのVersionも指定する。
今回 compose bom
が 2022.10.00
を使っているのでこちら参照するとComposeUIのVersionは 1.3.0 なので今回は 2.2.0
を使います。
libs.versions.toml
に以下を追加
[versions]
calendar-view = "2.2.0"
[libraries]
calendar-view = { module = "com.kizitonwose.calendar:compose", version.ref = "calendar-view" }
app/build.gradle.kts
に以下を追加
dependencies {
implementation(libs.calendar.view)
}
ドキュメント
Calendar/docs/Compose.md at main · kizitonwose/Calendar
↑を参考にすすめていきます。
主な4つのComposable
- HorizontalCalendar
- 横スクロールの月単位のカレンダー
- VerticalCalendar
- 縦スクロールの月単位のカレンダー
- WeekCalendar
- 横スクロールの週単位のカレンダー
- HeatMapCalendar
- GitHub の草のあれ
HorizontalCalendar
で最低限の実装を試してみる
まずは @Composable
fun CalendarCompose() {
val currentMonth = remember { YearMonth.now() }
val startMonth = remember { currentMonth.minusMonths(100) } // Adjust as needed
val endMonth = remember { currentMonth.plusMonths(100) } // Adjust as needed
val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } // Available from the library
val state = rememberCalendarState(
startMonth = startMonth,
endMonth = endMonth,
firstVisibleMonth = currentMonth,
firstDayOfWeek = firstDayOfWeek
)
Column(modifier = Modifier.fillMaxSize()) {
HorizontalCalendar(
state = state,
dayContent = { Day(it) }
)
}
}
@Composable
private fun Day(day: CalendarDay) {
Box(
modifier = Modifier
.aspectRatio(1f), // This is important for square sizing!
contentAlignment = Alignment.Center
) {
Text(text = day.date.dayOfMonth.toString())
}
}
エミュレータで動作してみたものが↓になります。
VerticalCalendar, WeekCalendar, HeatMapCalendar の挙動
次に各カレンダーを使ってみたいと思います。
-
VerticalCalendar
HorizontalCalendarをVerticalCalendarに変更しただけのもの↓
-
WeekCalendar
@Composable fun WeekCalendarSimple() { val currentDate = remember { LocalDate.now() } val currentMonth = remember { YearMonth.now() } val startDate = remember { currentMonth.atStartOfMonth() } // Adjust as needed val endDate = remember { currentMonth.atEndOfMonth() } // Adjust as needed val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } // Available from the library val state = rememberWeekCalendarState( startDate = startDate, endDate = endDate, firstVisibleWeekDate = currentDate, firstDayOfWeek = firstDayOfWeek ) Column(modifier = Modifier.fillMaxSize()) { WeekCalendar( state = state, dayContent = { Day(it) } ) } } @Composable private fun Day(day: WeekDay) { Box( modifier = Modifier .aspectRatio(1f), // This is important for square sizing! contentAlignment = Alignment.Center ) { Text(text = day.date.dayOfMonth.toString()) } }
-
HeatMapCalendar
@Composable fun HeatMapCalendarSimple() { val currentMonth = remember { YearMonth.now() } val endDate = remember { LocalDate.now() } val startDate = remember { endDate.minusMonths(12) } val state = rememberHeatMapCalendarState( startMonth = startDate.yearMonth, endMonth = endDate.yearMonth, firstVisibleMonth = endDate.yearMonth, firstDayOfWeek = firstDayOfWeekFromLocale(), ) Column(modifier = Modifier.fillMaxSize()) { HeatMapCalendar( state = state, dayContent = { day, week -> Day(day, startDate, endDate, week) } ) } } @Composable private fun Day( day: CalendarDay, startDate: LocalDate, endDate: LocalDate, week: HeatMapWeek, ) { val weekDates = week.days.map { it.date } if (day.date in startDate..endDate) { LevelBox(Color.Green) } else if (weekDates.contains(startDate)) { LevelBox(Color.Transparent) } } @Composable private fun LevelBox(color: Color) { Box( modifier = Modifier .size(18.dp) // Must set a size on the day. .padding(2.dp) .clip(RoundedCornerShape(2.dp)) .background(color = color) ) }
WeekTitleをつけてみる
ドキュメントはこちら
@Composable
fun HorizontalCalendarWeekTitle() {
val currentMonth = remember { YearMonth.now() }
val startMonth = remember { currentMonth.minusMonths(100) } // Adjust as needed
val endMonth = remember { currentMonth.plusMonths(100) } // Adjust as needed
val daysOfWeek = remember { daysOfWeek() }
val state = rememberCalendarState(
startMonth = startMonth,
endMonth = endMonth,
firstVisibleMonth = currentMonth,
firstDayOfWeek = daysOfWeek.first()
)
Column(modifier = Modifier.fillMaxSize()) {
DaysOfWeekTitle(daysOfWeek = daysOfWeek)
HorizontalCalendar(
state = state,
dayContent = { Day(it) }
)
}
}
@Composable
fun DaysOfWeekTitle(daysOfWeek: List<DayOfWeek>) {
Row(modifier = Modifier.fillMaxWidth()) {
for (dayOfWeek in daysOfWeek) {
Text(
modifier = Modifier.weight(1f),
textAlign = TextAlign.Center,
text = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.getDefault()),
)
}
}
}
@Composable
private fun Day(day: CalendarDay) {
Box(
modifier = Modifier
.aspectRatio(1f), // This is important for square sizing!
contentAlignment = Alignment.Center
) {
Text(text = day.date.dayOfMonth.toString())
}
}
エミュレータで動作してみたものが↓になります。
感想
軽く試しただけですが、シンプルなベースのカレンダーを元に、
ドキュメント通り結構カスタマイズ性が高いライブラリだと感じました。
また深掘りした際は別途記事を書こうと思います。
Discussion