鸿蒙任务项设置案例实战 - 实践
2025-10-08 15:38 tlnshuju 阅读(6) 评论(0) 收藏 举报目录
案例效果
资源文件与初始化
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 = 2export const DEFAULT_8: number = 8export const DEFAULT_12: number = 12export const DEFAULT_10: number = 10export const DEFAULT_16: number = 16export const DEFAULT_18: number = 18export const DEFAULT_20: number = 20export const DEFAULT_24: number = 24export const DEFAULT_28: number = 28export const DEFAULT_32: number = 32export const DEFAULT_48: number = 48export const DEFAULT_56: number = 56export 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 = 0export const INIT_WEEK_IDS: string = '1, 2, 3, 4, 5, 6, 7' export const PER_DAY: string = '/ 天' export const ZERO: number = 0export const MINUS_20: number = -20export const HAS_NO_INDEX: number = -1 export const DRINK_STEP: number = 25export const DRINK_MAX_RANGE: number = 500export const TIMES_100: number = 100export const EAT_APPLE_RANGE: number = 100export 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@Componentstruct 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' @Componentexport 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' @Componentexport 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@Componentstruct 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' @Stylesfunction listItemStyle() { .backgroundColor($r('app.color.white')) .height(commonConst.DEFAULT_56) .borderRadius(commonConst.DEFAULT_10) .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })} @Componentexport default struct TaskDetail { @Provide settingParams: TaskListItem = this.parseRouterParams() parseRouterParams() { let params = router.getParams() as Record 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' @Componentexport 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' @Stylesfunction listItemStyle() { .backgroundColor($r('app.color.white')) .height(commonConst.DEFAULT_56) .borderRadius(commonConst.DEFAULT_10) .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })} @Componentexport default struct TaskDetail { @Provide settingParams: TaskListItem = this.parseRouterParams() parseRouterParams() { let params = router.getParams() as Record 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'))} @Componentexport 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组件@Componentexport 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' @Stylesfunction listItemStyle() { .backgroundColor($r('app.color.white')) .height(commonConst.DEFAULT_56) .borderRadius(commonConst.DEFAULT_10) .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })} @Componentexport default struct TaskDetail { @Provide settingParams: TaskListItem = this.parseRouterParams() // 5. 提供 broadCast @Provide broadCast: BroadCast = new BroadCast() parseRouterParams() { let params = router.getParams() as Record 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' @Componentexport 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' @CustomDialogexport 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) }} @CustomDialogexport 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) }} @CustomDialogexport 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' @CustomDialogexport 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(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) }} @CustomDialogexport 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) }} @CustomDialogexport 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 { const appleRangeArr: string[] = [] for (let i = 1; i { 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' @Stylesfunction listItemStyle() { .backgroundColor($r('app.color.white')) .height(commonConst.DEFAULT_56) .borderRadius(commonConst.DEFAULT_10) .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })} @Componentexport default struct TaskDetail { @Provide settingParams: TaskListItem = this.parseRouterParams() @Provide broadCast: BroadCast = new BroadCast() parseRouterParams() { let params = router.getParams() as Record 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'))} @Componentexport 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) }} @Componentexport 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.实现开启提醒@Componentexport 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.实现提醒时间@Componentexport 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' @CustomDialogexport 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(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) }} // 实现时间提醒弹窗@CustomDialogexport 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) }} @CustomDialogexport 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' @Stylesfunction listItemStyle() { .backgroundColor($r('app.color.white')) .height(commonConst.DEFAULT_56) .borderRadius(commonConst.DEFAULT_10) .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })} @Componentexport 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 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'))} @Componentexport 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) }} @Componentexport 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) }} @Componentexport 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) }} @Componentexport 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. 实现频率任务项视图@Componentexport 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' @CustomDialogexport 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(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) }} @CustomDialogexport 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.实现频率设置弹窗@CustomDialogexport 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@Componentstruct 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@Componentstruct 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@Componentstruct 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@Componentstruct 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@Componentstruct 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@Componentstruct 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@Componentstruct 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@Componentstruct 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@Componentstruct 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@Componentstruct 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%') }}