A composable Calendar.
![default](https://private-user-images.githubusercontent.com/7933189/336353506-3c78bbbd-7283-4066-8206-6c494a87f716.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjYwODg3ODQsIm5iZiI6MTcyNjA4ODQ4NCwicGF0aCI6Ii83OTMzMTg5LzMzNjM1MzUwNi0zYzc4YmJiZC03MjgzLTQwNjYtODIwNi02YzQ5NGE4N2Y3MTYucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDkxMSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA5MTFUMjEwMTI0WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9MGQwZWEwZWJlY2JhYjJmOTk2ZWFiNTYzYjA4M2ZiZGU3ODQwNGE0MmY4YWEzZjhjYjQ4NDhjMzczOTliYzdjNSZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.Dk8w6_monRt8MYNyu276q0D3aF8dG-bTxWR83cBWph4)
![default](https://private-user-images.githubusercontent.com/7933189/336353050-f96b275b-2ba1-40b3-895e-0026c5267460.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjYwODg3ODQsIm5iZiI6MTcyNjA4ODQ4NCwicGF0aCI6Ii83OTMzMTg5LzMzNjM1MzA1MC1mOTZiMjc1Yi0yYmExLTQwYjMtODk1ZS0wMDI2YzUyNjc0NjAucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDkxMSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA5MTFUMjEwMTI0WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9ZTQ5Zjk5MTM0ZjAxMjcwMmU4MjY5ZDYwMDM5ZDY5OTM4NjliNzM3YzMzMzQyMmE3NGM2ODdmMmY1YzczNzBhYSZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.NwxU2UCI81INzpUwy_POhG6Q-zETV2MSMm9bhK6mnqE)
![default](https://private-user-images.githubusercontent.com/7933189/335029683-c3f2d0ab-ed8a-429c-82ba-d672e9d65324.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjYwODg3ODQsIm5iZiI6MTcyNjA4ODQ4NCwicGF0aCI6Ii83OTMzMTg5LzMzNTAyOTY4My1jM2YyZDBhYi1lZDhhLTQyOWMtODJiYS1kNjcyZTlkNjUzMjQucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDkxMSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA5MTFUMjEwMTI0WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NzI5MzhiYzFmN2E1NWM0MzdmMmU0ZGNlYmMzZjAyYmJiZThlNmJkMjhkODk0NDlmOWJkMDE5MGQwOTBkZTNlNCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.vWd9eJVy_Ba8Xc1CDBCy7Z30E5BwLtVzs6x9p8V03sA)
-
Add it in your root build.gradle at the end of repositories:
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { mavenCentral() maven { url 'https://jitpack.io' } } }
-
Add the dependency
dependencies { implementation 'com.github.dyguests:LinCalendar:0.4.0' }
val state = rememberLinCalendarState()
LinCalendar(
state = state,
)
fun rememberLinCalendarState(
initialDate: LocalDate = LocalDate.now(),
startDate: LocalDate = LocalDate.of(1900, 1, 1),
endDate: LocalDate = LocalDate.of(2099, 12, 31),
initialDisplayMode: LinCalendar.DisplayMode = LinCalendar.DisplayMode.MONTHLY,
option: LinCalendar.Option = LinCalendarDefaults.option(),
): LinCalendarState
enum class DisplayMode {
MONTHLY,
WEEKLY, // todo impl
}
data class Option(
val headerHeight: Dp = 32.dp,
val rowHeight: Dp = 36.dp,
val firstDayOfWeek: DayOfWeek = DayOfWeek.MONDAY,
val weekDisplayMode: WeekDisplayMode = WeekDisplayMode.FIXED_HEIGHT,
val locale: Locale = Locale.getDefault(),
)
Consider of WeekDisplayMode
, the height of Calendar in different months may be different.
Therefore, you cannot set the height of Calendar directly, but use headerHeight
and rowHeight
.
enum class WeekDisplayMode {
// Fixed height, leaving the fifth week empty
FIXED_HEIGHT,
// Fixed height, equally dividing height by weeks
EQUAL_HEIGHT, // todo impl
// Dynamic height, adapting to the number of weeks in the month
DYNAMIC_HEIGHT // todo impl
}
Each part of LinCalendar is composable, so you can customize each part yourself.
LinCalendar
monthsField
monthFiled
weekHeaderField
dayHeaderField
weekField
dayField
Add a highlighted background to today's day of the calendar.
Step 1
Add LinCalendar
val state = rememberLinCalendarState()
LinCalendar(
state = state,
)
Step 2
If you look at the source code, you can see that the above code is equivalent to:
LinCalendar(
state = state,
monthsField = LinCalendarDefaults.monthsField(
state = state,
monthFiled = LinCalendarDefaults.monthField(
state = state,
),
),
)
...
equivalent to:
LinCalendar(
state = state,
monthsField = LinCalendarDefaults.monthsField(
state = state,
monthFiled = LinCalendarDefaults.monthField(
state = state,
weekField = LinCalendarDefaults.weekField(
state = state,
dayField = LinCalendarDefaults.dayField(
state = state,
)
),
),
),
)
To highlighted background to today's day, You need to custom dayField
, change code to this:
val dayField = LinCalendarDefaults.dayField(
state = state,
)
LinCalendar(
// ...
dayField = dayField
// ...
)
Now we focus on dayField
Step 3
Inline the function LinCalendarDefaults.dayField
.(You can also copy the source code of LinCalendarDefaults.dayField
)
val dayField: @Composable RowScope.(YearMonth, LocalDate) -> Unit = { yearMonth: YearMonth, localDate: LocalDate ->
val now = remember { LocalDate.now() }
Box(
modifier = Modifier
.height(state.option.rowHeight)
.weight(1f)
.then(Modifier),
contentAlignment = Alignment.Center,
) {
Text(
text = localDate.dayOfMonth.toString(),
style = TextStyle(
color = if (localDate == now) MaterialTheme.colorScheme.primary
else MaterialTheme.colorScheme.onSurface,
fontWeight = if (localDate == now) FontWeight.Bold
else if (yearMonth.month == localDate.month) FontWeight.Normal
else FontWeight.Light,
),
)
}
}
Now you can add a highlighted background to today.
val dayField: @Composable RowScope.(YearMonth, LocalDate) -> Unit = { yearMonth: YearMonth, localDate: LocalDate ->
// ...
Box(
// ...
) {
if (localDate == now) {
Box(
modifier = Modifier
.size(32.dp)
.background(Color.Red, CircleShape)
)
}
Text(
// ...
style = TextStyle(
color = if (localDate == now) Color.White
// ...
),
)
}
}
![default](https://private-user-images.githubusercontent.com/7933189/336364971-0769bdb9-a2d1-426d-9d55-47d039c45e2a.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjYwODg3ODQsIm5iZiI6MTcyNjA4ODQ4NCwicGF0aCI6Ii83OTMzMTg5LzMzNjM2NDk3MS0wNzY5YmRiOS1hMmQxLTQyNmQtOWQ1NS00N2QwMzljNDVlMmEucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDkxMSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA5MTFUMjEwMTI0WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NjdlNTVhYzFhMmY5NzRlODRlMzBjMTc1ODI0OGQ0NTJkNDA0MzgzMzgwY2FhNzRjMWJlNzY0MDY4NDk1ODIyOSZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.AW4uXjkS3M_QtY5b1_BIVzfIUQv42W8PsikXCo8PpjQ)
That's it.
- 隐藏无关细节。比如monthFiled中的计算逻辑