​背景

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

实现效果

图片

实现思路

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

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
}

})
}

图片

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 })

}
}

图片

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)

}
}

图片

 总结

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


奥尼ᴮᴵᴹᵉʳ
1 声望1 粉丝