0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Jetpack Compose】カレンダー 水平にスクロール&表示されている月を取得

Last updated at Posted at 2024-10-27

カレンダーをスクロール

何をやったか(結論)

家計簿アプリを作っていて、カレンダーをUIに入れたかった。カレンダーを簡単に作れるライブラリがあったので使おうとした。しかし、横にスクロールして前後の月のカレンダーを表示することもデフォルトで可能だったが、現在表示されている月を取得する方法がわからなかった。そこで、HorizontalPagerを使って解決した。

環境

  • OS:Windows 11 Home
  • Android Studio Koala Feature Drop | 2024.1.2 Patch 1
    Build #AI-241.19072.14.2412.12360217, built on September 13, 2024
    Runtime version: 17.0.11+0--11852314 amd64
    VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.

手順

ライブラリのimport

koalaの場合、kizitonwoseのREADME.mdに表示されているjitpack.ioの書き方だとうまくいかなかった。 次のように書かないといけない。

settings.gradle.kts
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()

        //ここに自分で追加する
        maven {
            url = uri("https://jitpack.io")
        }
    }
}

参考 : Adding maven { url "https://jitpack.io" }

build.gradle.ktsのdependencies内には次を追加する

build.gradle.kts
dependencies {
    val calendar_version="2.6.0"
    // The compose calendar library for Android
    implementation("com.kizitonwose.calendar:compose:${calendar_version}")
    ..
    ..
}

カレンダーの表示

元々のコード
@Composable
fun CalendarDisplay() {
    // 現在の年月
    val currentMonth = remember { YearMonth.now() }
    // 現在より前の年月
    val startMonth = remember { currentMonth.minusMonths(100) }
    // 現在より後の年月
    val endMonth = remember { currentMonth.plusMonths(100) }
    // 曜日
    val daysOfWeek = remember { daysOfWeek() }
    // カレンダーの状態を持つ
    val state = rememberCalendarState(
        startMonth = startMonth,
        endMonth = endMonth,
        firstVisibleMonth = currentMonth,
        firstDayOfWeek = daysOfWeek.first(),
        outDateStyle = OutDateStyle.EndOfGrid
    )

    // 横スクロールのカレンダーを作成するためのComposable関数
    // 縦スクロールのVerticalなどもある
    HorizontalCalendar(
        state = state,
        // 日付を表示する部分
        dayContent = {Day(it)},
        // カレンダーのヘッダー
        monthHeader = {DaysOfWeekTitle(daysOfWeek = daysOfWeek)}
    )
}

@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
fun Day(day: CalendarDay) {
    Box(
        modifier = Modifier
            .aspectRatio(1f),
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = day.date.dayOfMonth.toString(),
            // ここで今月でないものの日付をグレーアウトさせている
            color = if (day.position == DayPosition.MonthDate) Color.Black else Color.Gray
        )
    }
}

カレンダーの修正

yearとmonthを渡すことで特定の月のカレンダーを作るように変更した。また横にスクロールするのはPagerを使って行うので、startMonthとendMonthを一致させた。

@Composable
fun CalendarDisplay(calendarYear:Int, calendarMonth:Int ) {
    // 現在の年月
    val calendarYearMonth = YearMonth.of(calendarYear,calendarMonth)
    // 現在より前の年月
    val startMonth =  calendarYearMonth.minusMonths(0)
    // 現在より後の年月
    val endMonth = calendarYearMonth.plusMonths(0)
    // 曜日
    val daysOfWeek =  daysOfWeek()
    // カレンダーの状態を持つ
    val state = rememberCalendarState(
        startMonth = startMonth,
        endMonth = endMonth,
        firstVisibleMonth = calendarYearMonth,
        firstDayOfWeek = daysOfWeek.first(),
        outDateStyle = OutDateStyle.EndOfGrid
    )

    // 横スクロールのカレンダーを作成するためのComposable関数
    // 縦スクロールのVerticalなどもある
    HorizontalCalendar(
        state = state,
        // 日付を表示する部分
        dayContent = {Day(it)},
        // カレンダーのヘッダー
        monthHeader = {DaysOfWeekTitle(daysOfWeek = daysOfWeek)},
        //ユーザーのスクロール
        userScrollEnabled = false
    )
}

HorizontalPagerの基本的な使い方

参考 : Composeでのページャー

サンプルコード
// Display 10 items
val pagerState = rememberPagerState(pageCount = {
    10
})
VerticalPager(state = pagerState) { page ->
    // Our page content
    Text(
        text = "Page: $page",
        modifier = Modifier.fillMaxWidth()
    )
}

- 特にページの状態が変更したとき(通知を受け取る)
サンプルコード
val pagerState = rememberPagerState(pageCount = {
    10
})

LaunchedEffect(pagerState) {
    // Collect from the a snapshotFlow reading the currentPage
    snapshotFlow { pagerState.currentPage }.collect { page ->
        // Do something with each page change, for example:
        // viewModel.sendPageSelectedEvent(page)
        Log.d("Page change", "Page changed to $page")
    }
}

VerticalPager(
    state = pagerState,
) { page ->
    Text(text = "Page: $page")
}

HorizontalPagerを使ってCalendarをスクロールする

viewModelの中身は以下のようになっていて、calendarDateを保存している。

ExpenseViewModel.kt
class ExpenseViewModel():ViewModel() {
    private val calendarDate = mutableStateOf(LocalDate.now())

    fun getCalendarYear(): Int {
        return calendarDate.value.year
    }

    fun getCalendarMonth():Int{
        return calendarDate.value.monthValue
    }

    fun incrementMonth(){
        calendarDate.value = calendarDate.value.plusMonths(1)
    }

    fun decrementMonth(){
        calendarDate.value = calendarDate.value.minusMonths(1)
    }

    //MainViewもLazyColumnに表示する
    fun getMonthExpenses():List<Expense>{
        return DummyExpenses.expensesList.filter {
            it.datetime.year == calendarDate.value.year &&
                    it.datetime.month == calendarDate.value.month
        }
    }
}

スクロール部分の実装

MainView.kt
    //カレンダー横スクロールのため
    val calendarPagerState= rememberPagerState(initialPage = 1){ 3 }
    var previousCalendarPage by remember { mutableStateOf(calendarPagerState.currentPage) }
....
....

            Row(
                modifier = Modifier.fillMaxWidth(),
            ){
                HorizontalPager(
                    state = calendarPagerState,
                    modifier = Modifier.weight(1f)
                ) {
                    //viewModelからCalendarの年と月を渡す
                    CalendarDisplay(viewModel.getCalendarYear(),viewModel.getCalendarMonth())
                }
            }

            /**************************************************/
            /*カレンダーをスクロールしたときにviewModel内の日付を変更する*/
            /**************************************************/
            LaunchedEffect(calendarPagerState) {
                snapshotFlow { calendarPagerState.currentPage }
                    .distinctUntilChanged()
                    .collect{
                    currentPage->
                        if(currentPage>previousCalendarPage){
                            //右にスクロールしたとき
                            //incrementすることによって次の月に切り替える
                            viewModel.incrementMonth()
                        }else if (currentPage<previousCalendarPage) {
                            //左にスクロールしたとき
                            //decrementすることによって前の月に切り替える
                            viewModel.decrementMonth()
                        }
                        previousCalendarPage=currentPage
                }
            }
....
....

これでviewModel内のcalendarDateと表示されているカレンダーの年月が一致するようになった。
質問あればお願いしますm(__)m

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?