From 4ad4d41551dd27ea643aefebf3a94566ef2dbdaa Mon Sep 17 00:00:00 2001 From: Blackwen Date: Fri, 29 Dec 2023 11:26:32 +0800 Subject: [PATCH 1/6] Add Chinese button link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b0bdfbfa..a9b66730 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ LazyRow/LazyColumn for compose. [![Maven Central Beta](https://img.shields.io/badge/dynamic/xml.svg?label=Maven%20Central%20Beta&color=slateblue&url=https://repo1.maven.org/maven2/com/kizitonwose/calendar/core/maven-metadata.xml&query=(//metadata/versioning/versions/version)[contains(text(),%27beta%27)][last()])](https://repo1.maven.org/maven2/com/kizitonwose/calendar/) [![License](https://img.shields.io/badge/License-MIT-0097A7.svg)](https://github.com/kizitonwose/Calendar/blob/main/LICENSE.md) [![Twitter](https://img.shields.io/badge/Twitter-@kizitonwose-9C27B0.svg)](https://twitter.com/kizitonwose) - +[![cn](https://img.shields.io/badge/Lang-Chinese-blue?color=%23FF0000)](docs_chs/README.chs.md) **With this library, your calendar will look however you want it to.** ![Preview](https://user-images.githubusercontent.com/15170090/197389318-b3925b65-aed9-4e1f-a778-ba73007cbdf7.png) From f32fe997197b450322dae63bb01e71795e65132d Mon Sep 17 00:00:00 2001 From: Blackwen Date: Fri, 29 Dec 2023 11:26:46 +0800 Subject: [PATCH 2/6] Add Chinese README --- docs_chs/README.chs.md | 132 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 docs_chs/README.chs.md diff --git a/docs_chs/README.chs.md b/docs_chs/README.chs.md new file mode 100644 index 00000000..a0bbf202 --- /dev/null +++ b/docs_chs/README.chs.md @@ -0,0 +1,132 @@ +# Calendar + +中文翻译:[VinKon](https://github.com/Blackwen) + +适用于 Android 的高度可定制的日历库,由用于视图系统的 RecyclerView 和用于撰写的 LazyRow/LazyColumn 提供支持。 + +[![Tests](https://github.com/kizitonwose/Calendar/workflows/Check/badge.svg?branch=main)](https://github.com/kizitonwose/Calendar/actions) +[![Maven Central](https://img.shields.io/badge/dynamic/xml.svg?label=Maven%20Central&color=blue&url=https://repo1.maven.org/maven2/com/kizitonwose/calendar/core/maven-metadata.xml&query=(//metadata/versioning/versions/version)[not(contains(text(),%27-%27))][last()])](https://repo1.maven.org/maven2/com/kizitonwose/calendar/) +[![Maven Central Beta](https://img.shields.io/badge/dynamic/xml.svg?label=Maven%20Central%20Beta&color=slateblue&url=https://repo1.maven.org/maven2/com/kizitonwose/calendar/core/maven-metadata.xml&query=(//metadata/versioning/versions/version)[contains(text(),%27beta%27)][last()])](https://repo1.maven.org/maven2/com/kizitonwose/calendar/) +[![License](https://img.shields.io/badge/License-MIT-0097A7.svg)](https://github.com/kizitonwose/Calendar/blob/main/LICENSE.md) +[![Twitter](https://img.shields.io/badge/Twitter-@kizitonwose-9C27B0.svg)](https://twitter.com/kizitonwose) +[![cn](https://img.shields.io/badge/Lang-Chinese-blue?color=%23FF0000)](docs_chs/README.chs.md) + + + +**通过使用这个库,你可以使你的日历呈现出你想要的任何样式。** + +![Preview](https://user-images.githubusercontent.com/15170090/197389318-b3925b65-aed9-4e1f-a778-ba73007cbdf7.png) + +## 特征 + +- [x] 单选、多选或范围选择 - 完全灵活,可以以你喜欢的方式实现日期的选择。 +- [x] 周模式或月模式 - 显示以周为基础的日历,或者传统的月份日历。 +- [x] 禁用特定日期 - 通过禁用,防止选择某些日期。 +- [x] 边界日期 - 限制日历的日期范围。 +- [x] 自定义日期视图/可组合 - 使你的日期单元格呈现出你希望的外观。 +- [x] 自定义日历视图/可组合 - 使你的日历呈现出你想要的样子,具备你想要的任何功能。 +- [x] 自定义一周的第一天 - 使用任何一天作为一周的第一天。 +- [x] 横向或纵向滚动的日历。 +- [x] 热力图日历 - 适用于展示随时间变化的数据,比如 GitHub 的贡献图表。 +- [x] 月/周标题和页脚 - 在每个月/周上添加任何类型的标题/页脚。 +- [x] 通过滑动操作或以编程方式轻松滚动到日历上的任意日期/周/月。 +- [x] 充分利用所有 RecyclerView/LazyRow/LazyColumn 的自定义选项,因为该日历是基于 RecyclerView 用于视图系统,并使用 LazyRow/LazyColumn 用于 Compose。 +- [x] 按照[你的意愿](https://github.com/kizitonwose/Calendar/issues/1)设计你的日历。该库提供逻辑,由你提供视图/组件。 + +## 示例项目 + +查看示例应用程序非常重要。其中提供了许多视图和 Compose 实现的示例。大多数你想要实现的技术在这些示例中已经完成。 + +下载示例应用程序 [这里](https://github.com/kizitonwose/Calendar/releases/download/2.0.0/sample.apk) + +查看示例应用程序的源代码 [这里](https://github.com/kizitonwose/Calendar/tree/main/sample) + +## 设置 + +该库通过 [Java 8+ API desugaring](https://developer.android.com/studio/write/java8-support#library-desugaring) 使用 `java.time` 类,以确保向后兼容性,因为这些类是在 Java 8 中添加的。 + +#### 步骤 1 + +如果你的应用的 `minSdkVersion` 小于 26,则需要执行这一步。如果不适用,请直接跳转到 [步骤 2](#step-2)。 + +要设置项目以进行脱糖,您需要首先确保您使用的是 Android Gradle 插件 4.0.0 或更高版本。 + +然后将以下内容写入到应用程序的 build.gradle 文件中: + +```groovy +android { + defaultConfig { + // 仅在你的 minSdkVersion 低于 21 时需要 + multiDexEnabled true + } + + compileOptions { + // 启用对新语言 API 的支持 + coreLibraryDesugaringEnabled true + // 设置Java兼容性(如果需要的话版本可以更高) + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + // 同样,对于Kotlin项目,也添加这个(有需要的话版本可以更高) + jvmTarget = "1.8" + } +} + +dependencies { + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:' +} +``` + +你可以在[这里](https://mvnrepository.com/artifact/com.android.tools/desugar_jdk_libs)找到 `desugar_jdk_libs` 的最新版本。 + +#### 步骤 2 + +将所需的日历库(视图或Compose)添加到你的应用的 `build.gradle` 文件中: + +```groovy +dependencies { + // 视图日历库 + implementation 'com.kizitonwose.calendar:view:' + + // Compose日历库 + implementation 'com.kizitonwose.calendar:compose:' +} +``` + +你可以在上面的 Maven 中央徽章中找到该库的最新版本。 + +开发版本的快照可以在 [Sonatype 的快照仓库](https://s01.oss.sonatype.org/content/repositories/snapshots/com/kizitonwose/calendar/) 中找到。 + +如果你正在从版本1.x.x升级到2.x.x,请查阅[迁移指南](https://github.com/kizitonwose/calendar/blob/main/docs/MigrationGuide.md)。 + +对于 Compose 日历库,请确保你使用的库版本与项目中的 Compose UI 版本匹配。如果你使用的库版本比项目中的 Compose UI 版本更高,Gradle 将通过传递依赖升级项目中的 Compose UI 版本。 + +| Compose UI | Calendar Library | +|:----------:|:----------------:| +| 1.2.x | 2.0.x | +| 1.3.x | 2.1.x - 2.2.x | +| 1.4.x | 2.3.x | +| 1.5.x | 2.4.x | +| 1.6.x | 2.5.x | + +## 用法 + +你可以在下面的链接中找到该库的相关文档。 + +| [视图相关的文档](View.chs.md) | [Compose 文档](Compose.chs.md) | +| :---------------------------: | :----------------------------: | + +## 分享你的创作 + +使用这个库创建了一个酷炫的日历?在[这里](https://github.com/kizitonwose/Calendar/issues/1)分享一张图片。 + +## 贡献 + +发现 bug ?请随时修复并发送拉取请求,或者[提出一个问题](https://github.com/kizitonwose/Calendar/issues)。 + +## 许可证 + +Calendar 库采用 MIT 许可证分发。详细信息请参阅 [LICENSE](https://github.com/kizitonwose/Calendar/blob/main/LICENSE.md)。 + From 2ccf5febd2141f468655eb9736b266b3394a6889 Mon Sep 17 00:00:00 2001 From: Blackwen Date: Fri, 29 Dec 2023 11:28:06 +0800 Subject: [PATCH 3/6] Add Chinese documentation for Compose and View --- docs_chs/Compose.chs.md | 563 +++++++++++++++++++++++++++++++++++ docs_chs/View.chs.md | 639 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1202 insertions(+) create mode 100644 docs_chs/Compose.chs.md create mode 100644 docs_chs/View.chs.md diff --git a/docs_chs/Compose.chs.md b/docs_chs/Compose.chs.md new file mode 100644 index 00000000..38dadaf1 --- /dev/null +++ b/docs_chs/Compose.chs.md @@ -0,0 +1,563 @@ +# Calendar Compose Documentation + +## 目录 + +- [快速链接](#快速链接) +- [Compose 版本](#Compose 版本) +- [日历 Composables](#日历 Composables) +- [使用方法](#使用方法) + * [日历状态](#日历状态) + * [一周的第一天](#一周的第一天) + * [头部和尾部](#头部和尾部) + * [日历容器](#日历容器) + * [Composable 参数](#Composable 参数) + * [状态属性](#状态属性) + * [状态方法](#状态方法) + * [日期点击](#日期点击) + * [日期选择](#日期选择) + * [禁用日期](#禁用日期) +- [周历](#周历) +- [热力图日历](#热力图日历) + +## 快速链接 + +如果您还没有查看示例应用程序,请务必查看。大多数您想要实现的技术在示例中已经完成。 + +下载示例应用程序 [此处](https://github.com/kizitonwose/Calendar/releases/download/2.0.0/sample.apk) + +阅读示例应用程序的源代码 [此处](https://github.com/kizitonwose/Calendar/tree/main/sample) + +将库添加到您的项目 [此处](https://github.com/kizitonwose/Calendar#setup) + +**如果您正在寻找基于视图的文档,您可以在[此处](View.chs.md)找到。** + +## Compose 版本 + +确保您使用的库版本与您项目中的Compose UI版本相匹配。如果您使用的库版本具有比您项目中更高版本的Compose UI,Gradle 将通过传递依赖关系升级您项目中的Compose UI版本。 + +| Compose UI | Calendar Library | +|:----------:|:----------------:| +| 1.2.x | 2.0.x | +| 1.3.x | 2.1.x - 2.2.x | +| 1.4.x | 2.3.x | +| 1.5.x | 2.4.x | +| 1.6.x | 2.5.x | + +## 日历 Composables + +该库可以通过四个可组合项使用: + +`HorizontalCalendar()`: 水平滚动的基于月份的日历。 + +`VerticalCalendar()`: 垂直滚动的基于月份的日历。 + +`WeekCalendar()`: 水平滚动的基于周的日历。 + +`HeatMapCalendar()`: 水平滚动的热力图日历,用于显示随时间变化的数据。一个常见的例子是 GitHub 上的用户贡献图。 + +所有可组合项都基于 LazyRow/LazyColumn 实现以提高效率。 + +在下面的示例中,我们主要会使用基于月份的 `HorizontalCalendar` 和 `VerticalCalendar` 可组合项,因为所有日历可组合项共享相同的基本概念。如果您想要一个基于周的日历,请使用 `WeekCalendar` 可组合项。在基于周的日历中,大多数具有名称前缀/后缀 `month`(例如 `firstVisibleMonth`)的状态属性/方法在基于周的日历中将具有相应的名称前缀/后缀 `week`(例如 `firstVisibleWeek`)。 + +## 用法 + +`HorizontalCalendar` 和 `VerticalCalendar`: + +```kotlin +@Composable +fun MainScreen() { + val currentMonth = remember { YearMonth.now() } + val startMonth = remember { currentMonth.minusMonths(100) } // 根据需要进行调整 + val endMonth = remember { currentMonth.plusMonths(100) } // 根据需要进行调整 + val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } // 从库中获取 + + val state = rememberCalendarState( + startMonth = startMonth, + endMonth = endMonth, + firstVisibleMonth = currentMonth, + firstDayOfWeek = firstDayOfWeek + ) + + HorizontalCalendar( + state = state, + dayContent = { Day(it) } + ) + +// 如果您需要垂直日历。 +// VerticalCalendar( +// state = state, +// dayContent = { Day(it) } +// ) +} +``` + +注意:创建状态时可以提供一个附加参数:“outDateStyle”。 这决定了过时的生成方式。 请参阅 [状态类型](#状态类型) 部分以了解此参数。 + +`WeekCalendar`: + +```kotlin +@Composable +fun MainScreen() { + val currentDate = remember { LocalDate.now() } + val currentMonth = remember { YearMonth.now() } + val startDate = remember { currentMonth.minusMonths(100).atStartOfMonth() } // Adjust as needed + val endDate = remember { currentMonth.plusMonths(100).atEndOfMonth() } // Adjust as needed + val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } // Available from the library + + val state = rememberWeekCalendarState( + startDate = startDate, + endDate = endDate, + firstVisibleWeekDate = currentDate, + firstDayOfWeek = firstDayOfWeek + ) + + WeekCalendar( + state = state, + dayContent = { Day(it) } + ) +} +``` + +您的 `Day` composable 的最简单形式将是: + +```kotlin +@Composable +fun Day(day: CalendarDay) { + Box( + modifier = Modifier + .aspectRatio(1f), // 这对于方形大小很重要! + contentAlignment = Alignment.Center + ) { + Text(text = day.date.dayOfMonth.toString()) + } +} +``` + +在上面的示例中,我们使用 `Modifier.aspectRatio(1f)`,这是因为日历将月份的宽度除以 7 作为每个日期单元格的宽度。没有设置高度,所以您有灵活性来决定什么对您最有效。 + 要在日历上获得典型的正方形外观,您使用 `Modifier.aspectRatio(1f)` 告诉框使其高度与分配的宽度相同。 + +您可以选择设置特定的高度。例如:`Modifier.height(70.dp)` + +**这就是您需要的所有简单用法!但是请继续阅读,还有更多内容!** + +### 一周的第一天 + +当然,您希望在日历上显示适当日期的工作日标题。 + +`Sun | Mon | Tue | Wed | Thu | Fri | Sat` + +这是一个根据用户当前语言环境生成工作日的方法。 + +```kotlin +val daysOfWeek = daysOfWeek() // 从库中获取 +``` + +函数采用 `firstDayOfWeek` 参数,以防您希望生成工作日,以便期望的日期位于第一个位置。 + +例如: + +```kotlin +val daysOfWeek = daysOfWeek(firstDayOfWeek = DayOfWeek.THURSDAY) +// Will produce => Thu | Fri | Sat | Sun | Mon | Tue | Wed +``` +使用 `daysOfWeek` 列表,您可以设置日历,使一周的第一天符合用户的预期。这可以是星期日、星期一等。最好使用Locale返回的值,因为这是用户所期望的。 + +使用提供的 `daysOfWeek` 列表设置日历状态的步骤如下: + +```diff +- val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } ++ val daysOfWeek = remember { daysOfWeek() } + val state = rememberCalendarState( + startMonth = startMonth, + endMonth = endMonth, + firstVisibleMonth = currentMonth, +- firstDayOfWeek = firstDayOfWeek ++ firstDayOfWeek = daysOfWeek.first() + ) +``` + +您还可以使用 `daysOfWeek` 列表的值来设置工作日标题,以便与日历上显示的内容相匹配。 + +要设置星期几的标题,可以使用显示在每个月份上的月份标题,允许标题随着月份滚动,或者可以在日历上方的静态可组合项上显示标题。下面两种方式都进行了介绍: + +使用静态标题可组合项设置星期几: + +```kotlin +@Composable +fun DaysOfWeekTitle(daysOfWeek: List) { + Row(modifier = Modifier.fillMaxWidth()) { + for (dayOfWeek in daysOfWeek) { + Text( + modifier = Modifier.weight(1f), + textAlign = TextAlign.Center, + text = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.getDefault()), + ) + } + } +} +``` + +现在,您可以将标题可组合项与日历一起放在列中: + +```kotlin +@Composable +fun MainScreen() { + Column { + DaysOfWeekTitle(daysOfWeek = daysOfWeek) // 在这使用标题 + HorizontalCalendar( + state = state, + dayContent = { Day(it) } + ) + } +} +``` + +要将标题用作月标题,使其随每个月份滚动,请继续阅读下面的月标题和页脚部分! + +### 头部和尾部 + +要为每个月份添加页眉或页脚,步骤与我们为日期使用 `dayContent` 日历参数所做的相同,但不同的是,您需要提供 `monthHeader` 或 `monthFooter` 可组合参数,而不是 `dayContent`。 + +要将星期几的标题作为月份页眉添加,我们可以将上面讨论的相同的 `DaysOfWeekTitle` 可组合参数设置为 `monthHeader`: + +```kotlin +@Composable +fun MainScreen() { + HorizontalCalendar( + state = state, + dayContent = { Day(it) }, + monthHeader = { + DaysOfWeekTitle(daysOfWeek = daysOfWeek) //将月头作为标题 + } + ) +} +``` + +In the code above, we use the same `daysOfWeek` list that was created when we initialized the calendar state. However, we can also get the `daysOfWeek` list from the month data passed into the `monthHeader` parameter: + +```kotlin +@Composable +fun MainScreen() { + HorizontalCalendar( + state = state, + dayContent = { Day(it) }, + monthHeader = { month -> + // 您可能希望在此处使用 `remember {}`,以便映射不是每次都进行, + // 因为星期几的顺序只有在状态中设置新值给 `firstDayOfWeek` 时才会更改。 + val daysOfWeek = month.weekDays.first().map { it.date.dayOfWeek } + MonthHeader(daysOfWeek = daysOfWeek) + } + ) +} +``` + +在添加了星期几的标题后,您可以拥有一个类似于这样的日历: + +Month calendar + +您不仅可以将星期几的标题用作标题,还可以在日历之外的其他位置显示月份名称,如果尚未显示的话。随意发挥创意,制作独特的月标题和页脚!对于复杂的用法,请参阅示例项目。 + +### 日历容器 + +日历可组合项的两个有趣参数是 `monthBody` 和 `monthContainer`。通常情况下,您不需要这些。但如果您想在渲染日历之前进行一些自定义操作,那么这是适合的地方。 + +例如,如果您想在渲染所有日期的容器后面绘制一个渐变,并在整个月容器周围添加圆角/边框,并且还想缩小整个月容器,使其不适应屏幕宽度,那么 `monthBody` 和 `monthContainer` 将是: + +```kotlin +@Composable +fun MainScreen() { + HorizontalCalendar( + // 绘制日期内容的渐变 + monthBody = { _, content -> + Box( + modifier = Modifier.background( + brush = Brush.verticalGradient( + colors = listOf( + Color(0xFFB2EBF2), + Color(0xFFB2B8F2) + ) + ) + ) + ) { + content() // 渲染提供的容器 + } + }, + // 添加角落/边框和月份宽度。 + monthContainer = { _, container -> + val configuration = LocalConfiguration.current + val screenWidth = configuration.screenWidthDp.dp + Box( + modifier = Modifier + .width(screenWidth * 0.73f) + .padding(8.dp) + .clip(shape = RoundedCornerShape(8.dp)) + .border( + color = Color.Black, + width = 1.dp, + shape = RoundedCornerShape(8.dp) + ) + ) { + container() // 渲染提供的内容 + } + } + ) +} +``` + +通过上面的 `monthBody` 和 `monthContainer` 配置,我们将得到这个日历: + +Background styles + +### Composable 参数 + +- **calendarScrollPaged**: 日历的滚动行为。当设置为 `true` 时,在滚动或滑动操作后,日历将对齐到最近的月份。当设置为 `false` 时,日历将正常滚动。 +- **userScrollEnabled**: 是否允许通过用户手势或辅助功能操作进行滚动。即使禁用了用户滚动,您仍然可以通过状态进行编程方式的滚动。从 LazyRow/LazyColumn 继承。 +- **reverseLayout**: 反转滚动和布局的方向。当设置为 `true` 时,月份将从末尾到开头组成,而 `startMonth` 将位于末尾。从 LazyRow/LazyColumn 继承。 +- **contentPadding**: 整个日历周围的内边距。这将在内容被剪切后添加内边距,这是通过 `modifier` 参数不可能实现的。从 LazyRow/LazyColumn 继承。 + +### 状态类型 + +在通过 `rememberCalendarState()` 或 `rememberWeekCalendarState()` 创建状态时设置的所有属性都可以通过状态对象中相应的属性在未来进行更新。状态对象中还有其他一些值得一提的有趣属性。 + +**`HorizontalCalendar` 和 `VerticalCalendar` 的 `CalendarState` 属性:** + +- **firstVisibleMonth**: 在日历上可见的第一个月份。 + +- **lastVisibleMonth**: 在日历上可见的最后一个月份。 + +- **layoutInfo**: 在上一次布局传递期间计算的 `LazyListLayoutInfo` 的子类。例如,您可以使用它来计算当前可见的项目。 + +- **isScrollInProgress**: 当前此日历是否正在通过手势、滑动或编程方式进行滚动。 + +- **outDateStyle**: 确定日历上每个月如何生成 `outDates`。它可以是以下两个值之一: + + 1. **EndOfRow**: 日历将生成 `outDates` 直到达到月份行的末尾。这意味着如果一个月有5行,它将显示5行,如果一个月有6行,它将显示6行。 + 2. **EndOfGrid**: 日历将生成 `outDates` 直到达到每个月的 6 x 7 网格的末尾。这意味着所有月份都将有6行。 + + 此值还可以在通过 `rememberCalendarState(outDateStyle = ...)` 初始化日历状态时提供。 + +如果您想知道 `outDates` 和 `inDates` 是什么意思,让我们以下面的截图为例。 + +in-dates and out-dates + +在图像中,绿色注释中的日期是“inDates”,红色注释中的日期是“outDates”,而没有注释的日期是“monthDates”。 您可以在渲染日历日时检查这一点。 为了在图像上实现精确的效果,我们更新了“Day”可组合项: + +```kotlin +@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.White else Color.Gray + ) + } +} +``` + +- `inDates` 的 `position` 属性设置为 `DayPosition.InDate` + + `outDates` 的 `position` 属性设置为 `DayPosition.OutDate` + + `monthDates` 的 `position` 属性设置为 `DayPosition.MonthDate`,如上述代码片段中所见。 + + **`WeekCalendar` 的 `WeekCalendarState` 属性:** + + - **firstVisibleWeek**: 在日历上可见的第一个星期。 + + - **lastVisibleWeek**: 在日历上可见的最后一个星期。 + + - **layoutInfo**: 在上一次布局传递期间计算的 `LazyListLayoutInfo` 的子类。例如,您可以使用它来计算当前可见的项目。 + + - **isScrollInProgress**: 当前此日历是否正在通过手势、滑动或编程方式进行滚动。 + +### 状态方法 + +**`CalendarState` 方法:** + +- **scrollToMonth(month: YearMonth)**: 立即滚动到日历上的特定月份,无需动画。 + +- **animateScrollToMonth(month: YearMonth)**: 使用平滑的滚动动画滚动到日历上的一个月。 + +**`WeekCalendarState` 方法:** + +- **scrollToWeek(date: LocalDate)**: 立即滚动到包含给定日期的日历周,无需动画。 + +- **animateScrollToWeek(date: LocalDate)**: 使用平滑的滚动动画滚动到包含给定日期的日历周。 + +无需在此重复文档。请查看相应的类以获取所有可用属性和方法的详细文档。 + +### 日期点击 + +您可以像处理任何其他可组合项一样通过修饰符处理“Day”可组合项中的点击: + +```kotlin +@Composable +fun Day(day: CalendarDay, onClick: (CalendarDay) -> Unit) { + Box( + modifier = Modifier + .aspectRatio(1f) + .clickable( + enabled = day.position == DayPosition.MonthDate, + onClick = { onClick(day) } + ), + contentAlignment = Alignment.Center + ) { + Text(text = day.date.dayOfMonth.toString()) + } +} +``` + +### 日期选择 + +该库没有内置的选中/未选中日期的概念,这使您可以自由选择如何最好地实现此用例。 + +实现日期选择就像在 `Day` 可组合项中显示特定日期的背景一样简单。 + +例如,我只想要在日历上选择最后点击的日期。 + +首先,我们更新我们的 `Day` 可组合项,以在选择日期时显示一个圆形背景: + +```kotlin +@Composable +fun Day(day: CalendarDay, isSelected: Boolean, onClick: (CalendarDay) -> Unit) { + Box( + modifier = Modifier + .aspectRatio(1f) + .clip(CircleShape) + .background(color = if (isSelected) Color.Green else Color.Transparent) + .clickable( + enabled = day.position == DayPosition.MonthDate, + onClick = { onClick(day) } + ), + contentAlignment = Alignment.Center + ) { + Text(text = day.date.dayOfMonth.toString()) + } +} +``` + +接下来,使用上面日期点击部分已经展示的点击逻辑,我们在每次点击日期时更新选定日期的状态: + +```kotlin +@Composable +fun MainScreen() { + var selectedDate by remember { mutableStateOf(null) } + HorizontalCalendar( + state = state, + dayContent = { day -> + Day(day, isSelected = selectedDate == day.date) { day -> + selectedDate = if (selectedDate == day.date) null else day.date + } + } + ) +} +``` + +对于更复杂的选择逻辑,比如范围选择,请参阅示例项目。这相当简单,其中的奥秘都在于您的逻辑! + +### 禁用日期 + +正如预期的那样,该库不会在内部提供此逻辑,因此您具有完全的灵活性。 + +要禁用日期,您可以简单地将这些日期的文本设置为看起来已禁用,并禁用对这些日期的点击。例如,如果我们想显示 in 和 out 日期但禁用它们以防止选择,我们只需在文本上设置不同的颜色。 + +实际上,在日期点击部分的示例中,我们已经使用以下逻辑忽略了 in 和 out 日期的点击: + +```kotlin +@Composable +fun Day(day: CalendarDay, onClick: (CalendarDay) -> Unit) { + Box( + modifier = Modifier + .aspectRatio(1f) + .clickable( + enabled = day.position == DayPosition.MonthDate, // 只有月历可以被点击 + onClick = { onClick(day) } + ), + contentAlignment = Alignment.Center + ) { // 改变 in-dates 和 out-dates 的颜色, 您也可以完全隐藏它们! + Text( + text = day.date.dayOfMonth.toString(), + color = if (day.position == DayPosition.MonthDate) Color.White else Color.Gray + ) + } +} +``` + +现在我们已经介绍了典型用法。 图书馆的美妙之处在于它无限的可能性。 您不受如何构建用户界面的限制,该库为您提供所需的日历数据逻辑,并且您提供所需的 UI 逻辑。 + +有关一些复杂的实现,请参阅示例项目。 + +## 周历 + +正如之前讨论的,该库提供了 `HorizontalCalendar`、`VerticalCalendar` 和 `WeekCalendar` 可组合项。`WeekCalendar` 是一个基于周的日历。几乎所有上面介绍的关于月份日历的主题都适用于周历。主要的区别是状态属性/方法通常会有一个略有不同的名称,通常是带有 `week` 前缀/后缀而不是 `month`。 + +例如:`firstVisibleMonth` => `firstVisibleWeek`,`scrollToMonth()` => `scrollToWeek()` 等等,但您已经get到了这个概念。 + +我们已经在[用法](#用法)部分中展示了如何使用 `WeekCalendar`,但在最基本的形式中,它是: + +```kotlin +@Composable +fun MainScreen() { + val state = rememberWeekCalendarState( + startDate = ..., + endDate = ..., + firstVisibleWeekDate = ..., + firstDayOfWeek = ... + ) + WeekCalendar( + state = state, + dayContent = { Day(it) } + ) +} +``` + +来自示例应用程序的周历实现: + +Week calendar + +如果您想在月份和周模式之间切换日历,请参阅示例应用程序,我们在其中通过动画修改 `Modifier` 的高度以及使用 `AnimatedVisibility` API 进行了切换。 + +## 热力图日历 + +这是一个水平滚动的热力图日历实现,用于展示随时间变化的数据。一个常见的例子是 GitHub 上的用户贡献图表。另一个用途可能是展示用户追踪的习惯频率的变化。 + +示例应用程序中的截图如下: + +HeatMap calendar + +所有在基于月份的日历中的属性在热力图日历中也是可用的,除了 `OutDateStyle` 配置,因为在这种情况下这是不相关的。请注意,日历上有过时的日期,但由于日期是以列而不是行的方式布局的,因此这里不需要两个 `OutDateStyle` 选项 `EndOfRow` 和 `EndOfGrid`。所有其他基于月份的属性都是可用的! + +基本的热力图日历用法: + +```kotlin +@Composable +fun MainScreen() { + val currentMonth = remember { YearMonth.now() } + val startMonth = remember { currentMonth.minusMonths(100) } // 根据需要进行调整 + val endMonth = remember { currentMonth.plusMonths(100) } // 根据需要进行调整 + val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } // 从库中获取 + + val state = rememberHeatMapCalendarState( + startMonth = startMonth, + endMonth = startMonth, + firstVisibleMonth = currentMonth, + firstDayOfWeek = firstDayOfWeek, + ) + HeatMapCalendar( + state = state, + dayContent = { day, _ -> Day(day) }, + weekHeader = { WeekHeader(it) }, + monthHeader = { MonthHeader(it) } + ) +} +``` + +请查看 `HeatMapCalendar` 可组合项以获取完整的文档。示例应用程序中也有示例。 + +请记住,到目前为止显示的所有截图都只是使用该库可以实现的示例,您绝对可以根据自己的需要构建您的日历。 + +使用这个库创建了漂亮的日历吗?在[这里]()分享一张图片 diff --git a/docs_chs/View.chs.md b/docs_chs/View.chs.md new file mode 100644 index 00000000..504c5a4c --- /dev/null +++ b/docs_chs/View.chs.md @@ -0,0 +1,639 @@ +# 日历视图文档 + +## 目录 + +- [快速链接](#快速链接) +- [类信息](#类信息) +- [用法](#用法) + * [步骤1](#步骤1) + * [步骤2](#步骤2) + * [一周的第一天](#一周的第一天和星期标题) + * [页眉和页脚](#页眉和页脚) + * [属性](#属性) + * [性能](#性能) + * [方法](#方法) + * [日期点击](#日期点击) + * [日期选择](#日期选择) + * [禁用日期](#禁用日期) +- [周视图](#周视图) +- [常见问题解答](#常见问题解答) +- [迁移](#迁移) + +## 快速链接 + +如果尚未查看示例应用,请查看一下。大多数你想要实现的技术在示例中已经完成。 + +下载示例应用程序,请点击[此处](https://github.com/kizitonwose/Calendar/releases/download/2.0.0/sample.apk)。 + +阅读示例应用程序的源代码,请点击[此处](https://github.com/kizitonwose/Calendar/tree/main/sample)。 + +将库添加到你的项目中,请点击[此处](https://github.com/kizitonwose/Calendar/docs_chs/README.chs.md#设置)。 + +**如果你正在寻找Compose文档,你可以在[这里](https://github.com/kizitonwose/Calendar/blob/main/docs_chs/Compose.chs.md)找到。** + +## 类信息 + +该库可以通过两个类使用: + +`CalendarView`:传统的基于月份的日历。 + +`WeekCalendarView`:基于周的日历。 + +这两个类都继承自 `RecyclerView`,因此你可以使用所有 `RecyclerView` 的自定义功能,比如装饰器等。 + +在下面的示例中,我们将主要使用 `CalendarView` 类,因为这两个类共享相同的基本概念。如果你想要一个基于周的日历,在你的 XML/代码中将 `CalendarView` 替换为 `WeekCalendarView`。 +`CalendarView` 中带有名称前缀/后缀 `month`(例如 `monthHeaderResource`)的大多数 XML 属性和类属性/方法,在 `WeekCalendarView` 中都会有一个等效的名称前缀/后缀 `week`(例如 `weekHeaderResource`)。 + +## 用法 + +#### 步骤 1 + +像其他视图一样,将 `CalendarView` 添加到你的 XML 文件中。 + +```xml + +``` +查看所有可用的[属性](#属性)。 + +在 `res/layout/calendar_day_layout.xml` 中创建你的日视图资源。 + +```xml + +``` + +创建一个视图容器,它作为每个日期单元的视图持有者。在这里传递的视图是你提供的膨胀的日视图资源。 + +```kotlin +class DayViewContainer(view: View) : ViewContainer(view) { + val textView = view.findViewById(R.id.calendarDayText) + + // 使用 ViewBinding + // val textView = CalendarDayLayoutBinding.bind(view).calendarDayText +} +``` + +使用你的 `DayViewContainer` 类型为 `CalendarView` 提供一个 `MonthDayBinder`。 + +```kotlin +calendarView.dayBinder = object : MonthDayBinder { + // 只有在需要新的容器时才会调用。 + override fun create(view: View) = DayViewContainer(view) + + // 在每次需要重用容器时都会调用。 + override fun bind(container: DayViewContainer, data: CalendarDay) { + container.textView.text = data.date.dayOfMonth.toString() + } +} +``` + +#### 步骤 2 + +在你的 Fragment 或 Activity 中设置所需的日期: + +**`CalendarView` 设置:** + +```kotlin +val currentMonth = YearMonth.now() +val startMonth = currentMonth.minusMonths(100) // 根据需要进行调整 +val endMonth = currentMonth.plusMonths(100) // 根据需要进行调整 +val firstDayOfWeek = firstDayOfWeekFromLocale() // 从库中获取 +calendarView.setup(startMonth, endMonth, firstDayOfWeek) +calendarView.scrollToMonth(currentMonth) +``` + +**`WeekCalendarView` 设置:** + +```diff +- +``` + +```kotlin +val currentDate = LocalDate.now() +val currentMonth = YearMonth.now() +val startDate = currentMonth.minusMonths(100).atStartOfMonth() // 根据需要进行调整 +val endDate = currentMonth.plusMonths(100).atEndOfMonth() // 根据需要进行调整 +val firstDayOfWeek = firstDayOfWeekFromLocale() // 从库中获取 +weekCalendarView.setup(startDate, endDate, firstDayOfWeek) +weekCalendarView.scrollToWeek(currentDate) +``` + +**这就是简单使用所需的全部!但请继续阅读,还有更多内容!** + +### 一周的第一天和星期标题 + +当然,你想要在日历上的相应日期显示星期标题。 + +`Sun | Mon | Tue | Wed | Thu | Fri | Sat` + +这是一个根据用户当前的语言环境生成星期几的函数。 + +```kotlin +val daysOfWeek = daysOfWeek() // 从库中获取 +``` + +该函数接受一个 `firstDayOfWeek` 参数,以便在生成星期几时,将期望的星期几放在第一个位置。 + +例如: + +```kotlin +val daysOfWeek = daysOfWeek(firstDayOfWeek = DayOfWeek.THURSDAY) +// 将会生成 => Thu | Fri | Sat | Sun | Mon | Tue | Wed +``` +使用 `daysOfWeek` 列表,你可以设置日历,使一周的第一天是用户期望的星期几,比如星期日、星期一等。最好使用语言环境返回的值,因为这是用户期望的。 + +使用提供的 `daysOfWeek` 列表设置日历: + +```diff +- val firstDayOfWeek = firstDayOfWeekFromLocale() ++ val daysOfWeek = daysOfWeek() + calendarView.setup(startMonth, endMonth, daysOfWeek.first()) +``` + +你还应该使用 `daysOfWeek` 列表的值来设置星期标题,这样它就与在 `CalendarView` 上显示的内容匹配。 + +为了设置星期几的标题,你可以使用月标题,它会在每个月份显示标题,并允许标题随着月份滚动,或者你可以在日历上方显示一个静态视图。以下是两种方式的具体实现: + +使用静态视图设置星期几: + +#### 步骤 1 +在 `res/layout/calendar_day_title_text.xml` 中创建你的标题文本视图。 + +```xml + +``` + +在 `res/layout/calendar_day_titles_container.xml` 中创建一个容器资源以使用文本。 + +```xml + + + + + + + + + + + + + + + + + +``` + +在与 `CalendarView` 相同的布局中添加标题容器: + +```kotlin + + + + + + + +``` + +#### 步骤2 + +现在,你可以使用之前讨论的 `daysOfWeek` 列表来设置标题: + +```kotlin +val titlesContainer = findViewById(R.id.titlesContainer) +titlesContainer.children + .map { it as TextView } + .forEachIndexed { index, textView -> + val dayOfWeek = daysOfWeek[index] + val title = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.getDefault()) + textView.text = title + } +``` + +如果要将标题用作月标题,使其随着每个月份滚动,请继续阅读下面的月标题和页脚部分! + +### 页眉和页脚 + +要为每个月份添加页眉或页脚,步骤与我们上面为日期使用 `dayViewResource` 相同,但是不是提供日期资源,而是提供你的 `monthHeaderResource` 或 `monthFooterResource` 属性,然后设置 `CalendarView` 的 `monthHeaderBinder` 或 `monthFooterBinder` 属性。 + +要将星期几的标题作为月标题添加,我们将标题容器作为页眉资源提供: + +```xml + + +``` + +现在我们可以使用页眉来显示标题: + +```kotlin +class MonthViewContainer(view: View) : ViewContainer(view) { + // 或者,你可以向容器布局添加一个 ID,并使用 findViewById() 方法 + val titlesContainer = view as ViewGroup +} + +calendarView.monthHeaderBinder = object : MonthHeaderFooterBinder { + override fun create(view: View) = MonthViewContainer(view) + override fun bind(container: MonthViewContainer, data: CalendarMonth) { + // 请记住,页眉是可重复使用的,因此这将对每个月都调用。 + // 但是,一周的第一天不会改变, + // 因此不需要在每次重用时都绑定相同的视图. + if (container.titlesContainer.tag == null) { + container.titlesContainer.tag = data.yearMonth + container.titlesContainer.children.map { it as TextView } + .forEachIndexed { index, textView -> + val dayOfWeek = daysOfWeek[index] + val title = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.getDefault()) + textView.text = title + // 在上面的代码中,我们使用了在设置日历时创建的相同的 `daysOfWeek` 列表。 + // 然而,我们也可以从月份数据中获取 `daysOfWeek` 列表: + // val daysOfWeek = data.weekDays.first().map { it.date.dayOfWeek } + // 或者,您可以获取此特定索引的值: + // val dayOfWeek = data.weekDays.first()[index].date.dayOfWeek + } + } + } +} +``` + +你不仅可以将星期几的标题用作页眉,还可以显示月份名称,如果它尚未在日历外部的单独视图上显示。请随意在月标题和页脚上发挥创意!对于更复杂的用法,请参阅示例项目。 + +### 属性 + +#### XML(为了清晰起见,所有的前缀都是 `cv_`) + +**以下属性适用于`CalendarView`和`WeekCalendarView`类:** + +- **dayViewResource**: 用作日单元格视图的 XML 资源。必须提供此项。 + +- **scrollPaged**: 日历的滚动行为。如果为 `true`,日历将在滚动或滑动操作后自动对齐到最近的月份或周(在 `WeekCalendarView` 中)。如果为 `false`,日历将以正常方式滚动。 + +- **daySize**: 确定日历上每一天的大小如何计算。可以是以下三个值之一: + 1. **square**: 每一天的宽度和高度都与日历的宽度除以7相匹配。 + 2. **rectangle**: 每一天的宽度与日历的宽度除以7相匹配,而高度与日历的高度除以索引中的周数相匹配 - 月份日历可以是4、5或6,周日历为1。 + 使用此选项,如果要使每个月或周填充父元素的宽度和高度。 + 3. **seventhWidth**: 每一天的宽度与日历的宽度除以7相匹配。该天可以通过设置特定值或使用 `LayoutParams.WRAP_CONTENT` 来确定其高度。 + 4. **freeForm**: 该天可以通过设置特定值或使用 `LayoutParams.WRAP_CONTENT` 来确定其宽度和高度。 + +**以下属性仅适用于`CalendarView`类:** + +- **monthHeaderResource**: 用作每个月份页眉的 XML 资源。 + +- **monthFooterResource**: 用作每个月份页脚的 XML 资源。 + +- **orientation**: 日历的滚动方向,可以是 `horizontal` 或 `vertical`。默认为 `horizontal`。 + +- **monthViewClass**: 一个 ViewGroup,用作每个月份的容器。这个类必须有一个只接受 Context 的构造函数。如果启用了代码混淆,你应该排除这个类的名称和构造函数。 + +- **outDateStyle**: 这确定了如何为日历上的每个月份生成 `outDates`。可以是以下两个值之一: + 1. **endOfRow**: 日历将生成 `outDates` 直到达到月份行的末尾。这意味着如果一个月有5行,它将显示5行,如果一个月有6行,它将显示6行。 + 2. **endOfGrid**: 日历将生成 `outDates` 直到达到每个月的 6 x 7 网格的末尾。这意味着所有月份都将有6行。 + +如果你想知道 `outDates` 和 `inDates` 是什么意思,让我们使用下面的截图作为例子。 + +in-dates and out-dates + +在图像中,绿色标注内的日期是 `inDates`,红色标注内的日期是 `outDates`,而没有标注的日期是 `monthDates`。在绑定日历时,你可以检查这一点。要实现图像上的效果,我们可以这样做: + +```kotlin +calendarView.dayBinder = object : MonthDayBinder { + override fun create(view: View) = DayViewContainer(view) + override fun bind(container: DayViewContainer, data: CalendarDay) { + container.textView.text = data.date.dayOfMonth.toString() + if (data.position == DayPosition.MonthDate) { + container.textView.setTextColor(Color.WHITE) + } else { + container.textView.setTextColor(Color.GRAY) + } + } +} +``` + +`inDates` 的 `position` 属性设置为 `DayPosition.InDate` + +`outDates` 的 `position` 属性设置为 `DayPosition.OutDate` + +`monthDates` 的 `position` 属性设置为 `DayPosition.MonthDate` 如上面的代码片段中所示。 + +- **以下属性仅适用于`WeekCalendarView`类:** + - **weekHeaderResource**: 用作每周页眉的 XML 资源。 + + - **weekFooterResource**: 用作每周页脚的 XML 资源。 + + - **weekViewClass**: 一个 ViewGroup,用作每周的容器。这个类必须有一个只接受 Context 的构造函数。如果启用了代码混淆,你应该排除这个类的名称和构造函数。 + +### 性能 + +所有上面列出的相应 XML 属性也作为 `CalendarView` 和 `WeekCalendarView` 类的属性可用,因此它们可以通过代码设置。因此,除了上述属性之外,我们还有: + +**`CalendarView` 属性:** + +- **monthScrollListener**: 当日历滚动到新月份时调用。如果 `scrollPaged` 为 `true`,则大多数情况下是有益的。 + +- **dayBinder**: 用于管理日期单元格视图的 `MonthDayBinder` 实例。 + +- **monthHeaderBinder**: 用于管理头部视图的 `MonthHeaderFooterBinder` 实例。头部视图显示在日历上的每个月份上方。 + +- **monthFooterBinder**: 用于管理页脚视图的 `MonthHeaderFooterBinder` 实例。页脚视图显示在日历上的每个月份下方。 + +- **monthMargins**: 要在每个月视图上应用的边距,以像素为单位。这可以用于在两个月之间添加空间。 + +**`WeekCalendarView` 属性:** + +- **weekScrollListener**: 当日历滚动到新的一周时调用。如果 `scrollPaged` 为 `true`,则大多数情况下是有益的。 +- **dayBinder**: 用于管理日期单元格视图的 `WeekDayBinder` 实例。 + +- **weekHeaderBinder**: 用于管理头部视图的 `WeekHeaderFooterBinder` 实例。头部视图显示在日历上的每周上方。 + +- **weekFooterBinder**: 用于管理页脚视图的 `WeekHeaderFooterBinder` 实例。页脚视图显示在日历上的每周下方。 + +- **weekMargins**: 要在每周视图上应用的边距,以像素为单位。这可以用于在两个周之间添加空间。 + +### 方法 + +**`CalendarView` 方法:** + +- **scrollToDate(date: LocalDate)**: 滚动到日历上的特定日期。使用 `smoothScrollToDate()` 可以获得平滑的滚动动画。 + +- **scrollToMonth(month: YearMonth)**: 滚动到日历上的一个月。使用 `smoothScrollToMonth()` 可以获得平滑的滚动动画。 + +- **notifyDateChanged(date: LocalDate)**: 重新加载指定日期的视图。 + +- **notifyMonthChanged(month: YearMonth)**: 重新加载指定月份的页眉、主体和页脚视图。 + +- **notifyCalendarChanged()**: 重新加载整个日历。 + +- **findFirstVisibleMonth()** 和 **findLastVisibleMonth()**: 分别查找日历上第一个和最后一个可见的月份。 + +- **findFirstVisibleDay()** 和 **findLastVisibleDay()**: 分别查找日历上第一个和最后一个可见的日期。 + +- **updateMonthData()**: 在初始设置后更新 `CalendarView` 的起始月份、结束月份或一周的第一天。当前可见的月份会被保留。日历可以处理非常大的日期范围,因此您可能希望设置具有大日期范围的日历,而不是频繁地更新范围。 + +**`WeekCalendarView` 方法:** + +- **scrollToDate(date: LocalDate)**: 滚动到日历上的特定日期。使用 `smoothScrollToDate()` 可以获得平滑的滚动动画。 + +- **scrollToWeek(date: LocalDate)**: 滚动到包含此日期的星期。使用 `smoothScrollToWeek()` 可以获得平滑的滚动动画。 + +- **notifyDateChanged(date: LocalDate)**: 重新加载指定日期的视图。 + +- **notifyWeekChanged(date: LocalDate)**: 重新加载包含此日期的星期的页眉、主体和页脚视图。 + +- **notifyCalendarChanged()**: 重新加载整个日历。 + +- **findFirstVisibleWeek()** 和 **findLastVisibleWeek()**: 分别查找日历上第一个和最后一个可见的星期。 + +- **findFirstVisibleDay()** 和 **findLastVisibleDay()**: 分别查找日历上第一个和最后一个可见的日期。 + +- **updateWeekData()**: 在初始设置后更新 `WeekCalendarView` 的起始日期、结束日期或一周的第一天。当前可见的星期会被保留。日历可以处理非常大的日期范围,因此您可能希望设置具有大日期范围的日历,而不是频繁地更新范围。 + +无需列出所有可用的方法或在此重复文档。请查看 [CalendarView](https://github.com/kizitonwose/Calendar/blob/main/view/src/main/java/com/kizitonwose/calendar/view/CalendarView.kt) 和 [WeekCalendarView](https://github.com/kizitonwose/Calendar/blob/main/view/src/main/java/com/kizitonwose/calendar/view/WeekCalendarView.kt) 类,其中包含所有属性和方法的详细文档。 + +### 日期点击 + +您应该在提供给视图容器的视图上设置点击监听器。 + +日期单元格的 XML 文件 `calendar_day_layout.xml`: + +```xml + + +``` + +当然,你需要将这个文件设置为 `CalendarView` 上的 `cv_dayViewResource`: + +```xml + +``` + +在 Fragment 或 Activity 中的点击监听器实现: + +```kotlin +class DayViewContainer(view: View) : ViewContainer(view) { + val textView = view.findViewById(R.id.calendarDayText) + // 当此容器被绑定时将被设置 + lateinit var day: CalendarDay + + init { + view.setOnClickListener { + // 使用与此容器关联的 CalendarDay。 + } + } +} + +calendarView.dayBinder = object : MonthDayBinder { + override fun create(view: View) = DayViewContainer(view) + override fun bind(container: DayViewContainer, data: CalendarDay) { + // 为此容器设置日历日期。 + container.day = data + // 设置日期文本 + container.textView.text = data.date.dayOfMonth.toString() + // 任何其他绑定逻辑 + } +} +``` + +### 日期选择 + +该库没有内置的选中/未选中日期的概念,这使您可以自由选择最适合实现此用例的方式。 + +实现日期选择就像在日期绑定器上显示特定日期的背景一样简单。请记住,由于 `CalendarView` 和 `WeekCalendarView` 扩展自 `RecyclerView`,您需要取消不需要的日期上的任何特殊效果。 + +在这个例子中,我希望在日历上只选择最后点击的日期。 + +首先,让我们保留对所选日期的引用: + +```kotlin +private var selectedDate: LocalDate? = null +``` + +接下来,使用上面日期点击部分已经显示的视图容器上的点击逻辑,我们在点击日期时更新此字段,并在点击的日期上显示选中背景。 + +```kotlin +view.setOnClickListener { + // 检查日期位置,因为我们不希望选择 in 或 out 日期。 + if (day.position == DayPosition.MonthDate) { + // 保留对先前选择的任何引用 + // 以防我们覆盖它并需要重新加载它。 + val currentSelection = selectedDate + if (currentSelection == day.date) { + // 如果用户点击相同的日期,则清除选择。 + selectedDate = null + // 重新加载此日期以便调用 dayBinder + // 我们可以移除选择背景。 + calendarView.notifyDateChanged(currentSelection) + } else { + selectedDate = day.date + // 重新加载新选择的日期以便调用 dayBinder + // 我们可以添加选择背景。 + calendarView.notifyDateChanged(day.date) + if currentSelection != null { + // 我们还需要重新加载先前选择的日期, + // 以便我们可以移除选择背景。 + calendarView.notifyDateChanged(currentSelection) + } + } + } +} +``` + +最后,我们实现 `dayBinder` 来相应地反映选择: + +```kotlin +calendarView.dayBinder = object : MonthDayBinder { + override fun create(view: View) = DayViewContainer(view) + override fun bind(container: DayViewContainer, data: CalendarDay) { + container.day = data + val day = data + val textView = container.textView + textView.text = day.date.dayOfMonth.toString() + if (day.position == DayPosition.MonthDate) { + // 显示月份日期。请记住,视图是可重用的! + textView.visibility = View.VISIBLE + if (day.date == selectedDate) { + // 如果这是选定的日期,请显示圆形背景并更改文本颜色。 + textView.setTextColor(Color.WHITE) + textView.setBackgroundResource(R.drawable.selection_background) + } else { + // 如果这不是选定的日期,请移除背景并重置文本颜色。. + textView.setTextColor(Color.BLACK) + textView.background = null + } + } else { + // 隐藏 in 和 out 日期 + textView.visibility = View.INVISIBLE + } + } +} +``` + +对于更复杂的选择逻辑(例如范围选择),请参阅示例项目。 很简单,神奇之处在于您的绑定逻辑! + +### 禁用日期 + +正如预期的那样,该库不会在内部提供此逻辑,因此您拥有完全的灵活性。 + +要禁用日期,您只需将这些日期上的文本设置为禁用并忽略对这些日期的点击即可。 例如,如果我们想要显示开始和结束日期,但禁用它们以便无法选择它们,我们只需在`dayBinder`中设置这些日期的 alpha 属性即可达到禁用的效果。 当然,如果您愿意,您可以设置不同的颜色。 + +继续日期选择部分中的示例,我们已经使用以下逻辑忽略了输入和输出日期的点击: + +```kotlin +view.setOnClickListener { + // 检查日期位置,因为我们不想选择开始或结束日期。 + if (day.position == DayPosition.MonthDate) { + // 只使用月份日期 + } +} +``` + +然后在`dayBinder`中,我们检查日期位置并相应地设置文本 alpha 或颜色: + +```kotlin +calendarView.dayBinder = object : MonthDayBinder { + override fun create(view: View) = DayViewContainer(view) + override fun bind(container: DayViewContainer, data: CalendarDay) { + container.day = data + val textView = container.textView + textView.text = data.date.dayOfMonth.toString() + textView.alpha = if (day.position == DayPosition.MonthDate) 1f else 0.3f +} +``` + +现在我们已经介绍了典型用法。 这个库的美妙之处在于它无限的可能性。 您不受如何构建用户界面的限制,该库为您提供所需的日历数据逻辑,并且您提供所需的 UI 逻辑。 + +有关一些复杂的实现,请参阅示例项目。 + +## 周视图 + +正如之前讨论的,该库提供了两个类`CalendarView`和`WeekCalendarView`。`WeekCalendarView`类是一个基于周的日历实现。几乎上面提到的所有适用于月历的主题都适用于周历。主要的区别在于xml属性和类的属性/方法将具有略微不同的名称,通常是以`week`为前缀/后缀,而不是`month`。 + +例如:`monthHeaderResource` => `weekHeaderResource`,`scrollToMonth()` => `scrollToWeek()`,`findFirstVisibleMonth()` => `findFirstVisibleWeek()`等等,但你可以理解这个思路。 + +要在布局中显示周历,添加以下视图: + +```xml + +``` + +然后按照上面的设置说明提供一个日资源/绑定器等,就像您为月历所做的那样。 + +如果您想在月视图和周视图之间切换日历,请参见示例应用程序,我们在其中使用了`ValueAnimator`来实现此功能。您可以使用您喜欢的任何动画逻辑。结果如下所示: + +Week and month modes + +如果您希望在周历中一次显示超过或少于7天,您应将`scrollPaged`属性设置为`false`。此外,将`daySize`属性设置为`FreeForm`,这使您可以自定义日单元的首选大小。请阅读`DaySize`类中的文档,以充分了解可用的选项。 + +来自示例应用程序的周历实现: + +Week calendar + +请记住,到目前为止显示的所有截图只是使用该库可以实现的示例,您绝对可以按照自己的喜好构建您的日历。 + +**使用这个库创建了漂亮的日历吗?在[这里](https://github.com/kizitonwose/Calendar/issues/1)分享一张图片。** + +## 常见问题解答 + +**问**: 我如何在Java项目使用这个库 + +**答**: 它可以直接使用,但是 `MonthScrollListener` 不是一个接口而是一个 Kotlin 函数。要在 Java 项目中设置 `MonthScrollListener`,请查看 [此链接](https://github.com/kizitonwose/Calendar/issues/74)。 + +**问**: 如何禁用用户在日历上滚动,以便只能以编程方式滚动? + +**答**: 查阅[此处](https://github.com/kizitonwose/Calendar/issues/38#issuecomment-525786644). + +## 迁移 + +请参阅[迁移指南](https://github.com/kizitonwose/calendar/blob/main/docs_chs/MigrationGuide.chs.md) + From 7aa94b3a3bd13ec4ea96b1a3eea4c5d71df00a66 Mon Sep 17 00:00:00 2001 From: Blackwen Date: Fri, 29 Dec 2023 11:28:36 +0800 Subject: [PATCH 4/6] Add Chinese documentation for Migration Guide --- docs_chs/MigrationGuide.chs.md | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 docs_chs/MigrationGuide.chs.md diff --git a/docs_chs/MigrationGuide.chs.md b/docs_chs/MigrationGuide.chs.md new file mode 100644 index 00000000..7962dafd --- /dev/null +++ b/docs_chs/MigrationGuide.chs.md @@ -0,0 +1,50 @@ +# 迁移指南 + +- [View](#view) + * [1.x.x => 2.x.x](#1xx--2xx) + * [0.3.x => 0.4.x / 1.x.x](#03x--04x--1xx) +- [Compose](#compose) + +## View + +### 1.x.x => 2.x.x + +- 如果你从版本 `1.x.x` 升级到 `2.x.x`,请记住版本 2 是库的完全重写,重点是性能。在这个背景下,以下是一些破坏性的更改: + + - `async()` 方法已被移除,因为现在日历能够处理非常大的日期范围而不会出现性能问题。日历数据是根据需要生成的。已删除的方法有 `setupAsync()` 和 `updateMonthRangeAsync()`。请使用方法 `setup()` 和 `updateMonthData()`。 + + - `updateMonthRange()` 方法已重命名为 `updateMonthData()`,因为它现在允许你在需要时可选地更改一周的第一天。 + + - `daySize` 属性不再是具有 `height` 和 `width` 值的 `Size` 类型。它现在是一个带有三个选项的 `DaySize` 枚举 - `Square`、`SeventhWidth` 和 `FreeForm`。请查看 `DaySize` 枚举文档以了解每个值。 + + - `inDateStyle` 枚举属性已被移除,因为它现在是多余的。每个月的数据中都包含了 in-dates,你可以选择在视图上隐藏它们或保留它们。 + + - `outDateStyle` 枚举属性仍然可用。但现在有两个选项 `EndOfRow` 和 `EndOfGrid`。先前可用的第三个选项 `None` 已被移除。如果不需要 out-dates,你可以简单地隐藏它们。 + + - `scrollMode` 枚举属性现在是一个名为 `scrollPaged` 的布尔类型。将此属性设置为 `false` 以获得以前的 `ScrollMode.CONTINUOUS` 滚动行为,或将其设置为 `true` 以获得以前的 `ScrollMode.PAGED` 行为。 + + - `DayOwner` 枚举已重命名为 `DayPosition`。匹配的 case 值为: + - `DayOwner.PREVIOUS_MONTH` => `DayPosition.InDate` + - `DayOwner.THIS_MONTH` => `DayPosition.MonthDate` + - `DayOwner.NEXT_MONTH` => `DayPosition.OutDate` + + - `maxRowCount` 属性已被移除,因为现在有一个应该用于周历的 `WeekCalendarView` 类。主要的 `CalendarView` 类用于月历实现。 + + - `hasBoundaries` 属性已被移除,因为在上面讨论的周历和月历实现引入后不再需要。 + + - `monthMarginStart` | `monthMarginTop` | `monthMarginEnd` | `monthMarginBottom` 属性已合并为一个 `monthMargins` 属性。 + + - `monthPaddingStart` | `monthPaddingTop` | `monthPaddingEnd` | `monthPaddingBottom` 属性已被移除,因为它们没有提供真正的好处。如果需要,可以在自定义的 `monthViewClass` 中设置填充。 + + +### 0.3.x => 0.4.x / 1.x.x + +如果你从版本 `0.3.x` 升级到 `0.4.x` 或 `1.x.x`,主要的变化是该库从使用 [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP) 切换到使用 [Java 8 API desugaring](https://developer.android.com/studio/write/java8-support#library-desugaring) 来处理日期。在遵循新的 [设置](https://github.com/kizitonwose/Calendar#setup) 说明之后,你需要做的下一步是将与日期/时间相关的类的导入从 `org.threeten.bp.*` 更改为 `java.time.*`。 + +你还需要从你的应用程序类的 `onCreate()` 方法中删除 `AndroidThreeTen.init(this)` 这一行,因为它不再需要。 + + + +## Compose + +这里暂时没有什么内容... 😉 From d46e04cb8719ec0f124256702dc26e02bfe833a1 Mon Sep 17 00:00:00 2001 From: Blackwen Date: Fri, 29 Dec 2023 11:30:21 +0800 Subject: [PATCH 5/6] update:Button --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a9b66730..e9f25a4f 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ LazyRow/LazyColumn for compose. [![License](https://img.shields.io/badge/License-MIT-0097A7.svg)](https://github.com/kizitonwose/Calendar/blob/main/LICENSE.md) [![Twitter](https://img.shields.io/badge/Twitter-@kizitonwose-9C27B0.svg)](https://twitter.com/kizitonwose) [![cn](https://img.shields.io/badge/Lang-Chinese-blue?color=%23FF0000)](docs_chs/README.chs.md) + **With this library, your calendar will look however you want it to.** ![Preview](https://user-images.githubusercontent.com/15170090/197389318-b3925b65-aed9-4e1f-a778-ba73007cbdf7.png) From dc35dbbca339d7671216a321b0f5bc8edbe8877f Mon Sep 17 00:00:00 2001 From: Blackwen Date: Fri, 29 Dec 2023 11:41:39 +0800 Subject: [PATCH 6/6] update:Table of contents --- docs_chs/Compose.chs.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs_chs/Compose.chs.md b/docs_chs/Compose.chs.md index 38dadaf1..0acf8a9a 100644 --- a/docs_chs/Compose.chs.md +++ b/docs_chs/Compose.chs.md @@ -3,14 +3,14 @@ ## 目录 - [快速链接](#快速链接) -- [Compose 版本](#Compose 版本) -- [日历 Composables](#日历 Composables) +- [Compose 版本](#Compose版本) +- [日历 Composables](#日历Composables) - [使用方法](#使用方法) * [日历状态](#日历状态) * [一周的第一天](#一周的第一天) * [头部和尾部](#头部和尾部) * [日历容器](#日历容器) - * [Composable 参数](#Composable 参数) + * [Composable 参数](#Composable参数) * [状态属性](#状态属性) * [状态方法](#状态方法) * [日期点击](#日期点击) @@ -43,7 +43,7 @@ | 1.5.x | 2.4.x | | 1.6.x | 2.5.x | -## 日历 Composables +## 日历Composables 该库可以通过四个可组合项使用: @@ -308,7 +308,7 @@ fun MainScreen() { Background styles -### Composable 参数 +### Composable参数 - **calendarScrollPaged**: 日历的滚动行为。当设置为 `true` 时,在滚动或滑动操作后,日历将对齐到最近的月份。当设置为 `false` 时,日历将正常滚动。 - **userScrollEnabled**: 是否允许通过用户手势或辅助功能操作进行滚动。即使禁用了用户滚动,您仍然可以通过状态进行编程方式的滚动。从 LazyRow/LazyColumn 继承。