目錄
案例效果
資源文件與初始化
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.etsexport 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 = 60export const LIST_ITEM_SPACE: number = 2export 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 = -1export 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.etsimport 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[] = TaskInitListbuild() {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.etsexport interface TaskListItem {taskID: numbertaskName: ResourceisOpen: booleanunit: stringicon: ResourcetargetValue: stringisAlarm: booleanstartTime: stringfrequency: 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.etsimport * 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.etsimport * 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/TaskTargetSettingimport { TaskListItem } from '../model/TaskInitList'
export const formatParams = (params: TaskListItem) => {return JSON.stringify(params)
}
編輯頁面
// ets/pages/TaskEditPage.etsimport { 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.etsimport * 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.etsimport { TaskListItem } from '../model/TaskInitList'
import {DEFAULT_20,DEFAULT_32,DEFAULT_56,THOUSANDTH_1000,
} from '../common/constants/CommonConstant'@Component
export struct TaskChooseItem {@Consume settingParams: TaskListItembuild() {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.etsimport * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import {TaskChooseItem,// 2. 引入TargetSetItemTargetSetItem
} 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.etsimport { 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: TaskListItembuild() {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.etsexport 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.etsexport 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 = thisif (!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.etsimport { 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: BroadCasttargetSettingDialogController: 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 = thisthis.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.etsimport * 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.etsimport * 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: TaskListItemcurrentTime: string = commonConst.DEFAULT_TIMEcurrentValue: string = this.settingParams.taskID === taskType.drinkWater ? commonConst.DEFAULT_TEXT :commonConst.DEFAULT_APPLEdrinkRange: 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.currentTimereturn}if (this.settingParams.taskID === taskType.sleepEarly) {if (!this.compareTime(commonConst.SLEEP_EARLY_TIME, commonConst.SLEEP_LATE_TIME)) {return}this.settingParams.targetValue = this.currentTimereturn}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_TIMEthis.currentValue = commonConst.DEFAULT_TEXTthis.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.etsimport { 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 = 0let minute: number = 0if (value.hour !== undefined && value.minute !== undefined) {hour = value.hourminute = 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.etsexport function padTo2Digits(num: number) {return num.toString().padStart(2, '0')
}
時間提醒彈窗實現
更新TaskDetail
// ets/view/TaskDetail.etsimport * 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: TaskListItembuild() {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: TaskListItembuild() {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: TaskListItembuild() {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: TaskListItembuild() {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: TaskListItemcurrentTime: string = commonConst.DEFAULT_TIMEcurrentValue: string = this.settingParams.taskID === taskType.drinkWater ? commonConst.DEFAULT_TEXT :commonConst.DEFAULT_APPLEdrinkRange: 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.currentTimereturn}if (this.settingParams?.taskID === taskType.sleepEarly) {if (!this.compareTime(commonConst.SLEEP_EARLY_TIME, commonConst.SLEEP_LATE_TIME)) {return}this.settingParams.targetValue = this.currentTimereturn}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: TaskListItembuild() {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_TIMEthis.controller.close()})Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor')).onClick(() => {this.settingParams.startTime = this.currentTimethis.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.etsimport * 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.EVERYDAYparseRouterParams() {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. 引入FrequencyItemListItem() {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.etsimport { 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: TaskListItembuild() {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: TaskListItembuild() {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: TaskListItembuild() {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: TaskListItembuild() {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: stringbuild() {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.etsimport * 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: TaskListItemcurrentTime: string = commonConst.DEFAULT_TIMEcurrentValue: string = this.settingParams.taskID === taskType.drinkWater ? commonConst.DEFAULT_TEXT :commonConst.DEFAULT_APPLEdrinkRange: 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.currentTimereturn}if (this.settingParams?.taskID === taskType.sleepEarly) {if (!this.compareTime(commonConst.SLEEP_EARLY_TIME, commonConst.SLEEP_LATE_TIME)) {return}this.settingParams.targetValue = this.currentTimereturn}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: TaskListItembuild() {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_TIMEthis.controller.close()})Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor')).onClick(() => {this.settingParams.startTime = this.currentTimethis.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: stringsetFrequency() {const checkedArr = this.frequencyChooseRange.filter((item: FrequencyContentType) => item.isChecked)if (checkedArr.length === this.frequencyChooseRange.length || checkedArr.length === commonConst.NO_LENGTH) {this.currentFrequency = commonConst.EVERYDAYthis.settingParams.frequency = commonConst.INIT_WEEK_IDSreturn}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.currentFrequencythis.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.etsimport { 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 = truebuild() {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.etsimport { 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 = falseprivate 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 = valueconsole.info('select current date is: ' + value.toString())})}.width('100%')}
}
TextPicker
示例1(設置選擇器列數)
該示例通過配置range實現單列或多列文本選擇器。
// ets/pages/picker/TextPicker01Page.etsclass bottom {bottom: number = 50
}let bott: bottom = new bottom()@Entry
@Component
struct TextPicker01Page {private select: number = 1private 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 = 1private 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 = 1private 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 = 1private 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 = 1private 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 = falseprivate 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%')}
}