#HarmonyOS NEXT体验官# 封装时间选择按钮 原创

奥尼5354
发布于 2025-3-10 22:59
780浏览
0收藏


背景

由于原生的时间选择器(DatePicker)不能满足UI的设计要求,比如:会自带分割线;无法调节各选择项高度等限制,虽然也无法理解TextPicker已有的属性,DatePicker上竟然没有。因此,想着使用三个TextPicker组合成一个时间选择器,后期还可以做更多的设置,可以只选择月份和日期等。

实现效果

#HarmonyOS NEXT体验官#  封装时间选择按钮-鸿蒙开发者社区0900086000300134184.20201216095126.86523331460016843504112994983392.png

实现思路

  • 封装时间滑动组件,实现月份变化时,结合年份动态改变日期集合
  • 封装按钮控件,添加上点击下翻的动画和整体控件高度的变化

MyTimePicker

提供了开始时间、结束时间、选择日期属性可以给父级构件进行调用。为了统一三个TextPicker样式,封装了MyPicker公用样式,后续需要统一修改他们的样式可以直接在这里面添加。

@ComponentV2
export struct MyTimePicker {
  @Param Start: Date = new Date('1970-1-1')
  @Param End: Date = new Date('2100-12-31')
  @Param SelectDate: Date = new Date()
  @Local Years: number[] = []
  @Local Months: number[] = []
  @Local Days: number[] = []
  @Local YearSelectIndex: number = 0
  @Local MonthSelectIndex: number = 0
  @Local DaySelectIndex: number = 0

  aboutToAppear(): void {
    this.Years =
      Array.from<number, number>({ length: this.End.getFullYear() - this.Start.getFullYear() + 1 },
        (_, k) => this.Start.getFullYear() + k)
    this.Months = Array.from<number, number>({ length: 12 }, (_, k) => k + 1)
    this.UpdateDaysInMonth(this.SelectDate.getFullYear(), this.SelectDate.getMonth() + 1);
    this.SelectIndexInit();
  }

  build() {
    Row() {
      // 年份选择
      TextPicker({ range: this.Years.map(x => `${x}年`), selected: this.YearSelectIndex })
        .onChange((value, index) => {
          const newYear = this.Years[index as number]
          this.SelectDate.setFullYear(newYear)
          this.UpdateDaysInMonth(newYear, this.SelectDate.getMonth() + 1)
        })
        .MyPicker()

      // 月份选择
      TextPicker({ range: this.Months.map(v => `${v}月`), selected: this.MonthSelectIndex })
        .onChange((value, index) => {
          if (index as number || index == 0) {
            const newMonth = index as number + 1
            this.SelectDate.setMonth(newMonth - 1)
            this.UpdateDaysInMonth(this.SelectDate.getFullYear(), newMonth)
          }
        })
        .MyPicker()

      // 日期选择
      TextPicker({ range: this.Days.map(x => `${x}日`), selected: this.DaySelectIndex })
        .onChange((value, index) => {
          console.info(index.toString())
          this.SelectDate.setDate(index as number + 1)
        })
        .MyPicker()
    }
    .height('100%')
    .width('100%')
  }

  /**
   * 选择索引初始化
   */
  private SelectIndexInit() {
    let yearIndex: number = this.Years.findIndex((value: number) => {
      return this.SelectDate.getFullYear() == value
    });
    let monthIndex: number = this.Months.findIndex((value: number) => {
      return this.SelectDate.getMonth() + 1 == value
    });
    let dayIndex: number = this.Days.findIndex((value: number) => {
      return this.SelectDate.getDate() == value
    });
    this.YearSelectIndex = yearIndex;
    this.MonthSelectIndex = monthIndex;
    this.DaySelectIndex = dayIndex;
  }

  private UpdateDaysInMonth(year: number, month: number) {
    const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    if (month === 2 && this.IsLeapYear(year)) {
      this.Days = Array.from<number, number>({ length: 29 }, (_, i) => i + 1); // 闰年2月有29天
    } else {
      this.Days = Array.from<number, number>({ length: daysInMonth[month - 1] }, (_, i) => i + 1);
    }
    let dayIndex: number = this.Days.findIndex((value: number) => {
      return this.SelectDate.getDate() == value
    });
    this.DaySelectIndex = dayIndex;
  }

  /**
   * 判断是否是闰年
   * @param year
   * @returns
   */
  private IsLeapYear(year: number): boolean {
    return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
  }
}

@Extend(TextPicker)
function MyPicker() {
  .divider(null)
  .layoutWeight(1)
  .selectedTextStyle({
    color: Color.Black,
    font: {
      weight: FontWeight.Bold
    }
  })
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.

#HarmonyOS NEXT体验官#  封装时间选择按钮-鸿蒙开发者社区

DatePickButton

为了实现组件的复用,把标题也封装进来。这里需要注意的点是,这里使用了dayjs第三方库,需要提前引用第三方库哈。

import dayjs from "dayjs"
import { MyTimePicker } from "./MyTimePicker"

@ComponentV2
export struct DatePickButton {
  @Param Title: string = "开始时间:"
  @Param SelectDate: Date = new Date()
  @Local IsPickerShow: boolean = false

  build() {
    RelativeContainer() {
      Text(this.Title)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .id("Title")
      Button() {
        Row() {
          Text(dayjs(this.SelectDate).format("YYYY 年 MM 月 DD 日 "))
            .fontSize(18)
          Path()
            .width(30)
            .height(30)
            .commands(`M${vp2px(7.5)} ${vp2px(10)} L${vp2px(15)} ${vp2px(20)} L${vp2px(22.5)} ${vp2px(10)} Z`)
            .rotate(this.IsPickerShow ? {
              centerX: "50%",
              centerY: "50%",
              angle: 180
            } : {
              angle: 0
            })
        }
        .alignItems(VerticalAlign.Center)
        .justifyContent(FlexAlign.SpaceBetween)
      }
      .border({
        color: Color.Black,
        width: 2,
        radius: 15
      })
      .backgroundColor(Color.White)
      .type(ButtonType.Normal)
      .height(40)
      .margin({ left: 5 })
      .padding({ left: 15, right: 15 })
      .alignRules({
        left: { anchor: "Title", align: HorizontalAlign.End },
        center: { anchor: "Title", align: VerticalAlign.Center }
      })
      .onClick(() => {
        animateTo({ duration: 100 }, () => {
          this.IsPickerShow = !this.IsPickerShow;
        })
      })
      .id("PickerBtn")

      MyTimePicker({
        SelectDate: this.SelectDate
      })
        .height(this.IsPickerShow ? 150 : 0)
        .margin({ top: 10 })
        .alignRules({
          top: { anchor: "PickerBtn", align: VerticalAlign.Bottom },
          left: { anchor: "Title", align: HorizontalAlign.Start },
          right: { anchor: "PickerBtn", align: HorizontalAlign.End }
        })
        .id("DatePicker")

      Rect()
        .width("100%")
        .height(this.IsPickerShow ? 35 : 0)
        .radiusWidth(20)
        .fill("#56FFEB")
        .fillOpacity(0.5)
        .stroke(Color.Black)
        .strokeWidth(2)
        .alignRules({
          middle: { anchor: "DatePicker", align: HorizontalAlign.Center },
          center: { anchor: "DatePicker", align: VerticalAlign.Center },
        })
    }
    .height(this.IsPickerShow ? 200 : 50)
    .width("100%")
    .padding({ left: 15, right: 15 })
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.

#HarmonyOS NEXT体验官#  封装时间选择按钮-鸿蒙开发者社区

Index

在主页面中使用时间选择组件

import { DatePickButton } from './DatePickButton'


@Entry
@ComponentV2
struct Index {
  @Local StartDate: Date = new Date()
  @Local EndDate: Date = new Date()

  build() {
    Column({ space: 5 }) {
      DatePickButton({
        Title: "开始时间:",
        SelectDate: this.StartDate
      })
      DatePickButton({
        Title: "结束时间:",
        SelectDate: this.EndDate
      })
    }
    .padding({ top: 20 })
    .height('100%')
    .width('100%')
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Start)
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.

#HarmonyOS NEXT体验官#  封装时间选择按钮-鸿蒙开发者社区

 总结

主要实现了UI需要的没有分割线的时间选择器,而且可以有更多自定义空间,但是也会受限于TextPicker的能力范围,但是现阶段基本能满足使用需求了。希望这篇文章可以帮到你~~


©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报


回复
    相关推荐