鸿蒙任务项设置案例实战

news2025/6/7 4:10:04

目录

案例效果

资源文件与初始化

string.json

color.json

CommonConstant

添加任务

首页组件

任务列表初始化

任务列表视图

任务编辑页

添加跳转

任务目标设置模型(formatParams)

编辑页面

详情页

任务编辑列表项

目标设置展示

引入目标设置

目标设置展示实现

TaskInfo 枚举模型设置

弹窗构造逻辑

定义单击事件

定义 BroadCast

定义弹窗视图

定义弹窗 builder 组件

目标设置弹窗实现

目标设置窗口逻辑

任务目标设置视图模型

频率设置视图模型

时间提醒弹窗实现

更新TaskDetail

实现编辑任务列表的开启提醒与提醒时间

实现时间提醒弹窗

频率弹窗和提交完成的实现

实现频率任务项视图

实现频率设置弹窗

定义频率设置视图模型


案例效果

资源文件与初始化

string.json

{
  "string": [
    {
      "name": "entry_desc",
      "value": "description"
    },
    {
      "name": "entryAbility_desc",
      "value": "description"
    },
    {
      "name": "entryAbility_label",
      "value": "List_HDC"
    },
    {
      "name": "task_morning",
      "value": "早起"
    },
    {
      "name": "task_water",
      "value": "喝水"
    },
    {
      "name": "task_apple",
      "value": "吃苹果"
    },
    {
      "name": "task_smile",
      "value": "每日微笑"
    },
    {
      "name": "task_brush",
      "value": "每日刷牙"
    },
    {
      "name": "task_night",
      "value": "早睡"
    },
    {
      "name": "already_open",
      "value": "已开启"
    },
    {
      "name": "complete",
      "value": "完成"
    },
    {
      "name": "frequency",
      "value": "频率"
    },
    {
      "name": "remind_time",
      "value": "提醒时间"
    },
    {
      "name": "open_reminder",
      "value": "开启提醒"
    },
    {
      "name": "target_setting",
      "value": "目标设置"
    },
    {
      "name": "cancel",
      "value": "取消"
    },
    {
      "name": "confirm",
      "value": "确认"
    },
    {
      "name": "set_your_frequency",
      "value": "请设置您的频率"
    }
  ]
}

color.json

{
  "color": [
    {
      "name": "white",
      "value": "#FFFFFF"
    },
    {
      "name": "primaryBgColor",
      "value": "#F1F3F5"
    },
    {
      "name": "titleColor",
      "value": "#182431"
    },
    {
      "name": "btnBgColor",
      "value": "#F2F2F2"
    },
    {
      "name": "statusTipColor",
      "value": "#989A9C"
    },
    {
      "name": "blueColor",
      "value": "#007DFF"
    },
    {
      "name": "black",
      "value": "#000000"
    },
    {
      "name": "primaryRed",
      "value": "#E92F4F"
    },
    {
      "name": "tabTitleColor",
      "value": "#999"
    },
    {
      "name": "signatureColor",
      "value": "#66686a"
    },
    {
      "name": "leveColor",
      "value": "#c99411"
    },
    {
      "name": "leveBgColor",
      "value": "#d4e6f1"
    },
    {
      "name": "borderColor",
      "value": "#cccccc"
    },
    {
      "name": "mineBgColor",
      "value": "#edf2f5"
    },
    {
      "name": "launcherBlueColor",
      "value": "#4694C2"
    },
    {
      "name": "disabledColor",
      "value": "#dddadc"
    }
  ]
}

CommonConstant

// ets/common/contants/CommonConstant.ets

export const THOUSANDTH_80: string = '8%'
export const THOUSANDTH_100: string = '10%'
export const THOUSANDTH_400: string = '40%'
export const THOUSANDTH_500: string = '50%'
export const THOUSANDTH_560: string = '56%'
export const THOUSANDTH_800: string = '80%'
export const THOUSANDTH_900: string = '90%'
export const THOUSANDTH_940: string = '94%'
export const THOUSANDTH_1000: string = '100%'

export const DEFAULT_2: number = 2
export const DEFAULT_8: number = 8
export const DEFAULT_12: number = 12
export const DEFAULT_10: number = 10
export const DEFAULT_16: number = 16
export const DEFAULT_18: number = 18
export const DEFAULT_20: number = 20
export const DEFAULT_24: number = 24
export const DEFAULT_28: number = 28
export const DEFAULT_32: number = 32
export const DEFAULT_48: number = 48
export const DEFAULT_56: number = 56
export const DEFAULT_60: number = 60

export const LIST_ITEM_SPACE: number = 2

export const ADD_TASK_TITLE: string = '添加任务'
export const EDIT_TASK_TITLE: string = '编辑任务'

export const SETTING_FINISHED_MESSAGE = '设置完成!!!'
export const CHOOSE_TIME_OUT_RANGE: string = '选择时间超出范围'

export const TODAY: string = new Date().toDateString()

export const DEFAULT_TIME: string = '08:00'
export const GET_UP_TIME_RANGE: string = '(06:00 - 09:00)'
export const SLEEP_TIME_RANGE: string = '(20:00 - 23:00)'
export const GET_UP_EARLY_TIME: string = '06:00'
export const GET_UP_LATE_TIME: string = '09:00'
export const SLEEP_EARLY_TIME: string = '20:00'
export const SLEEP_LATE_TIME: string = '23:00'
export const DEFAULT_SELECTED_TIME: Date = new Date(`${TODAY} 8:00:00`)

export const EVERYDAY: string = '每天'
export const NO_LENGTH: number = 0
export const INIT_WEEK_IDS: string = '1, 2, 3, 4, 5, 6, 7'

export const PER_DAY: string = '/ 天'

export const ZERO: number = 0
export const MINUS_20: number = -20
export const HAS_NO_INDEX: number = -1

export const DRINK_STEP: number = 25
export const DRINK_MAX_RANGE: number = 500
export const TIMES_100: number = 100
export const EAT_APPLE_RANGE: number = 100
export const DEFAULT_TEXT: string = '0.25'
export const DEFAULT_APPLE: string = '1'

添加任务

首页组件

// ets/pages/Index.ets

import TaskList from '../view/TaskList'
import { TaskListItem, TaskInitList } from '../model/TaskInitList'
import { THOUSANDTH_1000, ADD_TASK_TITLE } from '../common/constants/CommonConstant'

@Entry
@Component
struct Index {
  @Provide taskList: TaskListItem[] = TaskInitList

  build() {
    Row() {
      Navigation() {
        Column() {
          TaskList()
        }
        .width(THOUSANDTH_1000)
        .justifyContent(FlexAlign.Center)
      }
      .size({ width: THOUSANDTH_1000, height: THOUSANDTH_1000 })
      .title(ADD_TASK_TITLE)
      .titleMode(NavigationTitleMode.Mini)
    }
    .backgroundColor($r('app.color.primaryBgColor'))
    .height(THOUSANDTH_1000)
  }
}

任务列表初始化

// ets/model/TaskInitList.ets

export interface TaskListItem {
  taskID: number
  taskName: Resource
  isOpen: boolean
  unit: string
  icon: Resource
  targetValue: string
  isAlarm: boolean
  startTime: string
  frequency: string
}

export interface FrequencyContentType {
  id: number,
  label: string,
  isChecked: boolean
}

export const TaskInitList: TaskListItem[] = [
  { // Get up early.
    taskID: 1,
    taskName: $r('app.string.task_morning'),
    icon: $r('app.media.morning'),
    targetValue: '08: 00',
    isOpen: true,
    unit: '',
    isAlarm: false,
    startTime: '08: 00',
    frequency: '1, 2, 3, 4, 5, 6, 7'
  },
  { // Drink water.
    taskID: 2,
    taskName: $r('app.string.task_water'),
    icon: $r('app.media.water'),
    targetValue: '0.25',
    isOpen: true,
    unit: 'L',
    isAlarm: false,
    startTime: '08: 00',
    frequency: '1, 2, 3, 4, 5, 6, 7'
  },
  { // Eat apples.
    taskID: 3,
    taskName: $r('app.string.task_apple'),
    icon: $r('app.media.apple'),
    targetValue: '1',
    isOpen: true,
    unit: '个',
    isAlarm: false,
    startTime: '08: 00',
    frequency: '1, 2, 3, 4, 5, 6, 7'
  },
  { // Smile every day.
    taskID: 4,
    taskName: $r('app.string.task_smile'),
    icon: $r('app.media.smile'),
    targetValue: '1',
    isOpen: false,
    unit: '次',
    isAlarm: false,
    startTime: '08: 00',
    frequency: '1, 2, 3, 4, 5, 6, 7'
  },
  { // Clean one’s teeth.
    taskID: 5,
    taskName: $r('app.string.task_brush'),
    icon: $r('app.media.brush'),
    targetValue: '1',
    isOpen: false,
    unit: '次',
    isAlarm: false,
    startTime: '08: 00',
    frequency: '1, 2, 3, 4, 5, 6, 7'
  },
  { // Go to bed early.
    taskID: 6,
    taskName: $r('app.string.task_night'),
    icon: $r('app.media.night'),
    targetValue: '20: 00',
    isOpen: false,
    unit: '',
    isAlarm: false,
    startTime: '08: 00',
    frequency: '1, 2, 3, 4, 5, 6, 7'
  }
]

任务列表视图

// ets/view/TaskList.ets

import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'

@Component
export default struct TaskList {
  @Consume taskList: TaskListItem[]

  build() {
    List({ space: commonConst.LIST_ITEM_SPACE }) {
      ForEach(this.taskList, (item: TaskListItem) => {
        ListItem() {
          Row() {
            Row() {
              Image(item?.icon)
                .width(commonConst.DEFAULT_24)
                .height(commonConst.DEFAULT_24)
                .margin({ right: commonConst.DEFAULT_8 })
              Text(item?.taskName)
                .fontSize(commonConst.DEFAULT_20)
                .fontColor($r('app.color.titleColor'))
            }
            .width(commonConst.THOUSANDTH_500)

            Blank()
              .layoutWeight(1)

            if (item?.isOpen) {
              Text($r('app.string.already_open'))
                .fontSize(commonConst.DEFAULT_16)
                .flexGrow(1)
                .align(Alignment.End)
                .margin({ right: commonConst.DEFAULT_8 })
                .fontColor($r('app.color.titleColor'))
            }

            Image($r('app.media.right_grey'))
              .width(commonConst.DEFAULT_8)
              .height(commonConst.DEFAULT_16)
          }
          .width(commonConst.THOUSANDTH_1000)
          .justifyContent(FlexAlign.SpaceBetween)
          .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
        }
        .height(commonConst.THOUSANDTH_80)
        .borderRadius(commonConst.DEFAULT_12)
        .backgroundColor($r('app.color.white'))
      })
    }
    .height(commonConst.THOUSANDTH_1000)
    .width(commonConst.THOUSANDTH_940)
  }
}

任务编辑页

添加跳转

// ets/view/TaskList.ets

import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import { router } from '@kit.ArkUI'
import { formatParams } from '../viewModel/TaskTargetSetting'

@Component
export default struct TaskList {
  @Consume taskList: TaskListItem[]

  build() {
    List({ space: commonConst.LIST_ITEM_SPACE }) {
      ForEach(this.taskList, (item: TaskListItem) => {
        ListItem() {
          Row() {
            Row() {
              Image(item?.icon)
                .width(commonConst.DEFAULT_24)
                .height(commonConst.DEFAULT_24)
                .margin({ right: commonConst.DEFAULT_8 })
              Text(item?.taskName)
                .fontSize(commonConst.DEFAULT_20)
                .fontColor($r('app.color.titleColor'))
            }
            .width(commonConst.THOUSANDTH_500)

            Blank()
              .layoutWeight(1)

            if (item?.isOpen) {
              Text($r('app.string.already_open'))
                .fontSize(commonConst.DEFAULT_16)
                .flexGrow(1)
                .align(Alignment.End)
                .margin({ right: commonConst.DEFAULT_8 })
                .fontColor($r('app.color.titleColor'))
            }

            Image($r('app.media.right_grey'))
              .width(commonConst.DEFAULT_8)
              .height(commonConst.DEFAULT_16)
          }
          .width(commonConst.THOUSANDTH_1000)
          .justifyContent(FlexAlign.SpaceBetween)
          .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
        }
        .height(commonConst.THOUSANDTH_80)
        .borderRadius(commonConst.DEFAULT_12)
        .backgroundColor($r('app.color.white'))
        
        // 1. 添加链接
        .onClick(() => {
          router.pushUrl({
            url: 'pages/TaskEditPage',
            params: {
              params: formatParams(item)
            }
          })
          
        })
      })
    }
    .height(commonConst.THOUSANDTH_1000)
    .width(commonConst.THOUSANDTH_940)
  }
}

任务目标设置模型(formatParams)

// ets/viewModel/TaskTargetSetting

import { TaskListItem } from '../model/TaskInitList'
export const formatParams = (params: TaskListItem) => {
  return JSON.stringify(params)
}

编辑页面

// ets/pages/TaskEditPage.ets

import { THOUSANDTH_1000, EDIT_TASK_TITLE } from '../common/constants/CommonConstant'
import TaskDetail from '../view/TaskDetail'

@Entry
@Component
struct TaskEdit {
  build() {
    Row() {
      Navigation() {
        Column() {
          TaskDetail()
        }
        .width(THOUSANDTH_1000)
        .height(THOUSANDTH_1000)
      }
      .size({ width: THOUSANDTH_1000, height: THOUSANDTH_1000 })
      .title(EDIT_TASK_TITLE)
      .titleMode(NavigationTitleMode.Mini)
    }
    .height(THOUSANDTH_1000)
    .backgroundColor($r('app.color.primaryBgColor'))
  }
}

详情页

// ets/view/TaskDetail.ets

import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import {
  TaskChooseItem
} from './TaskEditListItem'
import { router } from '@kit.ArkUI'

@Styles
function listItemStyle() {
  .backgroundColor($r('app.color.white'))
  .height(commonConst.DEFAULT_56)
  .borderRadius(commonConst.DEFAULT_10)
  .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}

@Component
export default struct TaskDetail {
  @Provide settingParams: TaskListItem = this.parseRouterParams()

  parseRouterParams() {
    let params = router.getParams() as Record<string, Object>
    const routerParams: TaskListItem = JSON.parse(params.params as string)
    return routerParams
  }

  build() {
    Column() {
      List({ space: commonConst.LIST_ITEM_SPACE }) {
        ListItem() {
          TaskChooseItem()
        }
        .listItemStyle()
      }
    }
    .width(commonConst.THOUSANDTH_1000)
  }
}

任务编辑列表项

// ets/view/TaskEditListItem.ets

import { TaskListItem } from '../model/TaskInitList'
import {
  DEFAULT_20,
  DEFAULT_32,
  DEFAULT_56,
  THOUSANDTH_1000,
} from '../common/constants/CommonConstant'

@Component
export struct TaskChooseItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text(this.settingParams.taskName).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
      Toggle({ type: ToggleType.Switch, isOn: this.settingParams.isOpen })
        .width(DEFAULT_56)
        .height(DEFAULT_32)
        .selectedColor($r('app.color.blueColor'))
        .onChange((isOn: boolean) => {
          this.settingParams.isOpen = isOn;
        })
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

目标设置展示

引入目标设置

// ets/view/TaskDetail.ets

import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import {
  TaskChooseItem,
  // 2. 引入TargetSetItem
  TargetSetItem
} from './TaskEditListItem'
import { router } from '@kit.ArkUI'
import { taskType } from '../model/TaskInfo'

@Styles
function listItemStyle() {
  .backgroundColor($r('app.color.white'))
  .height(commonConst.DEFAULT_56)
  .borderRadius(commonConst.DEFAULT_10)
  .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}

@Component
export default struct TaskDetail {
  @Provide settingParams: TaskListItem = this.parseRouterParams()

  parseRouterParams() {
    let params = router.getParams() as Record<string, Object>
    const routerParams: TaskListItem = JSON.parse(params.params as string)
    return routerParams
  }

  build() {
    Column() {
      List({ space: commonConst.LIST_ITEM_SPACE }) {
        ListItem() {
          TaskChooseItem()
        }
        .listItemStyle()

        // 1. 目标设置入口
        ListItem() {
          TargetSetItem()
        }
        .listItemStyle()
        .enabled(
          this.settingParams?.isOpen &&
            (this.settingParams?.taskID !== taskType.smile) &&
            (this.settingParams?.taskID !== taskType.brushTeeth)
        )
      }
      .width(commonConst.THOUSANDTH_940)
    }
    .width(commonConst.THOUSANDTH_1000)
  }
}

目标设置展示实现

// ets/view/TaskEditListItem.ets

import { TaskListItem } from '../model/TaskInitList'
import {
  DEFAULT_16,
  DEFAULT_20,
  DEFAULT_32,
  DEFAULT_56,
  DEFAULT_8,
  PER_DAY,
  THOUSANDTH_1000,
} from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'

// 2.定义公共样式targetSetCommon
@Extend(Text)
function targetSetCommon() {
  .fontSize(DEFAULT_16)
  .flexGrow(1)
  .margin({ right: DEFAULT_8 })
  .align(Alignment.End)
}

// 2.定义公共样式targetSettingStyle
@Extend(Text)
function targetSettingStyle(isOpen: boolean, taskID: number) {
  .fontColor(isOpen && taskID !== taskType.smile && taskID !== taskType.brushTeeth ?
  $r('app.color.titleColor') :
  $r('app.color.disabledColor'))
}

@Component
export struct TaskChooseItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text(this.settingParams.taskName).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
      Toggle({ type: ToggleType.Switch, isOn: this.settingParams.isOpen })
        .width(DEFAULT_56)
        .height(DEFAULT_32)
        .selectedColor($r('app.color.blueColor'))
        .onChange((isOn: boolean) => {
          this.settingParams.isOpen = isOn;
        })
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

// 1. 定义TargetSetItem组件
@Component
export struct TargetSetItem {
  @Consume settingParams: TaskListItem;

  build() {
    Row() {
      Text($r('app.string.target_setting'))
        .fontSize(DEFAULT_20)
        .fontWeight(FontWeight.Medium)

      Blank()
        .layoutWeight(1)

      if (this.settingParams?.unit === '') {
        Text(`${this.settingParams?.targetValue}`)
          .targetSetCommon()
          .targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
      } else {
        Text(`${this.settingParams?.targetValue} ${this.settingParams?.unit} ${PER_DAY}`)
          .targetSetCommon()
          .targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
      }
      Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16);
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

TaskInfo 枚举模型设置

// ets/model/TaskInfo.ets

export enum taskType {
  'getup' = 1,
  'drinkWater',
  'eatApple',
  'smile',
  'brushTeeth',
  'sleepEarly'
}

弹窗构造逻辑

定义单击事件

import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import {
  TaskChooseItem,
  TargetSetItem
} from './TaskEditListItem'
import { router } from '@kit.ArkUI'
import { taskType } from '../model/TaskInfo'

// 4. 引入BroadCast(先去创建)
import { BroadCast, BroadCastType } from '../common/utils/BroadCast'

import { CustomDialogView } from './CustomDialogView'

@Styles
function listItemStyle() {
  .backgroundColor($r('app.color.white'))
  .height(commonConst.DEFAULT_56)
  .borderRadius(commonConst.DEFAULT_10)
  .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}

@Component
export default struct TaskDetail {
  @Provide settingParams: TaskListItem = this.parseRouterParams()

  // 5. 提供 broadCast
  @Provide broadCast: BroadCast = new BroadCast()

  parseRouterParams() {
    let params = router.getParams() as Record<string, Object>
    const routerParams: TaskListItem = JSON.parse(params.params as string)
    return routerParams
  }

  // 7. 先去定义弹窗和builder,注册(on)完,这里解绑
  aboutToAppear() {
    this.broadCast.off()
  }

  build() {
    Column() {
      List({ space: commonConst.LIST_ITEM_SPACE }) {
        ListItem() {
          TaskChooseItem()
        }
        .listItemStyle()

        ListItem() {
          TargetSetItem()
        }
        .listItemStyle()
          
        // 3. 设置 smile & brushTeeth 不可单击
        .enabled(
          this.settingParams?.isOpen &&
            (this.settingParams?.taskID !== taskType.smile) &&
            (this.settingParams?.taskID !== taskType.brushTeeth)
        )
        // 1. 定义单击事件
        .onClick(() => {
          // 2. 测试单击,目的是引出第 3 步
          // console.log('test')

          // 8. 最后再触发
          this.broadCast.emit(
            BroadCastType.SHOW_TARGET_SETTING_DIALOG)
        })

        // 9. 测试提醒时间设置
        ListItem() {
          Text('提醒时间')
        }
        .listItemStyle()
        .enabled(this.settingParams?.isOpen && this.settingParams?.isAlarm)
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_REMIND_TIME_DIALOG
          )
        })

        // 9. 测试频率设置
        ListItem() {
          Text('频率')
        }
        .listItemStyle()
        .enabled(this.settingParams?.isOpen)
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_FREQUENCY_DIALOG
          )
        })
      }

      // 6.定义弹框视图(先去创建)
      CustomDialogView()
    }
    .width(commonConst.THOUSANDTH_1000)
  }
}

定义 BroadCast

// ets/common/util/BroadCast.ets

export class BroadCast {
  private callBackArray = []

  public on(event: string, callback: Function) {
    (this.callBackArray[event] || (this.callBackArray[event] = [])).push(callback)
  }

  public off() {
    this.callBackArray = []
  }

  public emit(event: string) {
    let _self = this
    if (!this.callBackArray[event]) {
      return
    }
    let cbs: Function[] = this.callBackArray[event]
    if (cbs) {
      let len = cbs.length;
      for (let i = 0; i < len; i++) {
        try {
          cbs[i](_self)
        } catch (e) {
          new Error(e)
        }
      }
    }
  }
}

export enum BroadCastType {
  SHOW_TARGET_SETTING_DIALOG = 'showTargetSettingDialog',
  SHOW_REMIND_TIME_DIALOG = 'showRemindTimeDialog',
  SHOW_FREQUENCY_DIALOG = 'showFrequencyDialog'
}

定义弹窗视图

// ets/view/CustomDialogView.ets

import { TargetSettingDialog, RemindTimeDialog, FrequencyDialog } from './TaskSettingDialog'
import { BroadCast, BroadCastType } from '../common/utils/BroadCast'
import { ZERO, MINUS_20 } from '../common/constants/CommonConstant'

@Component
export struct CustomDialogView {
  @State isShow: boolean = false
  @Provide achievementLevel: number = 3
  @Consume broadCast: BroadCast

  targetSettingDialogController: CustomDialogController = new CustomDialogController({
    builder: TargetSettingDialog(),
    autoCancel: true,
    alignment: DialogAlignment.Bottom,
    offset: { dx: ZERO, dy: MINUS_20 }
  })

  RemindTimeDialogController: CustomDialogController = new CustomDialogController({
    builder: RemindTimeDialog(),
    autoCancel: true,
    alignment: DialogAlignment.Bottom,
    offset: { dx: ZERO, dy: MINUS_20 }
  });

  FrequencyDialogController: CustomDialogController = new CustomDialogController({
    builder: FrequencyDialog(),
    autoCancel: true,
    alignment: DialogAlignment.Bottom,
    offset: { dx: ZERO, dy: MINUS_20 }
  })

  aboutToAppear() {
    let self = this

    this.broadCast.on(
      BroadCastType.SHOW_TARGET_SETTING_DIALOG,
      () => {
        self.targetSettingDialogController.open()
      })

    this.broadCast.on(
      BroadCastType.SHOW_REMIND_TIME_DIALOG,
      () => {
        self.RemindTimeDialogController.open()
      })

    this.broadCast.on(
      BroadCastType.SHOW_FREQUENCY_DIALOG,
      () => {
        self.FrequencyDialogController.open()
      })
  }

  build() {
  }
}

定义弹窗 builder 组件

// ets/view/TaskSettingDialog.ets

import * as commonConst from '../common/constants/CommonConstant'

@CustomDialog
export struct TargetSettingDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: TargetSettingDialog()
  })

  build() {
    Column() {
      Text('target setting dialog')
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

@CustomDialog
export struct RemindTimeDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: RemindTimeDialog()
  })

  build() {
    Column() {
      Text('remind time dialog')
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

@CustomDialog
export struct FrequencyDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: FrequencyDialog()
  })

  build() {
    Column() {
      Text('frequency dialog')
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

目标设置弹窗实现

目标设置窗口逻辑

// ets/view/TaskSettingDialog.ets

import * as commonConst from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'
import { TaskListItem } from '../model/TaskInitList'
import { createAppleRange, createDrinkRange, formatTime, returnTimeStamp } from '../viewModel/TaskTargetSetting'
import { promptAction } from '@kit.ArkUI'

@CustomDialog
export struct TargetSettingDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: TargetSettingDialog()
  })

  @Consume settingParams: TaskListItem

  currentTime: string = commonConst.DEFAULT_TIME
  currentValue: string = this.settingParams.taskID === taskType.drinkWater ? commonConst.DEFAULT_TEXT :
  commonConst.DEFAULT_APPLE

  drinkRange: string[] = createDrinkRange()
  appleRange: string[] = createAppleRange()

  createSubTitle() {
    if (this.settingParams.taskID === taskType.getup) {
      return commonConst.GET_UP_TIME_RANGE
    }
    if (this.settingParams.taskID === taskType.sleepEarly) {
      return commonConst.SLEEP_TIME_RANGE
    }
    return ''
  }

  setTargetValue() {
    if (this.settingParams.taskID === taskType.getup) {
      if (!this.compareTime(commonConst.GET_UP_EARLY_TIME, commonConst.GET_UP_LATE_TIME)) {
        return
      }
      this.settingParams.targetValue = this.currentTime
      return
    }

    if (this.settingParams.taskID === taskType.sleepEarly) {
      if (!this.compareTime(commonConst.SLEEP_EARLY_TIME, commonConst.SLEEP_LATE_TIME)) {
        return
      }
      this.settingParams.targetValue = this.currentTime
      return
    }
    this.settingParams.targetValue = this.currentValue
  }

  compareTime(startTime: string, endTime: string) {
    if (returnTimeStamp(this.currentTime) < returnTimeStamp(startTime) ||
      returnTimeStamp(this.currentTime) > returnTimeStamp(endTime)) {

      promptAction.showToast({
        message: commonConst.CHOOSE_TIME_OUT_RANGE
      })
      return false
    }
    return true
  }

  build() {
    Column() {
      Row() {
        Text($r('app.string.target_setting')).fontSize(commonConst.DEFAULT_20).margin({ right: commonConst.DEFAULT_12 })
        Text(this.createSubTitle())
          .fontSize(commonConst.DEFAULT_16)
      }
      .width(commonConst.THOUSANDTH_1000)
      .justifyContent(FlexAlign.Start)

      if ([taskType.getup, taskType.sleepEarly].indexOf(this.settingParams.taskID) > commonConst.HAS_NO_INDEX) {
        TimePicker({
          selected: commonConst.DEFAULT_SELECTED_TIME
        })
          .height(commonConst.THOUSANDTH_800)
          .useMilitaryTime(true)
          .onChange((value: TimePickerResult) => {
            this.currentTime = formatTime(value)
          })
      } else {
        TextPicker({ range: this.settingParams.taskID === taskType.drinkWater ? this.drinkRange : this.appleRange,
          value: this.settingParams.targetValue })
          .width(commonConst.THOUSANDTH_900)
          .height(commonConst.THOUSANDTH_800)
          .onChange((value: string | string[]) => {
            this.currentValue = (value as string)?.split(' ')[0]
          })
      }

      Row() {
        Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.currentTime = commonConst.DEFAULT_TIME
            this.currentValue = commonConst.DEFAULT_TEXT
            this.controller.close()
          })

        Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.setTargetValue()
            this.controller.close()
          })
      }
      .justifyContent(FlexAlign.SpaceAround)
      .width(commonConst.THOUSANDTH_1000)
      .height(commonConst.DEFAULT_28)
      .margin({ bottom: commonConst.DEFAULT_20 })
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

@CustomDialog
export struct RemindTimeDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: RemindTimeDialog()
  })

  build() {
    Column() {
      Text('remind time dialog')
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

@CustomDialog
export struct FrequencyDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: FrequencyDialog()
  })

  build() {
    Column() {
      Text('frequency dialog')
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

任务目标设置视图模型

// ets/TaskTargetSetting.ets

import { DRINK_MAX_RANGE, DRINK_STEP, EAT_APPLE_RANGE, TIMES_100, TODAY } from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import { padTo2Digits } from './FrequencySetting'

export const formatParams = (params: TaskListItem) => {
  return JSON.stringify(params)
}

export const formatTime = (value: TimePickerResult) => {
  let hour: number = 0
  let minute: number = 0
  if (value.hour !== undefined && value.minute !== undefined) {
    hour = value.hour
    minute = value.minute
  }
  return `${padTo2Digits(hour)}:${padTo2Digits(minute)}`
}

export const createDrinkRange = () => {
  const drinkRangeArr: string[] = []
  for (let i = DRINK_STEP; i <= DRINK_MAX_RANGE; i += DRINK_STEP) {
    drinkRangeArr.push(`${i / TIMES_100} L`)
  }
  return drinkRangeArr
}

export const createAppleRange = () => {
  const appleRangeArr: string[] = []
  for (let i = 1; i <= EAT_APPLE_RANGE; i++) {
    appleRangeArr.push(`${i} 个`)
  }
  return appleRangeArr
}

export const returnTimeStamp = (currentTime: string) => {
  const timeString = `${TODAY} ${currentTime}`
  return new Date(timeString).getTime()
}

频率设置视图模型

// ets/viewModel/FrequencySetting.ets

export function padTo2Digits(num: number) {
  return num.toString().padStart(2, '0')
}

时间提醒弹窗实现

更新TaskDetail

// ets/view/TaskDetail.ets

import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import {
  TaskChooseItem,
  TargetSetItem,
  // 3. 导入模块
  OpenRemindItem, 
  RemindTimeItem
} from './TaskEditListItem'
import { router } from '@kit.ArkUI'
import { taskType } from '../model/TaskInfo'
import { BroadCast, BroadCastType } from '../common/utils/BroadCast'
import { CustomDialogView } from './CustomDialogView'

@Styles
function listItemStyle() {
  .backgroundColor($r('app.color.white'))
  .height(commonConst.DEFAULT_56)
  .borderRadius(commonConst.DEFAULT_10)
  .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}

@Component
export default struct TaskDetail {
  @Provide settingParams: TaskListItem = this.parseRouterParams()
  @Provide broadCast: BroadCast = new BroadCast()

  parseRouterParams() {
    let params = router.getParams() as Record<string, Object>
    const routerParams: TaskListItem = JSON.parse(params.params as string)
    return routerParams
  }

  aboutToAppear() {
    this.broadCast.off()
  }

  build() {
    Column() {
      List({ space: commonConst.LIST_ITEM_SPACE }) {
        ListItem() {
          TaskChooseItem()
        }
        .listItemStyle()

        ListItem() {
          TargetSetItem()
        }
        .listItemStyle()
        .enabled(
          this.settingParams?.isOpen &&
            (this.settingParams?.taskID !== taskType.smile) &&
            (this.settingParams?.taskID !== taskType.brushTeeth)
        )
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_TARGET_SETTING_DIALOG)
        })

        // 1.构造编辑列表相应内容
        ListItem() {
          OpenRemindItem()
        }
        .listItemStyle()
        .enabled(this.settingParams.isOpen)

        
        ListItem() {
          // 2.构造编辑列表相应内容
          RemindTimeItem()
        }
        .listItemStyle()
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_REMIND_TIME_DIALOG
          )
        })

        ListItem() {
          Text('频率')
        }
        .listItemStyle()
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_FREQUENCY_DIALOG
          )
        })
      }

      CustomDialogView()
    }
    .width(commonConst.THOUSANDTH_1000)
  }
}

实现编辑任务列表的开启提醒与提醒时间

import { TaskListItem } from '../model/TaskInitList'
import {
  DEFAULT_16,
  DEFAULT_20,
  DEFAULT_32,
  DEFAULT_56,
  DEFAULT_8,
  PER_DAY,
  THOUSANDTH_1000,
} from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'

@Extend(Text)
function targetSetCommon() {
  .fontSize(DEFAULT_16)
  .flexGrow(1)
  .margin({ right: DEFAULT_8 })
  .align(Alignment.End)
}

@Extend(Text)
function targetSettingStyle(isOpen: boolean, taskID: number) {
  .fontColor(isOpen && taskID !== taskType.smile && taskID !== taskType.brushTeeth ?
  $r('app.color.titleColor') :
  $r('app.color.disabledColor'))
}

@Extend(Text)
function remindTimeStyle(isOpen: boolean, isAlarm: boolean) {
  .fontColor(isOpen && isAlarm ? $r('app.color.titleColor') : $r('app.color.disabledColor'))
}

@Component
export struct TaskChooseItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text(this.settingParams.taskName).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
      Toggle({ type: ToggleType.Switch, isOn: this.settingParams.isOpen })
        .width(DEFAULT_56)
        .height(DEFAULT_32)
        .selectedColor($r('app.color.blueColor'))
        .onChange((isOn: boolean) => {
          this.settingParams.isOpen = isOn;
        })
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

@Component
export struct TargetSetItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text($r('app.string.target_setting'))
        .fontSize(DEFAULT_20)
        .fontWeight(FontWeight.Medium)

      Blank()
        .layoutWeight(1)

      if (this.settingParams?.unit === '') {
        Text(`${this.settingParams?.targetValue}`)
          .targetSetCommon()
          .targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
      } else {
        Text(`${this.settingParams?.targetValue} ${this.settingParams?.unit} ${PER_DAY}`)
          .targetSetCommon()
          .targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
      }
      Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16);
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}


// 1.实现开启提醒
@Component
export struct OpenRemindItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text($r('app.string.open_reminder'))
        .fontSize(DEFAULT_20)
        .fontWeight(FontWeight.Medium)

      Blank()
        .layoutWeight(1)

      Toggle({ type: ToggleType.Switch, isOn: this.settingParams?.isAlarm })
        .width(DEFAULT_56)
        .height(DEFAULT_32)
        .selectedColor($r('app.color.blueColor'))
        .onChange((isOn: boolean) => {
          this.settingParams.isAlarm = isOn
        })
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

// 2.实现提醒时间
@Component
export struct RemindTimeItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text($r('app.string.remind_time')).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
      Blank()
        .layoutWeight(1)
      Text(this.settingParams?.startTime)
        .targetSetCommon()
        .remindTimeStyle(this.settingParams.isOpen, this.settingParams.isAlarm)
      Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16)
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

实现时间提醒弹窗

import * as commonConst from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'
import { TaskListItem } from '../model/TaskInitList'
import { createAppleRange, createDrinkRange, formatTime, returnTimeStamp } from '../viewModel/TaskTargetSetting'
import { promptAction } from '@kit.ArkUI'

@CustomDialog
export struct TargetSettingDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: TargetSettingDialog()
  })

  @Consume settingParams: TaskListItem

  currentTime: string = commonConst.DEFAULT_TIME
  currentValue: string = this.settingParams.taskID === taskType.drinkWater ? commonConst.DEFAULT_TEXT :
  commonConst.DEFAULT_APPLE

  drinkRange: string[] = createDrinkRange()
  appleRange: string[] = createAppleRange()

  createSubTitle() {
    if (this.settingParams.taskID === taskType.getup) {
      return commonConst.GET_UP_TIME_RANGE
    }
    if (this.settingParams.taskID === taskType.sleepEarly) {
      return commonConst.SLEEP_TIME_RANGE
    }
    return ''
  }

  setTargetValue() {
    if (this.settingParams.taskID === taskType.getup) {
      if (!this.compareTime(commonConst.GET_UP_EARLY_TIME, commonConst.GET_UP_LATE_TIME)) {
        return
      }
      this.settingParams.targetValue = this.currentTime
      return
    }

    if (this.settingParams?.taskID === taskType.sleepEarly) {
      if (!this.compareTime(commonConst.SLEEP_EARLY_TIME, commonConst.SLEEP_LATE_TIME)) {
        return
      }
      this.settingParams.targetValue = this.currentTime
      return
    }
    this.settingParams.targetValue = this.currentValue
  }

  compareTime(startTime: string, endTime: string) {
    if (returnTimeStamp(this.currentTime) < returnTimeStamp(startTime) ||
      returnTimeStamp(this.currentTime) > returnTimeStamp(endTime)) {

      promptAction.showToast({
        message: commonConst.CHOOSE_TIME_OUT_RANGE
      })
      return false
    }
    return true
  }

  build() {
    Column() {
      Row() {
        Text($r('app.string.target_setting')).fontSize(commonConst.DEFAULT_20).margin({ right: commonConst.DEFAULT_12 })
        Text(this.createSubTitle())
          .fontSize(commonConst.DEFAULT_16)
      }
      .width(commonConst.THOUSANDTH_1000)
      .justifyContent(FlexAlign.Start)

      if ([taskType.getup, taskType.sleepEarly].indexOf(this.settingParams.taskID) > commonConst.HAS_NO_INDEX) {
        TimePicker({
          selected: commonConst.DEFAULT_SELECTED_TIME
        })
          .height(commonConst.THOUSANDTH_800)
          .useMilitaryTime(true)
          .onChange((value: TimePickerResult) => {
            this.currentTime = formatTime(value)
          })
      } else {
        TextPicker({ range: this.settingParams?.taskID === taskType.drinkWater ? this.drinkRange : this.appleRange,
          value: this.settingParams.targetValue })
          .width(commonConst.THOUSANDTH_900)
          .height(commonConst.THOUSANDTH_800)
          .onChange((value: string | string[]) => {
            this.currentValue = (value as string)?.split(' ')[0]
          })
      }

      Row() {
        Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.currentTime = commonConst.DEFAULT_TIME;
            this.currentValue = commonConst.DEFAULT_TEXT;
            this.controller.close()
          })

        Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.setTargetValue()
            this.controller.close()
          })
      }
      .justifyContent(FlexAlign.SpaceAround)
      .width(commonConst.THOUSANDTH_1000)
      .height(commonConst.DEFAULT_28)
      .margin({ bottom: commonConst.DEFAULT_20 })
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

// 实现时间提醒弹窗
@CustomDialog
export struct RemindTimeDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: RemindTimeDialog()
  })

  currentTime: string = commonConst.DEFAULT_TIME

  @Consume settingParams: TaskListItem

  build() {
    Column() {
      Column() {
        Text($r('app.string.remind_time'))
          .fontSize(commonConst.DEFAULT_20)
          .margin({ top: commonConst.DEFAULT_10 })
          .width(commonConst.THOUSANDTH_1000)
          .textAlign(TextAlign.Start)
      }
      .width(commonConst.THOUSANDTH_900)

      TimePicker({
        selected: commonConst.DEFAULT_SELECTED_TIME
      })
        .height(commonConst.THOUSANDTH_800)
        .useMilitaryTime(true)
        .onChange((value: TimePickerResult) => {
          this.currentTime = formatTime(value)
        })

      Row() {
        Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.currentTime = commonConst.DEFAULT_TIME
            this.controller.close()
          })

        Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.settingParams.startTime = this.currentTime
            this.controller.close()
          })
      }
      .justifyContent(FlexAlign.SpaceAround)
      .width(commonConst.THOUSANDTH_1000)
      .height(commonConst.DEFAULT_28)
      .margin({ bottom: commonConst.DEFAULT_20 })
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

@CustomDialog
export struct FrequencyDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: FrequencyDialog()
  })

  build() {
    Column() {
      Text('frequency dialog')
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

频率弹窗和提交完成的实现

// ets/view/TaskDetail.ets

import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import {
  TaskChooseItem,
  TargetSetItem,
  OpenRemindItem,
  RemindTimeItem,
  FrequencyItem
} from './TaskEditListItem'
import { promptAction, router } from '@kit.ArkUI'
import { taskType } from '../model/TaskInfo'
import { BroadCast, BroadCastType } from '../common/utils/BroadCast'
import { CustomDialogView } from './CustomDialogView'

@Styles
function listItemStyle() {
  .backgroundColor($r('app.color.white'))
  .height(commonConst.DEFAULT_56)
  .borderRadius(commonConst.DEFAULT_10)
  .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}

@Component
export default struct TaskDetail {
  @Provide settingParams: TaskListItem = this.parseRouterParams()
  @Provide broadCast: BroadCast = new BroadCast()
  @Provide frequency: string = commonConst.EVERYDAY

  parseRouterParams() {
    let params = router.getParams() as Record<string, Object>
    const routerParams: TaskListItem = JSON.parse(params.params as string)
    return routerParams
  }

  aboutToAppear() {
    this.broadCast.off()
  }

  finishTaskEdit() {
    promptAction.showToast({
      message: commonConst.SETTING_FINISHED_MESSAGE
    })
  }

  build() {
    Column() {
      List({ space: commonConst.LIST_ITEM_SPACE }) {
        ListItem() {
          TaskChooseItem()
        }
        .listItemStyle()

        ListItem() {
          TargetSetItem()
        }
        .listItemStyle()
        .enabled(
          this.settingParams?.isOpen &&
            (this.settingParams?.taskID !== taskType.smile) &&
            (this.settingParams?.taskID !== taskType.brushTeeth)
        )
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_TARGET_SETTING_DIALOG)
        })

        ListItem() {
          OpenRemindItem()
        }
        .listItemStyle()
        .enabled(this.settingParams?.isOpen)

        ListItem() {
          RemindTimeItem()
        }
        .listItemStyle()
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_REMIND_TIME_DIALOG
          )
        })

        // 1. 引入FrequencyItem
        ListItem() {
          FrequencyItem()
        }
        .listItemStyle()
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_FREQUENCY_DIALOG
          )
        })
      }
      .width(commonConst.THOUSANDTH_940)

      // x. 最后实现完成按钮提交
      Button() {
        Text($r('app.string.complete')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
      }
      .width(commonConst.THOUSANDTH_800)
      .height(commonConst.DEFAULT_48)
      .backgroundColor($r('app.color.borderColor'))
      .onClick(() => {
        this.finishTaskEdit()
      })
      .position({
        x: commonConst.THOUSANDTH_100,
        y: commonConst.THOUSANDTH_800
      })

      CustomDialogView()
    }
    .width(commonConst.THOUSANDTH_1000)
  }
}

实现频率任务项视图

// ets/view/TaskEditListItem.ets

import { TaskListItem } from '../model/TaskInitList'
import {
  DEFAULT_12,
  DEFAULT_16,
  DEFAULT_20,
  DEFAULT_32,
  DEFAULT_56,
  DEFAULT_8,
  PER_DAY,
  THOUSANDTH_1000,
} from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'

@Extend(Text)
function targetSetCommon() {
  .fontSize(DEFAULT_16)
  .flexGrow(1)
  .margin({ right: DEFAULT_8 })
  .align(Alignment.End)
}

@Extend(Text)
function targetSettingStyle(isOpen: boolean, taskID: number) {
  .fontColor(isOpen && taskID !== taskType.smile && taskID !== taskType.brushTeeth ?
  $r('app.color.titleColor') :
  $r('app.color.disabledColor'))
}

@Extend(Text)
function remindTimeStyle(isOpen: boolean, isAlarm: boolean) {
  .fontColor(isOpen && isAlarm ? $r('app.color.titleColor') : $r('app.color.disabledColor'))
}

@Extend(Text)
function frequencyStyle(isOpen: boolean) {
  .fontSize(DEFAULT_12)
  .flexGrow(1)
  .margin({ right: DEFAULT_8 })
  .textAlign(TextAlign.End)
  .fontColor(isOpen ? $r('app.color.titleColor') : $r('app.color.disabledColor'))
}

@Component
export struct TaskChooseItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text(this.settingParams.taskName).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
      Toggle({ type: ToggleType.Switch, isOn: this.settingParams.isOpen })
        .width(DEFAULT_56)
        .height(DEFAULT_32)
        .selectedColor($r('app.color.blueColor'))
        .onChange((isOn: boolean) => {
          this.settingParams.isOpen = isOn;
        })
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

@Component
export struct TargetSetItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text($r('app.string.target_setting'))
        .fontSize(DEFAULT_20)
        .fontWeight(FontWeight.Medium)

      Blank()
        .layoutWeight(1)

      if (this.settingParams?.unit === '') {
        Text(`${this.settingParams?.targetValue}`)
          .targetSetCommon()
          .targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
      } else {
        Text(`${this.settingParams?.targetValue} ${this.settingParams?.unit} ${PER_DAY}`)
          .targetSetCommon()
          .targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
      }
      Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16);
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

@Component
export struct OpenRemindItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text($r('app.string.open_reminder'))
        .fontSize(DEFAULT_20)
        .fontWeight(FontWeight.Medium)

      Blank()
        .layoutWeight(1)

      Toggle({ type: ToggleType.Switch, isOn: this.settingParams?.isAlarm })
        .width(DEFAULT_56)
        .height(DEFAULT_32)
        .selectedColor($r('app.color.blueColor'))
        .onChange((isOn: boolean) => {
          this.settingParams.isAlarm = isOn
        })
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

@Component
export struct RemindTimeItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text($r('app.string.remind_time')).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
      Blank()
        .layoutWeight(1)
      Text(this.settingParams?.startTime)
        .targetSetCommon()
        .remindTimeStyle(this.settingParams.isOpen, this.settingParams.isAlarm)
      Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16)
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

// 1. 实现频率任务项视图
@Component
export struct FrequencyItem {
  @Consume settingParams: TaskListItem
  @Consume frequency: string

  build() {
    Row() {
      Text($r('app.string.frequency')).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
      Text(this.frequency)
        .targetSetCommon()
        .frequencyStyle(this.settingParams.isOpen)
      Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16)
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

实现频率设置弹窗

// ets/view/TaskSettingDialog.ets

import * as commonConst from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'
import { FrequencyContentType, TaskListItem } from '../model/TaskInitList'
import { createAppleRange, createDrinkRange, formatTime, returnTimeStamp } from '../viewModel/TaskTargetSetting'
import { promptAction } from '@kit.ArkUI'
import { frequencyRange } from '../viewModel/FrequencySetting'

@CustomDialog
export struct TargetSettingDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: TargetSettingDialog()
  })

  @Consume settingParams: TaskListItem

  currentTime: string = commonConst.DEFAULT_TIME
  currentValue: string = this.settingParams.taskID === taskType.drinkWater ? commonConst.DEFAULT_TEXT :
  commonConst.DEFAULT_APPLE

  drinkRange: string[] = createDrinkRange()
  appleRange: string[] = createAppleRange()

  createSubTitle() {
    if (this.settingParams.taskID === taskType.getup) {
      return commonConst.GET_UP_TIME_RANGE
    }
    if (this.settingParams.taskID === taskType.sleepEarly) {
      return commonConst.SLEEP_TIME_RANGE
    }
    return ''
  }

  setTargetValue() {
    if (this.settingParams.taskID === taskType.getup) {
      if (!this.compareTime(commonConst.GET_UP_EARLY_TIME, commonConst.GET_UP_LATE_TIME)) {
        return
      }
      this.settingParams.targetValue = this.currentTime
      return
    }

    if (this.settingParams?.taskID === taskType.sleepEarly) {
      if (!this.compareTime(commonConst.SLEEP_EARLY_TIME, commonConst.SLEEP_LATE_TIME)) {
        return
      }
      this.settingParams.targetValue = this.currentTime
      return
    }
    this.settingParams.targetValue = this.currentValue
  }

  compareTime(startTime: string, endTime: string) {
    if (returnTimeStamp(this.currentTime) < returnTimeStamp(startTime) ||
      returnTimeStamp(this.currentTime) > returnTimeStamp(endTime)) {

      promptAction.showToast({
        message: commonConst.CHOOSE_TIME_OUT_RANGE
      })
      return false
    }
    return true
  }

  build() {
    Column() {
      Row() {
        Text($r('app.string.target_setting')).fontSize(commonConst.DEFAULT_20).margin({ right: commonConst.DEFAULT_12 })
        Text(this.createSubTitle())
          .fontSize(commonConst.DEFAULT_16)
      }
      .width(commonConst.THOUSANDTH_1000)
      .justifyContent(FlexAlign.Start)

      if ([taskType.getup, taskType.sleepEarly].indexOf(this.settingParams.taskID) > commonConst.HAS_NO_INDEX) {
        TimePicker({
          selected: commonConst.DEFAULT_SELECTED_TIME
        })
          .height(commonConst.THOUSANDTH_800)
          .useMilitaryTime(true)
          .onChange((value: TimePickerResult) => {
            this.currentTime = formatTime(value)
          })
      } else {
        TextPicker({ range: this.settingParams?.taskID === taskType.drinkWater ? this.drinkRange : this.appleRange,
          value: this.settingParams.targetValue })
          .width(commonConst.THOUSANDTH_900)
          .height(commonConst.THOUSANDTH_800)
          .onChange((value: string | string[]) => {
            this.currentValue = (value as string)?.split(' ')[0]
          })
      }

      Row() {
        Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.currentTime = commonConst.DEFAULT_TIME;
            this.currentValue = commonConst.DEFAULT_TEXT;
            this.controller.close()
          })

        Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.setTargetValue()
            this.controller.close()
          })
      }
      .justifyContent(FlexAlign.SpaceAround)
      .width(commonConst.THOUSANDTH_1000)
      .height(commonConst.DEFAULT_28)
      .margin({ bottom: commonConst.DEFAULT_20 })
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

@CustomDialog
export struct RemindTimeDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: RemindTimeDialog()
  })

  currentTime: string = commonConst.DEFAULT_TIME

  @Consume settingParams: TaskListItem

  build() {
    Column() {
      Column() {
        Text($r('app.string.remind_time'))
          .fontSize(commonConst.DEFAULT_20)
          .margin({ top: commonConst.DEFAULT_10 })
          .width(commonConst.THOUSANDTH_1000)
          .textAlign(TextAlign.Start)
      }
      .width(commonConst.THOUSANDTH_900)

      TimePicker({
        selected: commonConst.DEFAULT_SELECTED_TIME
      })
        .height(commonConst.THOUSANDTH_800)
        .useMilitaryTime(true)
        .onChange((value: TimePickerResult) => {
          this.currentTime = formatTime(value);
        })

      Row() {
        Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.currentTime = commonConst.DEFAULT_TIME
            this.controller.close()
          })

        Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.settingParams.startTime = this.currentTime
            this.controller.close()
          })
      }
      .justifyContent(FlexAlign.SpaceAround)
      .width(commonConst.THOUSANDTH_1000)
      .height(commonConst.DEFAULT_28)
      .margin({ bottom: commonConst.DEFAULT_20 })
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

// 1.实现频率设置弹窗
@CustomDialog
export struct FrequencyDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: FrequencyDialog()
  })

  private frequencyChooseRange: FrequencyContentType[] = frequencyRange()

  private currentFrequency: string = commonConst.EVERYDAY

  @Consume settingParams: TaskListItem

  @Consume frequency: string

  setFrequency() {
    const checkedArr = this.frequencyChooseRange.filter((item: FrequencyContentType) => item.isChecked)

    if (checkedArr.length === this.frequencyChooseRange.length || checkedArr.length === commonConst.NO_LENGTH) {
      this.currentFrequency = commonConst.EVERYDAY
      this.settingParams.frequency = commonConst.INIT_WEEK_IDS
      return
    }

    this.currentFrequency = checkedArr.reduce((sum: string, current: FrequencyContentType) => {
      return sum + ' ' + current.label
    }, '')

    this.settingParams.frequency = checkedArr.reduce((sum: string, current: FrequencyContentType) => {
      return sum === '' ? sum + current.id : sum + ',' + current.id
    }, '')
  }

  build() {
    Column() {
      Column() {
        Text($r('app.string.set_your_frequency'))
          .fontSize(commonConst.DEFAULT_20)
          .margin({ top: commonConst.DEFAULT_10 })
          .width(commonConst.THOUSANDTH_1000)
          .textAlign(TextAlign.Start)
      }
      .width(commonConst.THOUSANDTH_900)

      List() {
        ForEach(this.frequencyChooseRange, (item: FrequencyContentType) => {
          ListItem() {
            Row() {
              Text(item?.label).fontSize(commonConst.DEFAULT_20)
              Toggle({ type: ToggleType.Checkbox })
                .onChange((isOn: boolean) => {
                  item.isChecked = isOn
                })
            }
            .width(commonConst.THOUSANDTH_1000)
            .justifyContent(FlexAlign.SpaceBetween)
            .height(commonConst.DEFAULT_60)
          }
        })
      }
      .divider({
        strokeWidth: commonConst.DEFAULT_2,
        color: $r('app.color.btnBgColor')
      })
      .flexGrow(1)
      .padding(commonConst.DEFAULT_12)
      .width(commonConst.THOUSANDTH_1000)

      Row() {
        Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.controller.close()
          })

        Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.setFrequency()
            this.frequency = this.currentFrequency
            this.controller.close()
          })
      }
      .justifyContent(FlexAlign.SpaceAround)
      .width(commonConst.THOUSANDTH_900)
      .height(commonConst.DEFAULT_28)
      .margin({ bottom: commonConst.DEFAULT_16 })
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_940)
    .padding(commonConst.DEFAULT_12)
  }
}

定义频率设置视图模型

// ets/viewModel/FrequencySetting.ets

import { FrequencyContentType } from "../model/TaskInitList"

export function padTo2Digits(num: number) {
  return num.toString().padStart(2, '0')
}

const chineseNumOfWeek: string[] = ['一', '二', '三', '四', '五', '六', '日']
const WEEK: string = '星期'

export const frequencyRange = () => {
  const frequencyRangeArr: FrequencyContentType[] = []
  chineseNumOfWeek.forEach((item: string, index: number) => {
    frequencyRangeArr.push({
      id: (index + 1),
      label: `${WEEK}${item}`,
      isChecked: false
    })
  })
  return frequencyRangeArr
}

 基础知识:切换按钮 (Toggle)

Toggle组件提供状态按钮样式、勾选框样式和开关样式,一般用于两种状态之间的切换。

创建切换按钮

Toggle通过调用接口来创建,接口调用形式如下:

Toggle(options: { type: ToggleType, isOn?: boolean })

其中,ToggleType为开关类型,包括Button、Checkbox和Switch,isOn为切换按钮的状态。

API version 11开始,Checkbox默认样式由圆角方形变为圆形。

接口调用有以下两种形式:

  • 创建不包含子组件的Toggle。

当ToggleType为Checkbox或者Switch时,用于创建不包含子组件的Toggle:

Toggle({ type: ToggleType.Checkbox, isOn: false })
Toggle({ type: ToggleType.Checkbox, isOn: true })

Toggle({ type: ToggleType.Switch, isOn: false })
Toggle({ type: ToggleType.Switch, isOn: true })

  • 创建包含子组件的Toggle。

当ToggleType为Button时,只能包含一个子组件,如果子组件有文本设置,则相应的文本内容会显示在按钮上。

Toggle({ type: ToggleType.Button, isOn: false }) {
  Text('status button')
    .fontColor('#182431')
    .fontSize(12)
}.width(100)
Toggle({ type: ToggleType.Button, isOn: true }) {
  Text('status button')
    .fontColor('#182431')
    .fontSize(12)
}.width(100)

自定义样式

  • 通过selectedColor属性设置Toggle打开选中后的背景颜色。
Toggle({ type: ToggleType.Button, isOn: true }) {
  Text('status button')
    .fontColor('#182431')
    .fontSize(12)
}.width(100).selectedColor(Color.Pink)
Toggle({ type: ToggleType.Checkbox, isOn: true })
  .selectedColor(Color.Pink)
Toggle({ type: ToggleType.Switch, isOn: true })
  .selectedColor(Color.Pink)

  • 通过switchPointColor属性设置Switch类型的圆形滑块颜色,仅对type为ToggleType.Switch生效。
Toggle({ type: ToggleType.Switch, isOn: false })
  .switchPointColor(Color.Pink)
Toggle({ type: ToggleType.Switch, isOn: true })
  .switchPointColor(Color.Pink)

添加事件

除支持通用事件外,Toggle还用于选中和取消选中后触发某些操作,可以绑定onChange事件来响应操作后的自定义行为。

Toggle({ type: ToggleType.Switch, isOn: false })
  .onChange((isOn: boolean) => {
    if(isOn) {
      // 需要执行的操作
    }
  })

案例整理

// ets/pages/toggle/usagePage.ets

@Entry
@Component
struct usagePage {
  @State isOn: boolean = true

  build() {
    Column({ space: 20 }) {
      Row() {
        Toggle({ type: ToggleType.Checkbox, isOn: false })
        Toggle({ type: ToggleType.Checkbox, isOn: true })
      }

      Row() {
        Toggle({ type: ToggleType.Switch, isOn: false })
        Toggle({ type: ToggleType.Switch, isOn: true })
      }

      Row() {
        Toggle({ type: ToggleType.Button, isOn: false }) {
          Text('status button')
            .fontColor('#182431')
            .fontSize(12)
        }.width(100)
        Toggle({ type: ToggleType.Button, isOn: true }) {
          Text('status button')
            .fontColor('#182431')
            .fontSize(12)
        }.width(100)
      }

      Row() {
        Toggle({ type: ToggleType.Button, isOn: true }) {
          Text('status button')
            .fontColor('#182431')
            .fontSize(12)
        }.width(100).selectedColor(Color.Orange)

        Toggle({ type: ToggleType.Checkbox, isOn: true })
          .selectedColor(Color.Orange)

        Toggle({ type: ToggleType.Switch, isOn: true })
          .selectedColor(Color.Orange)
      }

      Row() {
        Toggle({ type: ToggleType.Switch, isOn: false })
          .switchPointColor(Color.Orange)
        Toggle({ type: ToggleType.Switch, isOn: true })
          .switchPointColor(Color.Orange)
      }

      Row() {
        Toggle({ type: ToggleType.Switch, isOn: this.isOn })
          .onChange((isOn: boolean) => {
              this.isOn = !this.isOn
          })
        Text(`${this.isOn}`)
      }

    }
    .width('100%')
  }
}

场景示例

Toggle用于切换蓝牙开关状态。

// ets/pages/toggle/CasePage.ets

import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct CasePage {
  @State BOnSt: promptAction.ShowToastOptions = { 'message': 'Bluetooth is on.' }
  @State BOffSt: promptAction.ShowToastOptions = { 'message': 'Bluetooth is off.' }

  build() {
    Column() {
      Row() {
        Text("Bluetooth Mode")
          .height(50)
          .fontSize(16)
      }

      Row() {
        Text("Bluetooth")
          .height(50)
          .padding({ left: 10 })
          .fontSize(16)
          .textAlign(TextAlign.Start)
          .backgroundColor(0xFFFFFF)
        
        Toggle({ type: ToggleType.Switch })
          .margin({ left: 200, right: 10 })
          .onChange((isOn: boolean) => {
            if (isOn) {
              promptAction.showToast(this.BOnSt)
            } else {
              promptAction.showToast(this.BOffSt)
            }
          })
      }
      .backgroundColor(0xFFFFFF)
    }
    .padding(10)
    .backgroundColor(0xDCDCDC)
    .width('100%')
    .height('100%')
  }
}

基础知识:学习Picker选择器

CalendarPicker

示例

// ets/pages/picker/CalendarPickerPage.ets

@Entry
@Component
struct CalendarPickerPage {
  private selectedDate: Date = new Date('2025-03-05')

  build() {
    Column() {
      Text('月历日期选择器').fontSize(30)
      Column() {
        CalendarPicker({ hintRadius: 10, selected: this.selectedDate })
          .edgeAlign(CalendarAlign.END)
          .textStyle({ color: "#182431", font: { size: 20, weight: FontWeight.Normal } })
          .margin(10)
          .onChange((value) => {
            console.info("CalendarPicker onChange:" + JSON.stringify(value))
          })
      }
      .alignItems(HorizontalAlign.End)
      .width("100%")
    }.width('100%')
  }
}

DatePicker

示例

// ets/pages/picker/DatePickerPage.ets

@Entry
@Component
struct DatePickerPage {
  @State isLunar: boolean = false
  private selectedDate: Date = new Date('2025-08-08')

  build() {
    Column() {
      Button('切换公历农历')
        .margin({ top: 30, bottom: 30 })
        .onClick(() => {
          this.isLunar = !this.isLunar
        })

      DatePicker({
        start: new Date('1970-1-1'),
        end: new Date('2100-1-1'),
        selected: this.selectedDate
      })
        .disappearTextStyle({ color: Color.Gray, font: { size: '16fp', weight: FontWeight.Bold } })
        .textStyle({ color: '#182431', font: { size: '18fp', weight: FontWeight.Normal } })
        .selectedTextStyle({ color: '#0000FF', font: { size: '26fp', weight: FontWeight.Regular } })
        .lunar(this.isLunar)
        .onDateChange((value: Date) => {
          this.selectedDate = value
          console.info('select current date is: ' + value.toString())
        })
    }.width('100%')
  }
}

TextPicker

示例1(设置选择器列数)

该示例通过配置range实现单列或多列文本选择器。

// ets/pages/picker/TextPicker01Page.ets

class bottom {
  bottom: number = 50
}

let bott: bottom = new bottom()

@Entry
@Component
struct TextPicker01Page {
  private select: number = 1
  private apFruits: string[] = ['apple1', 'apple2', 'apple3', 'apple4']
  private orFruits: string[] = ['orange1', 'orange2', 'orange3', 'orange4']
  private peFruits: string[] = ['peach1', 'peach2', 'peach3', 'peach4']
  private multi: string[][] = [this.apFruits, this.orFruits, this.peFruits]
  private cascade: TextCascadePickerRangeContent[] = [
    {
      text: '辽宁省',
      children: [{ text: '沈阳市', children: [{ text: '沈河区' }, { text: '和平区' }, { text: '浑南区' }] },
        { text: '大连市', children: [{ text: '中山区' }, { text: '金州区' }, { text: '长海县' }] }]
    },
    {
      text: '吉林省',
      children: [{ text: '长春市', children: [{ text: '南关区' }, { text: '宽城区' }, { text: '朝阳区' }] },
        { text: '四平市', children: [{ text: '铁西区' }, { text: '铁东区' }, { text: '梨树县' }] }]
    },
    {
      text: '黑龙江省',
      children: [{ text: '哈尔滨市', children: [{ text: '道里区' }, { text: '道外区' }, { text: '南岗区' }] },
        { text: '牡丹江市', children: [{ text: '东安区' }, { text: '西安区' }, { text: '爱民区' }] }]
    }
  ]

  build() {
    Column() {
      TextPicker({ range: this.apFruits, selected: this.select })
        .onChange((value: string | string[], index: number | number[]) => {
          console.info('Picker item changed, value: ' + value + ', index: ' + index)
        }).margin(bott)
      TextPicker({ range: this.multi })
        .onChange((value: string | string[], index: number | number[]) => {
          console.info('TextPicker 多列:onChange ' + JSON.stringify(value) + ', ' + 'index: ' + JSON.stringify(index))
        }).margin(bott)
      TextPicker({ range: this.cascade })
        .onChange((value: string | string[], index: number | number[]) => {
          console.info('TextPicker 多列联动:onChange ' + JSON.stringify(value) + ', ' + 'index: ' +
          JSON.stringify(index))
        })
    }
  }
}

示例2(设置文本样式)

该示例通过配置disappearTextStyle、textStyle、selectedTextStyle实现文本选择器中的文本样式。

// ets/pages/picker/TextPicker02Page.ets

@Entry
@Component
struct TextPicker02Page {
  private select: number = 1
  private fruits: string[] = ['apple1', 'orange2', 'peach3', 'grape4']

  build() {
    Column() {
      TextPicker({ range: this.fruits, selected: this.select })
        .onChange((value: string | string[], index: number | number[]) => {
          console.info('Picker item changed, value: ' + value + ', index: ' + index)
        })
        .disappearTextStyle({ color: Color.Red, font: { size: 15, weight: FontWeight.Lighter } })
        .textStyle({ color: Color.Black, font: { size: 20, weight: FontWeight.Normal } })
        .selectedTextStyle({ color: Color.Blue, font: { size: 30, weight: FontWeight.Bolder } })
    }
    .width('100%')
    .height('100%')
  }
}

示例3(设置无分割线样式)

该示例通过配置divider为null实现无分割线样式的文本选择器。

// ets/pages/picker/TextPicker03Page.ets

@Entry
@Component
struct TextPicker03Page {
  private select: number = 1
  private fruits: string[] = ['apple1', 'orange2', 'peach3', 'grape4']

  build() {
    Column() {
      TextPicker({ range: this.fruits, selected: this.select })
        .onChange((value: string | string[], index: number | number[]) => {
          console.info('Picker item changed, value: ' + value + ', index: ' + index)
        })
        .disappearTextStyle({color: Color.Red, font: {size: 15, weight: FontWeight.Lighter}})
        .textStyle({color: Color.Black, font: {size: 20, weight: FontWeight.Normal}})
        .selectedTextStyle({color: Color.Blue, font: {size: 30, weight: FontWeight.Bolder}})
        .divider(null)
    }.width('100%').height('100%')
  }
}

示例4(设置分割线样式)

该示例通过配置divider的DividerOptions类型实现分割线样式的文本选择器。

// ets/pages/picker/TextPicker04Page.ets

@Entry
@Component
struct TextPicker04Page {
  private select: number = 1
  private fruits: string[] = ['apple1', 'orange2', 'peach3', 'grape4']

  build() {
    Column() {
      TextPicker({ range: this.fruits, selected: this.select })
        .onChange((value: string | string[], index: number | number[]) => {
          console.info('Picker item changed, value: ' + value + ', index: ' + index)
        })
        .disappearTextStyle({ color: Color.Red, font: { size: 15, weight: FontWeight.Lighter } })
        .textStyle({ color: Color.Black, font: { size: 20, weight: FontWeight.Normal } })
        .selectedTextStyle({ color: Color.Blue, font: { size: 30, weight: FontWeight.Bolder } })
        .divider({
          strokeWidth: 10,
          color: Color.Red,
          startMargin: 10,
          endMargin: 20
        } as DividerOptions)
    }
    .width('100%')
    .height('100%')
  }
}

示例5(设置渐隐效果)

该示例通过gradientHeight自定义TextPicker的渐隐效果高度。

// ets/pages/picker/TextPicker05Page.ets

@Entry
@Component
struct TextPicker05Page {
  private select: number = 1
  private fruits: string[] = ['apple1', 'orange2', 'peach3', 'grape4']

  build() {
    Column() {
      TextPicker({ range: this.fruits, selected: this.select })
        .onChange((value: string | string[], index: number | number[]) => {
          console.info('Picker item changed, value: ' + value + ', index: ' + index)
        })
        .disappearTextStyle({ color: Color.Red, font: { size: 15, weight: FontWeight.Lighter } })
        .textStyle({ color: Color.Black, font: { size: 20, weight: FontWeight.Normal } })
        .selectedTextStyle({ color: Color.Blue, font: { size: 30, weight: FontWeight.Bolder } })
        .gradientHeight(100)
    }.width('100%').height('100%')
  }
}

TimePicker

示例

// ets/pages/picker/TimePicker.ets

@Entry
@Component
struct TimePickerExample {
  @State isMilitaryTime: boolean = false
  private selectedTime: Date = new Date('2025-07-22T08:00:00')

  build() {
    Column() {
      Button('切换12小时制/24小时制')
        .margin(30)
        .onClick(() => {
          this.isMilitaryTime = !this.isMilitaryTime
        })

      TimePicker({
        selected: this.selectedTime,
        format: TimePickerFormat.HOUR_MINUTE_SECOND
      })
        .useMilitaryTime(this.isMilitaryTime)
        .onChange((value: TimePickerResult) => {
          if(value.hour >= 0) {
            this.selectedTime.setHours(value.hour, value.minute)
            console.info(`${value}`)
          }
        })
        .disappearTextStyle({color: Color.Red, font: {size: 15, weight: FontWeight.Lighter}})
        .textStyle({color: Color.Black, font: {size: 20, weight: FontWeight.Normal}})
        .selectedTextStyle({color: Color.Blue, font: {size: 30, weight: FontWeight.Bolder}})

    }.width('100%')
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2400960.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TDengine 的 AI 应用实战——运维异常检测

作者&#xff1a; derekchen Demo数据集准备 我们使用公开的 NAB数据集 里亚马逊 AWS 东海岸数据中心一次 API 网关故障中&#xff0c;某个服务器上的 CPU 使用率数据。数据的频率为 5min&#xff0c;单位为占用率。由于 API 网关的故障&#xff0c;会导致服务器上的相关应用…

使用Plop.js高效生成模板文件

前情 开发是个创造型的职业&#xff0c;也是枯燥的职业&#xff0c;因为开发绝大多数都是每天在业务的代码中无法自拨&#xff0c;说到开发工作&#xff0c;就永远都逃不开新建文件的步骤&#xff0c;特别现在组件化开发胜行&#xff0c;每天都是在新建新建组件的道路上一去不…

Vue框架2(vue搭建方式2:利用脚手架,ElementUI)

一.引入vue第二种搭建方式 在以前的前端项目中,一个项目需要多个html文件实现页面之前的切换,如果页面中需要依赖js或者css文件,那么我们就需要在多个html文件中都需要导入vue.js文件,太过繁琐. 现在前端开发都采用单页面结果,一个项目中只有一个html文件 其他不同的内容都写…

mac 设置cursor (像PyCharm一样展示效果)

一、注册 Cursor - The AI Code Editor 二、配置Python环境 我之前使用pycharm创建的python项目&#xff0c;以及创建了虚拟环境&#xff0c;现在要使用cursor继续开发。 2.1 选择Python 虚拟环境 PyCharm 通常将虚拟环境存储在项目目录下的 venv 或 .venv 文件夹中&#xf…

SpringCloudAlibaba微服务架构

技术架构图 SpringCloudAlibaba微服务架构 说明&#xff1a; 1.1、采用SpringCloudAlibaba分布式微服务架构&#xff0c;使用Nginx做代理&#xff0c;服务治理使用Nacos组件&#xff0c;Gateway网关做权限验证、路由、过滤。 1.2、Redis做消息缓存&#xff0c;包括数据大屏、数…

Java高级 | 【实验三】Springboot 静态资源访问

隶属文章&#xff1a; Java高级 | &#xff08;二十二&#xff09;Java常用类库-CSDN博客 系列文章&#xff1a; Java高级 | 【实验一】Spring Boot安装及测试 最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 目录 一、Thymeleaf 1.1 是什么&…

「Java教案」Java程序的构成

课程目标 1&#xff0e;知识目标 能够按照Java标识符的命名规则&#xff0c;规范变量的命名。能够区分Java中的关键字与保留字。能够对注释进行分类&#xff0c;根据注释的用途合理的选择注释方式。 2&#xff0e;能力目标 能编写符合规范的标识符。能识别Java中的关键字和…

区块链可投会议CCF B--EDBT 2026 截止10.8 附录用率

Conference&#xff1a;EDBT: 29th International Conference on Extending Database Technology CCF level&#xff1a;CCF B Categories&#xff1a;数据库&#xff0f;数据挖掘&#xff0f;内容检索 Year&#xff1a;2026 Conference time&#xff1a;24th March - 27th…

经典ReLU回归!重大缺陷「死亡ReLU问题」已被解决

来源 &#xff5c; 机器之心 在深度学习领域中&#xff0c;对激活函数的探讨已成为一个独立的研究方向。例如 GELU、SELU 和 SiLU 等函数凭借其平滑梯度与卓越的收敛特性&#xff0c;已成为热门选择。 尽管这一趋势盛行&#xff0c;经典 ReLU 函数仍因其简洁性、固有稀疏性及…

在VSCode中开发一个uni-app项目

创建项目 使用命令行工具&#xff08;例如 vue-cli&#xff09;来创建一个新的 uni-app 项目。 创建以JavaScript开发的工程 npx degit dcloudio/uni-preset-vue#vite my-vue3-project //或者 npx degit dcloudio/uni-preset-vue#vite-alpha my-vue3-project创建以TypeScript…

Python - 爬虫;Scrapy框架之插件Extensions(四)

阅读本文前先参考 https://blog.csdn.net/MinggeQingchun/article/details/145904572 在 Scrapy 中&#xff0c;扩展&#xff08;Extensions&#xff09;是一种插件&#xff0c;允许你添加额外的功能到你的爬虫项目中。这些扩展可以在项目的不同阶段执行&#xff0c;比如启动…

Spark实战能力测评模拟题精析【模拟考】

1.println(Array(1,2,3,4,5).filter(_%20).toList() 输出结果是&#xff08;B&#xff09; A. 2 4 B. List(2,4) C. List(1,3,5) D. 1 3 5 2.println(Array("tom","team","pom") .filter(_.matches("")).toList) 输出结果为(List(tom,…

【OSG学习笔记】Day 15: 路径动画与相机漫游

本章来学习下漫游相机。 路径动画与相机漫游 本届内容比较简单&#xff0c;其实就是实现物体的运动和相机的运动 当然这两个要一起执行。 贝塞尔曲线 贝塞尔曲线&#xff08;Bzier curve&#xff09;是一种在计算机图形学、动画制作、工业设计等领域广泛应用的参数曲线&am…

PostgreSQL(PostGIS)触发器+坐标转换案例

需求&#xff0c;只录入一份坐标参考为4326的数据&#xff0c;但是发布的数据要求坐标必须是3857 对这种需求可以利用数据库触发器实现数据的同步 步骤&#xff1a; 1. 使用ArcGIS Pro创建一个名字为testfc_4326的图层&#xff0c;坐标参考为4326 2. 使用Pro再创建一个名字…

Constraints and Triggers

目录 Kinds of Constraints Single-Attribute Keys Multiattribute Key Foreign Keys Expressing Foreign Keys Enforcing Foreign-Key Constraints Actions Taken Attribute-Based Checks Timing of Checks Tuple-Based Checks Assertions Timing of Assertion Ch…

BERT:让AI真正“读懂”语言的革命

BERT&#xff1a;让AI真正“读懂”语言的革命 ——图解谷歌神作《BERT: Pre-training of Deep Bidirectional Transformers》 2018年&#xff0c;谷歌AI团队扔出一篇核弹级论文&#xff0c;引爆了整个NLP领域。这个叫BERT的模型在11项任务中屠榜&#xff0c;甚至超越人类表现…

冷雨泉教授团队:新型视觉驱动智能假肢手,拟人化抓握技术突破,助力截肢者重获生活自信

研究背景&#xff1a;日常生活中&#xff0c;健康人依靠手完成对物体的操作。对于手部截肢患者&#xff0c;手部的缺失导致他们难以有效地操作物体&#xff0c;进而影响正常的日常生活。拥有一个能够实现拟人地自然抓取多种日常物体的五指动力假手是手部截肢患者的夙愿&#xf…

pikachu靶场通关笔记14 XSS关卡10-XSS之js输出(五种方法渗透)

目录 一、源码分析 1、进入靶场 2、代码审计 二、渗透实战 1、根据提示输入tmac 2、XSS探测 3、注入Payload1 4、注入Payload2 5、注入Payload3 6、注入Payload4 7、注入Payload5 本系列为通过《pikachu靶场通关笔记》的XSS关卡(共10关&#xff09;渗透集合&#x…

李沐-动手学深度学习:RNN

1.RNN从零开始实现 import math import torch from torch import nn from torch.nn import functional as F from d2l import torch as d2l#8.3.4节 #batch_size&#xff1a;每个小批量中子序列样本的数目&#xff0c;num_steps&#xff1a;每个子序列中预定义的时间步数 #loa…

【教学类-36-10】20250531蝴蝶图案描边,最适合大小(一页1图1图、2图图案不同、2图图案相同对称)

背景说明: 之前做了动物头像扇子(描边20),并制作成一页一套图案对称两张 【教学类-36-09】20250526动物头像扇子的描边(通义万相)对称图40张,根据图片长宽,自动旋转图片,最大化图片-CSDN博客文章浏览阅读1k次,点赞37次,收藏6次。【教学类-36-09】20250526动物头像…