HarmonyOS 鸿蒙Next中关于用TextPicker封装一个DateSelector的时候遇到的困难
HarmonyOS 鸿蒙Next中关于用TextPicker封装一个DateSelector的时候遇到的困难 各位大佬,我在用TextPicker封装一个日期选择组件的时候遇到了这样的问题,日期从3月1号跳转到2月的28/29号时以及从2月的28/29号跳转到3月1号的时候,封装的组件要么是日期混乱,要么就是一下子跳转到了1月份,请各位大佬帮我看一下我的这个组件的代码,如果可以的话请把我的bug指出,非常感谢。
初步判断bug应该出现在161~177行之间。
import { application, common, EnvironmentCallback } from '@kit.AbilityKit'
import { BusinessError } from '@kit.BasicServicesKit'
@ComponentV2
export struct LoopDatePage {
build() {
NavDestination() {
this.DateSelectorBuilder()
}
.title("LoopDatePage")
.width("100%")
.height("100%")
}
@Builder
DateSelectorBuilder() {
Column({ space: 20 }) {
DatePicker()
DateSelector()
}
.width("100%")
.height("100%")
}
}
// 选择器组件
@ComponentV2
struct DateSelector {
/**
* @param canLoop
* 是否可以循环
*/
@Param canLoop: boolean = true
/**
* @param startYear
* 年份开始范围
*/
@Param startYear: number = 150
/**
* @param endYear
* 年份结束范围
*/
@Param endYear: number = 12
/**
* 默认年/月/日传参
*/
@Param yearParam: number | undefined = undefined
@Param monthParam: number | undefined = undefined
@Param dayParam: number | undefined = undefined
@Local date: Date = new Date()
// 年/月/日列表
@Local yearArr: string[] = []
@Local monthArr: string[] = []
@Local dayArr: string[] = []
// 选中的日期
@Local selectedYearIndex: number = 0
@Local selectedMonthIndex: number = 0
@Local selectedDayIndex: number = 0
// 处理联动 年 - 月
@Local nowMonthIndex: number = 0
@Local nextMonthIndex: number = 0
// 处理联动 月 - 日
@Local nowIndex: number = 0
@Local nextIndex: number = 0
@Local nowArrLength: number = 0
@Local nextArrLength: number = 0
// 获取上下文
private moduleContext: common.Context | undefined = undefined
// 国际化
@Local isEnglishLanguage: boolean = false
// 是否显示单位
@Local showUnit: boolean = true
/**
* 取值回调返回
*/
@Event callbackDate: (year: number, month: number, day: number) => void =
(year: number, month: number, day: number) => {
}
async aboutToAppear() {
// 获取上下文
this.registerLocalesChangeEvent(async () => {
this.moduleContext = await this.initLocalesModuleResourceContextAsync()
this.toCheckLanguageEnvironment()
this.getDateArr()
})
this.moduleContext = await this.initLocalesModuleResourceContextAsync()
this.toCheckLanguageEnvironment()
this.initLoopDate()
this.getDateArr()
}
build() {
Column() {
Stack() {
// 选中项背景阴影
Row() {
Row() {
}
.width("100%")
.height("100%")
.backgroundColor("#f4f6f7")
.borderRadius(16)
}
.padding({
right: 20,
left: 20
})
.width("100%")
.height(48)
// 年月日滚动选择器
Row() {
TextPicker({
range: this.yearArr,
selected: $$this.selectedYearIndex,
})
.pickerStyle(this.canLoop)
.visibility(this.isEnglishLanguage ? Visibility.None : Visibility.Visible)
.onChange((item, index) => {
this.selectedYearIndex = index as number
})
.onScrollStop(() => {
this.callbackDate((this.selectedYearIndex + (this.date.getFullYear() - this.startYear)), this.selectedMonthIndex + 1, this.selectedDayIndex + 1)
})
TextPicker({
range: this.monthArr,
selected: $$this.selectedMonthIndex
}).pickerStyle(this.canLoop)
.onEnterSelectedArea((item, index) => {
let trans = index as number
this.nextMonthIndex = trans
if ((this.nowMonthIndex == 11 && this.nextMonthIndex == 0) || (this.nowMonthIndex == 0 && this.nextMonthIndex == 0)) {
this.selectedYearIndex = ((this.selectedYearIndex == this.yearArr.length) ? 0 : (this.selectedYearIndex + 1))
}
if ((this.nowMonthIndex == 0 && this.nextMonthIndex == 11) || (this.nowMonthIndex == 11 && this.nextMonthIndex == 11)) {
this.selectedYearIndex = ((this.selectedYearIndex == 0) ? (this.yearArr.length) : (this.selectedYearIndex - 1))
}
})
.onChange((item, index) => {
let trans = index as number
this.nowMonthIndex = trans
this.selectedMonthIndex = trans
this.changeDayArr()
})
.onScrollStop(() => {
this.callbackDate((this.selectedYearIndex + (this.date.getFullYear() - this.startYear)), this.selectedMonthIndex + 1, this.selectedDayIndex + 1)
})
TextPicker({
range: this.dayArr,
selected: $$this.selectedDayIndex
}).pickerStyle(this.canLoop)
.onEnterSelectedArea((item, index) => {
this.nextIndex = index as number
let arrLength = (this.dayArr.length - 1)
if ((this.nowIndex == arrLength && this.nextIndex == 0) || (this.nowIndex == 0 && this.nextIndex == 0)) {
this.selectedMonthIndex = (this.selectedMonthIndex == 11) ? 0 : this.selectedMonthIndex + 1
}
if ((this.nowIndex == 0 && this.nextIndex == arrLength) || (this.nowIndex == arrLength && this.nextIndex == arrLength)) {
this.selectedMonthIndex = (this.selectedMonthIndex == 0) ? 11 : this.selectedMonthIndex - 1
// Todo 有问题,需要重构逻辑
}
})
.onChange((item, index) => {
let trans = index as number
this.nowIndex = trans
this.selectedDayIndex = trans
this.changeDayArr()
})
.onScrollStop(() => {
this.callbackDate((this.selectedYearIndex + (this.date.getFullYear() - this.startYear)), this.selectedMonthIndex + 1, this.selectedDayIndex + 1)
})
TextPicker({
range: this.yearArr,
}).pickerStyle(this.canLoop)
.visibility(this.isEnglishLanguage ? Visibility.Visible : Visibility.None)
.onChange((item, index) => {
this.selectedYearIndex = index as number
})
.onScrollStop(() => {
this.callbackDate((this.selectedYearIndex + (this.date.getFullYear() - this.startYear)), this.selectedMonthIndex + 1, this.selectedDayIndex + 1)
})
}
.width("100%")
.justifyContent(FlexAlign.Center)
}
.width("100%")
.alignContent(Alignment.Center)
}
.width("100%")
}
/**
* 注册国际化改变回调
*/
registerLocalesChangeEvent(callback: VoidCallback) {
let environmentCallback: EnvironmentCallback = {
onConfigurationUpdated(config) {
callback()
},
onMemoryLevel(level) {
}
};
getContext(this).getApplicationContext().on('environment', environmentCallback);
}
/**
* 初始化语言模块上下文
* @returns
*/
initLocalesModuleResourceContextAsync(): Promise<Context | undefined> {
return new Promise<common.Context | undefined>((res) => {
try {
application.createModuleContext(getContext(this), 'locales').then((data: Context) => {
res(data)
}).catch((error: BusinessError) => {
res(undefined)
})
} catch (error) {
res(undefined)
}
})
}
/**
* 初始化数组
*/
getDateArr() {
this.yearArr = []
this.monthArr = []
this.dayArr = []
let year = this.date.getFullYear()
let test: string | undefined = undefined
test = this.moduleContext?.resourceManager.getStringByNameSync("year")
for (let i = (year - this.startYear); i <= (year - this.endYear); i++) {
this.yearArr.push(i.toString() + (this.showUnit ? test : ""))
}
let arr = this.moduleContext?.resourceManager.getStringByNameSync("month_arr")
this.monthArr = JSON.parse(arr!)
test = this.moduleContext?.resourceManager.getStringByNameSync("date")
for (let i = 1; i <= 31; i++) {
this.dayArr.push(i.toString().padStart(2, " ") + (this.showUnit ? test : ""))
}
}
/**
* 检查语言环境,改变UI布局
*/
toCheckLanguageEnvironment() {
let trans = (this.moduleContext?.resourceManager.getConfigurationSync().locale as string).slice(0, 2)
// this.getUIContext().showAlertDialog({
// message: JSON.stringify(trans)
// })
this.showUnit = this.checkShowUnit(trans) ? true : false
this.isEnglishLanguage = (trans === "en" ? true : false)
}
/**
* 检查是否应该显示单位(年/月/日)
*/
checkShowUnit(str: string): boolean {
let bool: boolean = true
// 后续不显示单位的国家标识可以添加到数组中
let areaArr: string[] = ["en", "ru", "fr", "es", "ar"]
bool = areaArr.includes(str) ? false : true
return bool
}
/**
* 改变天数(月份控制)
*/
changeDayArr() {
let currentMonth = this.selectedMonthIndex + 1
const month_31: number[] = [1, 3, 5, 7, 8, 10, 12]
const month_30: number[] = [4, 6, 9, 11]
if (month_31.includes(currentMonth)) {
if (this.dayArr.length != 31) {
this.dayArray(31)
}
} else if (month_30.includes(currentMonth)) {
if (this.dayArr.length != 30) {
if (this.selectedDayIndex == 30) {
this.selectedDayIndex = 29
}
this.dayArray(30)
}
} else {
if (this.isLeapYear(Number((this.yearArr[this.selectedYearIndex] ?? []).slice(0, 4)))) {
if (this.dayArr.length != 29) {
if (this.selectedDayIndex >= 28) {
this.selectedDayIndex = 28
}
this.dayArray(29)
}
} else {
if (this.dayArr.length != 28) {
if (this.selectedDayIndex >= 27) {
this.selectedDayIndex = 27
}
this.dayArray(28)
}
}
}
}
/**
* 判断是否是闰年
*/
isLeapYear(year: number): boolean {
return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
}
/**
* 用于往日期数组重置
*/
dayArray(num: number) {
this.dayArr = []
let test = this.moduleContext?.resourceManager.getStringByNameSync("date")
for (let i = 1; i <= num; i++) {
this.dayArr.push(i.toString().padStart(2, " ") + (this.showUnit ? test : ""))
}
}
/**
* 初始化组件
*/
initLoopDate() {
this.selectedYearIndex = (this.yearParam ? this.yearParam - (this.date.getFullYear() - this.startYear) : this.date.getFullYear() - 1909)
this.selectedMonthIndex = (this.monthParam ? this.monthParam - 1 : 0)
this.selectedDayIndex = this.dayParam ? this.dayParam - 1 : 0
this.callbackDate((this.selectedYearIndex + (this.date.getFullYear() - this.startYear)), this.selectedMonthIndex + 1, this.selectedDayIndex + 1)
}
}
/**
* 选择器公共样式
* @param canLoop 是否可以循环
*/
@Extend(TextPicker)
function pickerStyle(canLoop: boolean) {
.canLoop(canLoop)
.divider(null)
.disappearTextStyle({
color: "#00dddddd",
font: {
size: 0
}
})
.selectedTextStyle({
color: "#1B2126",
font: {
size: 20,
weight: FontWeight.Medium
}
})
.textStyle({
color: "575e66",
font: {
size: 15,
weight: FontWeight.Regular
}
})
}
更多关于HarmonyOS 鸿蒙Next中关于用TextPicker封装一个DateSelector的时候遇到的困难的实战教程也可以访问 https://www.itying.com/category-93-b0.html
开发者您好,
当前测试了您提供的代码,当日期由3月1日向上滑动到31日,日那一列跳转到28日,月跳转至2月,
从2月28日向下滑动到1日,日那一列跳转为1日,月那一列为3月,这个现象是正常的,
您提到一下子跳转为1月份,我这边复现步骤是:先将日志固定至3月1日,调整月份到2月,再将日由1号跳转到28号,此时月份确实会跳转到1月份,
这里的逻辑是如果您预备由3月1号跳转到2月28号,直接将日由1跳转至28即可。
上面先调月份至2月,此时的日期为2月1日,日再上调至31日时,系统默认是由2月1号调至1月31号,所有自动更新月份为1月了。
您可以参看下DatePicker实现筛选年月日的逻辑(https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-datepicker#示例3设置显示年月和月日列),跟您当前提供的现象是一致的。按这个逻辑来说,这不是BUG。
更多关于HarmonyOS 鸿蒙Next中关于用TextPicker封装一个DateSelector的时候遇到的困难的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
为什么我这段代码在本地跑的时候就会出现bug,能麻烦您再多测试一下嘛?比如快速来回滑动切换日期查看月份是否正常,或者快速朝某个方向滑动看月份是否切换正常。
尊敬的开发者,您好!您的问题已受理,请您耐心等待,感谢您的理解与支持!
直接提工单
开发者您好,为了更快解决您的问题,尽量补全以下信息: (如下信息根据实际情况选择)
-
问题现象,本地运行你的代码没有复现问题,请提供下复现步骤以及出现问题的截图;
-
版本信息(如:开发工具、手机系统版本信息);
2月份的日期,包括从3月1号到2月28/29号,或者从2月28/29号到3月1号的来回切换的时候会出现问题。 开发工具版本是win版本的6.0.0.848的DevEco,测试机版本是6.0develop版本的mate60。
找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17
我这边本地使用的是6.0.0.848版本的DevEco Studio,测试手机版本是6.0.0.100,我这边测试是3月1日向上滑动到31日,日那一列变成了28日,月那一列变成2月,从2月28日向下滑动到1日,日那一列变成了1日,月那一列变成3月,这个现象应该是正常的吧,另外你测试机的具体版本号是什么。
我这边的测试机是6.0.0.100版本的Mate60Pro,但是就是会出现这个问题,日期月份联动有问题,
问题出在月份切换时的年索引计算逻辑上。在 onEnterSelectedArea
回调中,当月份从3月(索引2)切换到2月(索引1)时,代码错误地调整了年份索引。
具体问题在161-177行,特别是:
- 月份切换边界条件判断不准确,导致年份错误增减
- 年份索引计算逻辑存在缺陷,没有正确处理跨年边界
建议修复:
- 简化月份切换时的年份联动逻辑,避免在
onEnterSelectedArea
中直接修改年份索引 - 在
onChange
回调中统一处理日期联动,确保年月日同步更新 - 使用更可靠的方法计算闰年2月的天数变化
关键是要确保当月数变化时,年份只在真正跨年时(1月到12月或12月到1月)才进行调整,而不是在任意月份切换时都触发年份变化。