鸿蒙5.0开发进阶:日历切换案例

打印 上一主题 下一主题

主题 1014|帖子 1014|积分 3042

往期鸿蒙全套实战文章必看:



  • 鸿蒙开发核心知识点,看这篇文章就够了
  • 最新版!鸿蒙HarmonyOS Next应用开发实战学习路线
  • 鸿蒙HarmonyOS NEXT开发技能最全学习路线指南
  • 鸿蒙应用开发实战项目,看这一篇文章就够了(部分项目附源码)

先容

本示例先容使用Swiper实现自定义日历月视图和周视图左右滑动切换月或周的效果。同时使用分段按钮和Tabs实现月视图和周视图的切换效果。
效果图预览



使用阐明

  • 进入页面,在月视图上手指往右滑动,可切换到上个月,往左滑动可切换到下个月。
  • 在月视图上点击非当日日期,日期上显示绿色边框选中效果。选中当日日期,当日日期显示为红底白字。
  • 月视图上点击非当月的日期,可切换到对应月,同时日期显示选中效果。
  • 点击“周”按钮,可从月视图切换到周视图,周视图展示的周信息根据月视图之前选中的日期举行跳转。
  • 周视图左右滑动可切换下一周和上一周。
  • 周视图上选中日期后,点击“月”按钮,可从周视图切换到月视图,月视图展示的月份信息根据周视图之前选中的日期举行月份跳转。
  • 周视图切换时,默认根据周视图中第一天的年代信息刷新页面顶部的“xxxx年x月”数据。手动点击周视图日期时,则根据选中的年代信息刷新数据。
实现思路


  • 自定义日历组件CustomCalendar。这里参考日历三方库@xsqd/calendar的部分源码使用两个ForEach循环实现日历的月视图和周视图的日期布局效果。通过CalendarStateType条件渲染对应的月视图或周视图。
  1. if (this.calendarState === CalendarStateType.MONTH) {
  2.   // 月视图
  3.   ForEach(this.monthDays, (items: Day[], index: number) => {
  4.     Row() {
  5.       ForEach(items, (item: Day) => {
  6.         this.monthDayBuilder(item, index + 1)
  7.       }, (item: Day, index: number) => {
  8.         return item.dayNum + "" + index
  9.       })
  10.     }
  11.     .width($r('app.string.calendar_switch_full_size'))
  12.     .justifyContent(FlexAlign.SpaceBetween)
  13.   }, (item: Day[], index: number) => {
  14.     return item.reduce((item1, item2) => {
  15.       return item1 + item2.dayInfo.year + item2.dayInfo.month + item2.dayInfo.date
  16.     }, '') + index
  17.   })
  18. } else if (this.calendarState === CalendarStateType.WEEK) {
  19.   // 周视图
  20.   ForEach(this.weekDays, (items: Day[], index: number) => {
  21.     Row() {
  22.       ForEach(items, (item: Day) => {
  23.         this.weekDayBuilder(item)
  24.       }, (item: Day, index: number) => {
  25.         return item.dayNum + "" + index;
  26.       })
  27.     }
  28.     .width($r('app.string.calendar_switch_full_size'))
  29.     .justifyContent(FlexAlign.SpaceBetween)
  30.   }, (item: Day[], index: number) => {
  31.     return item.reduce((item1, item2) => {
  32.       return item1 + item2.dayInfo.year + item2.dayInfo.month + item2.dayInfo.date
  33.     }, '') + index
  34.   })
  35. }
复制代码

  • 为了实现月视图和周视图的左右切换月或周的效果,通过在Swiper里分别使用三个自定义日历组件CustomCalendar实现。以月视图为例,Swiper中索引0,1,2分别对应上个月,本月,下个月份的数据,通过yearMonthInfo举行指定。周视图类似,通过weekNum指定上一周,本周,下一周的数据。源码参考CalendarSwitch.ets。
  1. Swiper() {
  2.   CustomCalendar({
  3.     currentSelectDay: this.currentSelectDay, // 必选项。选中的日期(年月日)
  4.     yearMonthInfo: this.lastYearMonth, // 必选项。设置日历年月信息
  5.     weekNum: this.weekNumDefault, // 必选项。仅用于周视图,weekNum需要使用@Link。月视图没有实际作用。
  6.     onCalendarClick: (item: CalendarData) => {
  7.       // 可选项。日期点击监听
  8.       hilog.info(0x0000, TAG, `day: ${JSON.stringify(item)}`);
  9.     },
  10.     CalendarStyle: {
  11.       textScaling: TEXT_SCALE, // 可选项。公历,农历文字缩放比例
  12.     },
  13.     ColorType: {
  14.       backgroundColor: Color.Transparent, // 可选项。日历背景色
  15.       monthDayColor: Color.Black, // 可选项。本月公历日期颜色
  16.       noMonthDayColor: Color.Gray, // 可选项。非本月公历日期颜色
  17.       lunarColor: Color.Gray // 可选项。农历的文字颜色
  18.     }
  19.   })
  20.   CustomCalendar({
  21.     currentSelectDay: this.currentSelectDay,
  22.     yearMonthInfo: this.currentYearMonth,
  23.     weekNum: this.weekNumDefault,
  24.     onCalendarClick: (item: CalendarData) => {
  25.       hilog.info(0x0000, TAG, `day: ${JSON.stringify(item)}`);
  26.     },
  27.     CalendarStyle: {
  28.       textScaling: TEXT_SCALE,
  29.     },
  30.     ColorType: {
  31.       backgroundColor: Color.Transparent,
  32.       monthDayColor: Color.Black,
  33.       noMonthDayColor: Color.Gray,
  34.       lunarColor: Color.Gray
  35.     }
  36.   })
  37.   CustomCalendar({
  38.     currentSelectDay: this.currentSelectDay,
  39.     yearMonthInfo: this.nextYearMonth,
  40.     weekNum: this.weekNumDefault,
  41.     onCalendarClick: (item: CalendarData) => {
  42.       hilog.info(0x0000, TAG, `day: ${JSON.stringify(item)}`);
  43.     },
  44.     CalendarStyle: {
  45.       textScaling: TEXT_SCALE,
  46.     },
  47.     ColorType: {
  48.       backgroundColor: Color.Transparent,
  49.       monthDayColor: Color.Black,
  50.       noMonthDayColor: Color.Gray,
  51.       lunarColor: Color.Gray
  52.     }
  53.   })
  54. }
复制代码

  • 月视图和周视图都是根据Swiper的onChange变乱(当前显示的子组件索引变革)举行月份或周的切换。以月视图为例,通过oldMonthViewIndex存储上一次的Swiper索引值,然后跟本次切换的索引举行比力,来辨认月份是左滑还是右滑。然后根据当前切换后的索引值去刷新所需的月份。例如,假设swiper索引0(7月),swiper索引1(8月),swiper索引2(9月),当前Swiper显示的索引为1。当Swiper右滑从索引1(8月)切换到索引0(7月)时,需要把Swiper里索引2(9月)的月份更新为6月的数据。周视图的onChange也是类似处理逻辑,这里不再赘述。
  1. .onChange((index: number) => {
  2.   if (this.oldMonthViewIndex === index) {
  3.     return;
  4.   }
  5.   // 判断是否右滑切换月份
  6.   const IS_RIGHT_SLIDE: boolean =
  7.     (this.oldMonthViewIndex === 1 && index === 0) || (this.oldMonthViewIndex === 0 && index === 2) ||
  8.       (this.oldMonthViewIndex === 2 && index === 1);
  9.   this.oldMonthViewIndex = index;
  10.   // 右滑切换月份
  11.   if (IS_RIGHT_SLIDE) {
  12.     if (index === 0) {
  13.       // swiper索引右滑到0时,修改swiper索引2的月份为当前月份(索引0)的上一个月。比如,假设swiper索引0(7月),swiper索引1(8月),swiper索引2(9月)。当左滑切换到索引0(7月)时,需要把索引2(9月)的月份改成6月。
  14.       this.setMonthViewLeftSlide();
  15.       // 修改swiper索引2的月份为当前月份(索引0)的上一个月
  16.       this.nextYearMonth.year = this.getLastYear();
  17.       this.nextYearMonth.month = this.getLastMonth();
  18.     } else if (index === 1) {
  19.       // swiper索引右滑到1时,修改swiper索引0的月份为当前月份(索引1)的上一个月。
  20.       this.setMonthViewLeftSlide();
  21.       this.lastYearMonth.year = this.getLastYear();
  22.       this.lastYearMonth.month = this.getLastMonth();
  23.     } else if (index === 2) {
  24.       // swiper索引右滑到2时,修改swiper索引1的月份为当前月份(索引2)的上一个月。
  25.       this.setMonthViewLeftSlide();
  26.       this.currentYearMonth.year = this.getLastYear();
  27.       this.currentYearMonth.month = this.getLastMonth();
  28.     }
  29.   } else {
  30.     // 左滑切换月份
  31.     if (index === 0) {
  32.       // swiper索引左滑到0时,修改swiper索引1的月份为当前月份(索引0)的下一个月。
  33.       this.setMonthViewRightSlide();
  34.       this.currentYearMonth.year = this.getNextYear();
  35.       this.currentYearMonth.month = this.getNextMonth();
  36.     } else if (index === 1) {
  37.       // swiper索引左滑到1时,修改swiper索引2的月份为当前月份(索引1)的下一个月。
  38.       this.setMonthViewRightSlide();
  39.       this.nextYearMonth.year = this.getNextYear();
  40.       this.nextYearMonth.month = this.getNextMonth();
  41.     } else if (index === 2) {
  42.       // swiper索引左滑到2时,修改swiper索引0的月份为当前月份(索引2)的下一个月。
  43.       this.setMonthViewRightSlide();
  44.       this.lastYearMonth.year = this.getNextYear();
  45.       this.lastYearMonth.month = this.getNextMonth();
  46.     }
  47.   }
  48. })
复制代码

  • 月视图和周视图之间的切换通过changeIndex控制Tabs切换到指定月视图或周视图的页签。从周视图切换到月视图时,月视图需要刷新的月份数据根据目前选中的日期currentSelectDay中的年代信息设置CustomCalendar的yearMonthInfo,然后通过触发yearMonthInfo的updateMonthViewData监听举行月份数据刷新(getMonthViewData)。从月视图切换到周视图时,周视图需要刷新的周数据,也是根据目前选中的日期currentSelectDay中的年代日信息。通过盘算选中日期到本日相差几周,来盘算需要传入CustomCalendar的weekNum,触发updateWeekData监听,举行周数据刷新(getWeekViewData)。
  1. if (this.tabSelectedIndexes[0] === 0) {
  2.   // 切到月视图
  3.   this.tabController.changeIndex(0);
  4.   // 月视图swiper索引重置为1
  5.   this.swiperMonthIndex = 1;
  6.   this.oldMonthViewIndex = 1;
  7.   // 刷新年月数据
  8.   this.currentShowYear = this.currentSelectDay.year;
  9.   this.currentShowMonth = this.currentSelectDay.month;
  10.   this.currentYearMonth.year = this.currentSelectDay.year;
  11.   // month设置-1是为了在周视图选择过日期后,切换回月视图时触发月视图的刷新
  12.   this.currentYearMonth.month = -1;
  13.   this.currentYearMonth.month = this.currentSelectDay.month;
  14.   this.lastYearMonth.year =
  15.     (this.currentYearMonth.month - 1 < 1) ? this.currentYearMonth.year - 1 : this.currentYearMonth.year;
  16.   this.lastYearMonth.month = (this.currentYearMonth.month - 1 < 1) ? 12 : this.currentYearMonth.month - 1;
  17.   this.nextYearMonth.year =
  18.     (this.currentYearMonth.month + 1 > 12) ? this.currentYearMonth.year + 1 : this.currentYearMonth.year;
  19.   this.nextYearMonth.month = (this.currentYearMonth.month + 1 > 12) ? 1 : this.currentYearMonth.month + 1;
  20. } else if (this.tabSelectedIndexes[0] === 1) {
  21.   // 切到周视图
  22.   this.tabController.changeIndex(1);
  23.   // 周视图swiper索引重置为1
  24.   this.swiperWeekIndex = 1;
  25.   const SELECT_DAY =
  26.     new Date(this.currentSelectDay.year, this.currentSelectDay.month - 1, this.currentSelectDay.date);
  27.   // 计算目前选中的日期selectDay距离今天相差的周数
  28.   const WEEKS_BETWEEN = this.weeksBetweenDates(SELECT_DAY);
  29.   this.weekNumOne = WEEKS_BETWEEN - 1; // 设置上一周
  30.   this.weekNumTwo = WEEKS_BETWEEN; // 设置本周
  31.   this.weekNumThree = WEEKS_BETWEEN + 1; // 设置下一周
  32. }
复制代码
高性能知识点

本示例中月视图左右滑动切换日历月份时,只更新一个月数据。周视图每次切换周时,只更新一周的数据,以优化日历左右切换时的性能。
以下是使用DevEco Studio内置的Profiler中的帧率分析工具Frame抓取本案例性能的相干数据(性能耗时数据因设备版本而异,以实测为准):

  • 响应时延。在cases工程案例列表中找到本案例模块,使用Frame抓取从点击案例模块到跳转进入案例页面绘制第一帧的耗时。如下图所示,可以看出点击响应时延为13.1ms。



  • 完成时延。在cases工程案例列表中找到本案例模块,使用Frame抓取从点击案例模块到跳转案例页面的转场动画竣事的耗时,如下图所示,可以看出完成时延为791.9ms。



  • 月视图左右滑动切换月份的帧率,如下图所示,可以看出月视图左右滑动切换月份的帧率为118.0fps。通过RenderFrame(实验GPU绘制)标签可以看出,滑动切换月份过程的平均渲染耗时为3.969ms。



  • 周视图左右滑动切换周的帧率,如下图所示,可以看出周视图左右滑动切换周的帧率为119.9fps。通过RenderFrame(实验GPU绘制)标签可以看出,滑动切换周过程的平均渲染耗时为4.107ms。


工程结构&模块类型

  1. calendarswitch                               // har类型
  2. |---constant
  3. |   |---Constants.ets                        // 常量定义
  4. |---model
  5. |   |---CalendarModel.ets                    // 日历配置
  6. |---utils
  7. |   |---TimeUtils.ets                        // 日历工具类
  8. |---view
  9. |   |---CalendarSwitch.ets                   // 日历切换页面
  10. |   |---CustomCalendar.ets                   // 自定义日历组件
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

天空闲话

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表