目录
[TaskInfo 枚举模型设置](#TaskInfo 枚举模型设置)
[定义 BroadCast](#定义 BroadCast)
[定义弹窗 builder 组件](#定义弹窗 builder 组件)
案例效果
资源文件与初始化
string.json
javascript
{
"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
javascript
{
"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
TypeScript
// ets/common/contants/CommonConstant.ets
export const THOUSANDTH_80: string = '8%'
export const THOUSANDTH_100: string = '10%'
export const THOUSANDTH_400: string = '40%'
export const THOUSANDTH_500: string = '50%'
export const THOUSANDTH_560: string = '56%'
export const THOUSANDTH_800: string = '80%'
export const THOUSANDTH_900: string = '90%'
export const THOUSANDTH_940: string = '94%'
export const THOUSANDTH_1000: string = '100%'
export const DEFAULT_2: number = 2
export const DEFAULT_8: number = 8
export const DEFAULT_12: number = 12
export const DEFAULT_10: number = 10
export const DEFAULT_16: number = 16
export const DEFAULT_18: number = 18
export const DEFAULT_20: number = 20
export const DEFAULT_24: number = 24
export const DEFAULT_28: number = 28
export const DEFAULT_32: number = 32
export const DEFAULT_48: number = 48
export const DEFAULT_56: number = 56
export const DEFAULT_60: number = 60
export const LIST_ITEM_SPACE: number = 2
export const ADD_TASK_TITLE: string = '添加任务'
export const EDIT_TASK_TITLE: string = '编辑任务'
export const SETTING_FINISHED_MESSAGE = '设置完成!!!'
export const CHOOSE_TIME_OUT_RANGE: string = '选择时间超出范围'
export const TODAY: string = new Date().toDateString()
export const DEFAULT_TIME: string = '08:00'
export const GET_UP_TIME_RANGE: string = '(06:00 - 09:00)'
export const SLEEP_TIME_RANGE: string = '(20:00 - 23:00)'
export const GET_UP_EARLY_TIME: string = '06:00'
export const GET_UP_LATE_TIME: string = '09:00'
export const SLEEP_EARLY_TIME: string = '20:00'
export const SLEEP_LATE_TIME: string = '23:00'
export const DEFAULT_SELECTED_TIME: Date = new Date(`${TODAY} 8:00:00`)
export const EVERYDAY: string = '每天'
export const NO_LENGTH: number = 0
export const INIT_WEEK_IDS: string = '1, 2, 3, 4, 5, 6, 7'
export const PER_DAY: string = '/ 天'
export const ZERO: number = 0
export const MINUS_20: number = -20
export const HAS_NO_INDEX: number = -1
export const DRINK_STEP: number = 25
export const DRINK_MAX_RANGE: number = 500
export const TIMES_100: number = 100
export const EAT_APPLE_RANGE: number = 100
export const DEFAULT_TEXT: string = '0.25'
export const DEFAULT_APPLE: string = '1'
添加任务
首页组件
TypeScript
// ets/pages/Index.ets
import TaskList from '../view/TaskList'
import { TaskListItem, TaskInitList } from '../model/TaskInitList'
import { THOUSANDTH_1000, ADD_TASK_TITLE } from '../common/constants/CommonConstant'
@Entry
@Component
struct Index {
@Provide taskList: TaskListItem[] = TaskInitList
build() {
Row() {
Navigation() {
Column() {
TaskList()
}
.width(THOUSANDTH_1000)
.justifyContent(FlexAlign.Center)
}
.size({ width: THOUSANDTH_1000, height: THOUSANDTH_1000 })
.title(ADD_TASK_TITLE)
.titleMode(NavigationTitleMode.Mini)
}
.backgroundColor($r('app.color.primaryBgColor'))
.height(THOUSANDTH_1000)
}
}
任务列表初始化
TypeScript
// 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'
}
]
任务列表视图
TypeScript
// ets/view/TaskList.ets
import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
@Component
export default struct TaskList {
@Consume taskList: TaskListItem[]
build() {
List({ space: commonConst.LIST_ITEM_SPACE }) {
ForEach(this.taskList, (item: TaskListItem) => {
ListItem() {
Row() {
Row() {
Image(item?.icon)
.width(commonConst.DEFAULT_24)
.height(commonConst.DEFAULT_24)
.margin({ right: commonConst.DEFAULT_8 })
Text(item?.taskName)
.fontSize(commonConst.DEFAULT_20)
.fontColor($r('app.color.titleColor'))
}
.width(commonConst.THOUSANDTH_500)
Blank()
.layoutWeight(1)
if (item?.isOpen) {
Text($r('app.string.already_open'))
.fontSize(commonConst.DEFAULT_16)
.flexGrow(1)
.align(Alignment.End)
.margin({ right: commonConst.DEFAULT_8 })
.fontColor($r('app.color.titleColor'))
}
Image($r('app.media.right_grey'))
.width(commonConst.DEFAULT_8)
.height(commonConst.DEFAULT_16)
}
.width(commonConst.THOUSANDTH_1000)
.justifyContent(FlexAlign.SpaceBetween)
.padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}
.height(commonConst.THOUSANDTH_80)
.borderRadius(commonConst.DEFAULT_12)
.backgroundColor($r('app.color.white'))
})
}
.height(commonConst.THOUSANDTH_1000)
.width(commonConst.THOUSANDTH_940)
}
}
任务编辑页
添加跳转
TypeScript
// ets/view/TaskList.ets
import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import { router } from '@kit.ArkUI'
import { formatParams } from '../viewModel/TaskTargetSetting'
@Component
export default struct TaskList {
@Consume taskList: TaskListItem[]
build() {
List({ space: commonConst.LIST_ITEM_SPACE }) {
ForEach(this.taskList, (item: TaskListItem) => {
ListItem() {
Row() {
Row() {
Image(item?.icon)
.width(commonConst.DEFAULT_24)
.height(commonConst.DEFAULT_24)
.margin({ right: commonConst.DEFAULT_8 })
Text(item?.taskName)
.fontSize(commonConst.DEFAULT_20)
.fontColor($r('app.color.titleColor'))
}
.width(commonConst.THOUSANDTH_500)
Blank()
.layoutWeight(1)
if (item?.isOpen) {
Text($r('app.string.already_open'))
.fontSize(commonConst.DEFAULT_16)
.flexGrow(1)
.align(Alignment.End)
.margin({ right: commonConst.DEFAULT_8 })
.fontColor($r('app.color.titleColor'))
}
Image($r('app.media.right_grey'))
.width(commonConst.DEFAULT_8)
.height(commonConst.DEFAULT_16)
}
.width(commonConst.THOUSANDTH_1000)
.justifyContent(FlexAlign.SpaceBetween)
.padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}
.height(commonConst.THOUSANDTH_80)
.borderRadius(commonConst.DEFAULT_12)
.backgroundColor($r('app.color.white'))
// 1. 添加链接
.onClick(() => {
router.pushUrl({
url: 'pages/TaskEditPage',
params: {
params: formatParams(item)
}
})
})
})
}
.height(commonConst.THOUSANDTH_1000)
.width(commonConst.THOUSANDTH_940)
}
}
任务目标设置模型(formatParams)
TypeScript
// ets/viewModel/TaskTargetSetting
import { TaskListItem } from '../model/TaskInitList'
export const formatParams = (params: TaskListItem) => {
return JSON.stringify(params)
}
编辑页面
TypeScript
// ets/pages/TaskEditPage.ets
import { THOUSANDTH_1000, EDIT_TASK_TITLE } from '../common/constants/CommonConstant'
import TaskDetail from '../view/TaskDetail'
@Entry
@Component
struct TaskEdit {
build() {
Row() {
Navigation() {
Column() {
TaskDetail()
}
.width(THOUSANDTH_1000)
.height(THOUSANDTH_1000)
}
.size({ width: THOUSANDTH_1000, height: THOUSANDTH_1000 })
.title(EDIT_TASK_TITLE)
.titleMode(NavigationTitleMode.Mini)
}
.height(THOUSANDTH_1000)
.backgroundColor($r('app.color.primaryBgColor'))
}
}
详情页
TypeScript
// ets/view/TaskDetail.ets
import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import {
TaskChooseItem
} from './TaskEditListItem'
import { router } from '@kit.ArkUI'
@Styles
function listItemStyle() {
.backgroundColor($r('app.color.white'))
.height(commonConst.DEFAULT_56)
.borderRadius(commonConst.DEFAULT_10)
.padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}
@Component
export default struct TaskDetail {
@Provide settingParams: TaskListItem = this.parseRouterParams()
parseRouterParams() {
let params = router.getParams() as Record<string, Object>
const routerParams: TaskListItem = JSON.parse(params.params as string)
return routerParams
}
build() {
Column() {
List({ space: commonConst.LIST_ITEM_SPACE }) {
ListItem() {
TaskChooseItem()
}
.listItemStyle()
}
}
.width(commonConst.THOUSANDTH_1000)
}
}
任务编辑列表项
TypeScript
// ets/view/TaskEditListItem.ets
import { TaskListItem } from '../model/TaskInitList'
import {
DEFAULT_20,
DEFAULT_32,
DEFAULT_56,
THOUSANDTH_1000,
} from '../common/constants/CommonConstant'
@Component
export struct TaskChooseItem {
@Consume settingParams: TaskListItem
build() {
Row() {
Text(this.settingParams.taskName).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
Toggle({ type: ToggleType.Switch, isOn: this.settingParams.isOpen })
.width(DEFAULT_56)
.height(DEFAULT_32)
.selectedColor($r('app.color.blueColor'))
.onChange((isOn: boolean) => {
this.settingParams.isOpen = isOn;
})
}
.width(THOUSANDTH_1000)
.justifyContent(FlexAlign.SpaceBetween)
}
}
目标设置展示
引入目标设置
TypeScript
// ets/view/TaskDetail.ets
import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import {
TaskChooseItem,
// 2. 引入TargetSetItem
TargetSetItem
} from './TaskEditListItem'
import { router } from '@kit.ArkUI'
import { taskType } from '../model/TaskInfo'
@Styles
function listItemStyle() {
.backgroundColor($r('app.color.white'))
.height(commonConst.DEFAULT_56)
.borderRadius(commonConst.DEFAULT_10)
.padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}
@Component
export default struct TaskDetail {
@Provide settingParams: TaskListItem = this.parseRouterParams()
parseRouterParams() {
let params = router.getParams() as Record<string, Object>
const routerParams: TaskListItem = JSON.parse(params.params as string)
return routerParams
}
build() {
Column() {
List({ space: commonConst.LIST_ITEM_SPACE }) {
ListItem() {
TaskChooseItem()
}
.listItemStyle()
// 1. 目标设置入口
ListItem() {
TargetSetItem()
}
.listItemStyle()
.enabled(
this.settingParams?.isOpen &&
(this.settingParams?.taskID !== taskType.smile) &&
(this.settingParams?.taskID !== taskType.brushTeeth)
)
}
.width(commonConst.THOUSANDTH_940)
}
.width(commonConst.THOUSANDTH_1000)
}
}
目标设置展示实现
TypeScript
// ets/view/TaskEditListItem.ets
import { TaskListItem } from '../model/TaskInitList'
import {
DEFAULT_16,
DEFAULT_20,
DEFAULT_32,
DEFAULT_56,
DEFAULT_8,
PER_DAY,
THOUSANDTH_1000,
} from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'
// 2.定义公共样式targetSetCommon
@Extend(Text)
function targetSetCommon() {
.fontSize(DEFAULT_16)
.flexGrow(1)
.margin({ right: DEFAULT_8 })
.align(Alignment.End)
}
// 2.定义公共样式targetSettingStyle
@Extend(Text)
function targetSettingStyle(isOpen: boolean, taskID: number) {
.fontColor(isOpen && taskID !== taskType.smile && taskID !== taskType.brushTeeth ?
$r('app.color.titleColor') :
$r('app.color.disabledColor'))
}
@Component
export struct TaskChooseItem {
@Consume settingParams: TaskListItem
build() {
Row() {
Text(this.settingParams.taskName).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
Toggle({ type: ToggleType.Switch, isOn: this.settingParams.isOpen })
.width(DEFAULT_56)
.height(DEFAULT_32)
.selectedColor($r('app.color.blueColor'))
.onChange((isOn: boolean) => {
this.settingParams.isOpen = isOn;
})
}
.width(THOUSANDTH_1000)
.justifyContent(FlexAlign.SpaceBetween)
}
}
// 1. 定义TargetSetItem组件
@Component
export struct TargetSetItem {
@Consume settingParams: TaskListItem;
build() {
Row() {
Text($r('app.string.target_setting'))
.fontSize(DEFAULT_20)
.fontWeight(FontWeight.Medium)
Blank()
.layoutWeight(1)
if (this.settingParams?.unit === '') {
Text(`${this.settingParams?.targetValue}`)
.targetSetCommon()
.targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
} else {
Text(`${this.settingParams?.targetValue} ${this.settingParams?.unit} ${PER_DAY}`)
.targetSetCommon()
.targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
}
Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16);
}
.width(THOUSANDTH_1000)
.justifyContent(FlexAlign.SpaceBetween)
}
}
TaskInfo 枚举模型设置
TypeScript
// ets/model/TaskInfo.ets
export enum taskType {
'getup' = 1,
'drinkWater',
'eatApple',
'smile',
'brushTeeth',
'sleepEarly'
}
弹窗构造逻辑
定义单击事件
TypeScript
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
TypeScript
// 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'
}
定义弹窗视图
TypeScript
// ets/view/CustomDialogView.ets
import { TargetSettingDialog, RemindTimeDialog, FrequencyDialog } from './TaskSettingDialog'
import { BroadCast, BroadCastType } from '../common/utils/BroadCast'
import { ZERO, MINUS_20 } from '../common/constants/CommonConstant'
@Component
export struct CustomDialogView {
@State isShow: boolean = false
@Provide achievementLevel: number = 3
@Consume broadCast: BroadCast
targetSettingDialogController: CustomDialogController = new CustomDialogController({
builder: TargetSettingDialog(),
autoCancel: true,
alignment: DialogAlignment.Bottom,
offset: { dx: ZERO, dy: MINUS_20 }
})
RemindTimeDialogController: CustomDialogController = new CustomDialogController({
builder: RemindTimeDialog(),
autoCancel: true,
alignment: DialogAlignment.Bottom,
offset: { dx: ZERO, dy: MINUS_20 }
});
FrequencyDialogController: CustomDialogController = new CustomDialogController({
builder: FrequencyDialog(),
autoCancel: true,
alignment: DialogAlignment.Bottom,
offset: { dx: ZERO, dy: MINUS_20 }
})
aboutToAppear() {
let self = this
this.broadCast.on(
BroadCastType.SHOW_TARGET_SETTING_DIALOG,
() => {
self.targetSettingDialogController.open()
})
this.broadCast.on(
BroadCastType.SHOW_REMIND_TIME_DIALOG,
() => {
self.RemindTimeDialogController.open()
})
this.broadCast.on(
BroadCastType.SHOW_FREQUENCY_DIALOG,
() => {
self.FrequencyDialogController.open()
})
}
build() {
}
}
定义弹窗 builder 组件
TypeScript
// ets/view/TaskSettingDialog.ets
import * as commonConst from '../common/constants/CommonConstant'
@CustomDialog
export struct TargetSettingDialog {
controller: CustomDialogController = new CustomDialogController({
builder: TargetSettingDialog()
})
build() {
Column() {
Text('target setting dialog')
}
.justifyContent(FlexAlign.Center)
.height(commonConst.THOUSANDTH_560)
.padding(commonConst.DEFAULT_12)
}
}
@CustomDialog
export struct RemindTimeDialog {
controller: CustomDialogController = new CustomDialogController({
builder: RemindTimeDialog()
})
build() {
Column() {
Text('remind time dialog')
}
.justifyContent(FlexAlign.Center)
.height(commonConst.THOUSANDTH_560)
.padding(commonConst.DEFAULT_12)
}
}
@CustomDialog
export struct FrequencyDialog {
controller: CustomDialogController = new CustomDialogController({
builder: FrequencyDialog()
})
build() {
Column() {
Text('frequency dialog')
}
.justifyContent(FlexAlign.Center)
.height(commonConst.THOUSANDTH_560)
.padding(commonConst.DEFAULT_12)
}
}
目标设置弹窗实现
目标设置窗口逻辑
TypeScript
// ets/view/TaskSettingDialog.ets
import * as commonConst from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'
import { TaskListItem } from '../model/TaskInitList'
import { createAppleRange, createDrinkRange, formatTime, returnTimeStamp } from '../viewModel/TaskTargetSetting'
import { promptAction } from '@kit.ArkUI'
@CustomDialog
export struct TargetSettingDialog {
controller: CustomDialogController = new CustomDialogController({
builder: TargetSettingDialog()
})
@Consume settingParams: TaskListItem
currentTime: string = commonConst.DEFAULT_TIME
currentValue: string = this.settingParams.taskID === taskType.drinkWater ? commonConst.DEFAULT_TEXT :
commonConst.DEFAULT_APPLE
drinkRange: string[] = createDrinkRange()
appleRange: string[] = createAppleRange()
createSubTitle() {
if (this.settingParams.taskID === taskType.getup) {
return commonConst.GET_UP_TIME_RANGE
}
if (this.settingParams.taskID === taskType.sleepEarly) {
return commonConst.SLEEP_TIME_RANGE
}
return ''
}
setTargetValue() {
if (this.settingParams.taskID === taskType.getup) {
if (!this.compareTime(commonConst.GET_UP_EARLY_TIME, commonConst.GET_UP_LATE_TIME)) {
return
}
this.settingParams.targetValue = this.currentTime
return
}
if (this.settingParams.taskID === taskType.sleepEarly) {
if (!this.compareTime(commonConst.SLEEP_EARLY_TIME, commonConst.SLEEP_LATE_TIME)) {
return
}
this.settingParams.targetValue = this.currentTime
return
}
this.settingParams.targetValue = this.currentValue
}
compareTime(startTime: string, endTime: string) {
if (returnTimeStamp(this.currentTime) < returnTimeStamp(startTime) ||
returnTimeStamp(this.currentTime) > returnTimeStamp(endTime)) {
promptAction.showToast({
message: commonConst.CHOOSE_TIME_OUT_RANGE
})
return false
}
return true
}
build() {
Column() {
Row() {
Text($r('app.string.target_setting')).fontSize(commonConst.DEFAULT_20).margin({ right: commonConst.DEFAULT_12 })
Text(this.createSubTitle())
.fontSize(commonConst.DEFAULT_16)
}
.width(commonConst.THOUSANDTH_1000)
.justifyContent(FlexAlign.Start)
if ([taskType.getup, taskType.sleepEarly].indexOf(this.settingParams.taskID) > commonConst.HAS_NO_INDEX) {
TimePicker({
selected: commonConst.DEFAULT_SELECTED_TIME
})
.height(commonConst.THOUSANDTH_800)
.useMilitaryTime(true)
.onChange((value: TimePickerResult) => {
this.currentTime = formatTime(value)
})
} else {
TextPicker({ range: this.settingParams.taskID === taskType.drinkWater ? this.drinkRange : this.appleRange,
value: this.settingParams.targetValue })
.width(commonConst.THOUSANDTH_900)
.height(commonConst.THOUSANDTH_800)
.onChange((value: string | string[]) => {
this.currentValue = (value as string)?.split(' ')[0]
})
}
Row() {
Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
.onClick(() => {
this.currentTime = commonConst.DEFAULT_TIME
this.currentValue = commonConst.DEFAULT_TEXT
this.controller.close()
})
Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
.onClick(() => {
this.setTargetValue()
this.controller.close()
})
}
.justifyContent(FlexAlign.SpaceAround)
.width(commonConst.THOUSANDTH_1000)
.height(commonConst.DEFAULT_28)
.margin({ bottom: commonConst.DEFAULT_20 })
}
.justifyContent(FlexAlign.Center)
.height(commonConst.THOUSANDTH_560)
.padding(commonConst.DEFAULT_12)
}
}
@CustomDialog
export struct RemindTimeDialog {
controller: CustomDialogController = new CustomDialogController({
builder: RemindTimeDialog()
})
build() {
Column() {
Text('remind time dialog')
}
.justifyContent(FlexAlign.Center)
.height(commonConst.THOUSANDTH_560)
.padding(commonConst.DEFAULT_12)
}
}
@CustomDialog
export struct FrequencyDialog {
controller: CustomDialogController = new CustomDialogController({
builder: FrequencyDialog()
})
build() {
Column() {
Text('frequency dialog')
}
.justifyContent(FlexAlign.Center)
.height(commonConst.THOUSANDTH_560)
.padding(commonConst.DEFAULT_12)
}
}
任务目标设置视图模型
TypeScript
// ets/TaskTargetSetting.ets
import { DRINK_MAX_RANGE, DRINK_STEP, EAT_APPLE_RANGE, TIMES_100, TODAY } from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import { padTo2Digits } from './FrequencySetting'
export const formatParams = (params: TaskListItem) => {
return JSON.stringify(params)
}
export const formatTime = (value: TimePickerResult) => {
let hour: number = 0
let minute: number = 0
if (value.hour !== undefined && value.minute !== undefined) {
hour = value.hour
minute = value.minute
}
return `${padTo2Digits(hour)}:${padTo2Digits(minute)}`
}
export const createDrinkRange = () => {
const drinkRangeArr: string[] = []
for (let i = DRINK_STEP; i <= DRINK_MAX_RANGE; i += DRINK_STEP) {
drinkRangeArr.push(`${i / TIMES_100} L`)
}
return drinkRangeArr
}
export const createAppleRange = () => {
const appleRangeArr: string[] = []
for (let i = 1; i <= EAT_APPLE_RANGE; i++) {
appleRangeArr.push(`${i} 个`)
}
return appleRangeArr
}
export const returnTimeStamp = (currentTime: string) => {
const timeString = `${TODAY} ${currentTime}`
return new Date(timeString).getTime()
}
频率设置视图模型
TypeScript
// ets/viewModel/FrequencySetting.ets
export function padTo2Digits(num: number) {
return num.toString().padStart(2, '0')
}
时间提醒弹窗实现
更新TaskDetail
TypeScript
// ets/view/TaskDetail.ets
import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import {
TaskChooseItem,
TargetSetItem,
// 3. 导入模块
OpenRemindItem,
RemindTimeItem
} from './TaskEditListItem'
import { router } from '@kit.ArkUI'
import { taskType } from '../model/TaskInfo'
import { BroadCast, BroadCastType } from '../common/utils/BroadCast'
import { CustomDialogView } from './CustomDialogView'
@Styles
function listItemStyle() {
.backgroundColor($r('app.color.white'))
.height(commonConst.DEFAULT_56)
.borderRadius(commonConst.DEFAULT_10)
.padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}
@Component
export default struct TaskDetail {
@Provide settingParams: TaskListItem = this.parseRouterParams()
@Provide broadCast: BroadCast = new BroadCast()
parseRouterParams() {
let params = router.getParams() as Record<string, Object>
const routerParams: TaskListItem = JSON.parse(params.params as string)
return routerParams
}
aboutToAppear() {
this.broadCast.off()
}
build() {
Column() {
List({ space: commonConst.LIST_ITEM_SPACE }) {
ListItem() {
TaskChooseItem()
}
.listItemStyle()
ListItem() {
TargetSetItem()
}
.listItemStyle()
.enabled(
this.settingParams?.isOpen &&
(this.settingParams?.taskID !== taskType.smile) &&
(this.settingParams?.taskID !== taskType.brushTeeth)
)
.onClick(() => {
this.broadCast.emit(
BroadCastType.SHOW_TARGET_SETTING_DIALOG)
})
// 1.构造编辑列表相应内容
ListItem() {
OpenRemindItem()
}
.listItemStyle()
.enabled(this.settingParams.isOpen)
ListItem() {
// 2.构造编辑列表相应内容
RemindTimeItem()
}
.listItemStyle()
.onClick(() => {
this.broadCast.emit(
BroadCastType.SHOW_REMIND_TIME_DIALOG
)
})
ListItem() {
Text('频率')
}
.listItemStyle()
.onClick(() => {
this.broadCast.emit(
BroadCastType.SHOW_FREQUENCY_DIALOG
)
})
}
CustomDialogView()
}
.width(commonConst.THOUSANDTH_1000)
}
}
实现编辑任务列表的开启提醒与提醒时间
TypeScript
import { TaskListItem } from '../model/TaskInitList'
import {
DEFAULT_16,
DEFAULT_20,
DEFAULT_32,
DEFAULT_56,
DEFAULT_8,
PER_DAY,
THOUSANDTH_1000,
} from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'
@Extend(Text)
function targetSetCommon() {
.fontSize(DEFAULT_16)
.flexGrow(1)
.margin({ right: DEFAULT_8 })
.align(Alignment.End)
}
@Extend(Text)
function targetSettingStyle(isOpen: boolean, taskID: number) {
.fontColor(isOpen && taskID !== taskType.smile && taskID !== taskType.brushTeeth ?
$r('app.color.titleColor') :
$r('app.color.disabledColor'))
}
@Extend(Text)
function remindTimeStyle(isOpen: boolean, isAlarm: boolean) {
.fontColor(isOpen && isAlarm ? $r('app.color.titleColor') : $r('app.color.disabledColor'))
}
@Component
export struct TaskChooseItem {
@Consume settingParams: TaskListItem
build() {
Row() {
Text(this.settingParams.taskName).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
Toggle({ type: ToggleType.Switch, isOn: this.settingParams.isOpen })
.width(DEFAULT_56)
.height(DEFAULT_32)
.selectedColor($r('app.color.blueColor'))
.onChange((isOn: boolean) => {
this.settingParams.isOpen = isOn;
})
}
.width(THOUSANDTH_1000)
.justifyContent(FlexAlign.SpaceBetween)
}
}
@Component
export struct TargetSetItem {
@Consume settingParams: TaskListItem
build() {
Row() {
Text($r('app.string.target_setting'))
.fontSize(DEFAULT_20)
.fontWeight(FontWeight.Medium)
Blank()
.layoutWeight(1)
if (this.settingParams?.unit === '') {
Text(`${this.settingParams?.targetValue}`)
.targetSetCommon()
.targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
} else {
Text(`${this.settingParams?.targetValue} ${this.settingParams?.unit} ${PER_DAY}`)
.targetSetCommon()
.targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
}
Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16);
}
.width(THOUSANDTH_1000)
.justifyContent(FlexAlign.SpaceBetween)
}
}
// 1.实现开启提醒
@Component
export struct OpenRemindItem {
@Consume settingParams: TaskListItem
build() {
Row() {
Text($r('app.string.open_reminder'))
.fontSize(DEFAULT_20)
.fontWeight(FontWeight.Medium)
Blank()
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.settingParams?.isAlarm })
.width(DEFAULT_56)
.height(DEFAULT_32)
.selectedColor($r('app.color.blueColor'))
.onChange((isOn: boolean) => {
this.settingParams.isAlarm = isOn
})
}
.width(THOUSANDTH_1000)
.justifyContent(FlexAlign.SpaceBetween)
}
}
// 2.实现提醒时间
@Component
export struct RemindTimeItem {
@Consume settingParams: TaskListItem
build() {
Row() {
Text($r('app.string.remind_time')).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
Blank()
.layoutWeight(1)
Text(this.settingParams?.startTime)
.targetSetCommon()
.remindTimeStyle(this.settingParams.isOpen, this.settingParams.isAlarm)
Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16)
}
.width(THOUSANDTH_1000)
.justifyContent(FlexAlign.SpaceBetween)
}
}
实现时间提醒弹窗
TypeScript
import * as commonConst from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'
import { TaskListItem } from '../model/TaskInitList'
import { createAppleRange, createDrinkRange, formatTime, returnTimeStamp } from '../viewModel/TaskTargetSetting'
import { promptAction } from '@kit.ArkUI'
@CustomDialog
export struct TargetSettingDialog {
controller: CustomDialogController = new CustomDialogController({
builder: TargetSettingDialog()
})
@Consume settingParams: TaskListItem
currentTime: string = commonConst.DEFAULT_TIME
currentValue: string = this.settingParams.taskID === taskType.drinkWater ? commonConst.DEFAULT_TEXT :
commonConst.DEFAULT_APPLE
drinkRange: string[] = createDrinkRange()
appleRange: string[] = createAppleRange()
createSubTitle() {
if (this.settingParams.taskID === taskType.getup) {
return commonConst.GET_UP_TIME_RANGE
}
if (this.settingParams.taskID === taskType.sleepEarly) {
return commonConst.SLEEP_TIME_RANGE
}
return ''
}
setTargetValue() {
if (this.settingParams.taskID === taskType.getup) {
if (!this.compareTime(commonConst.GET_UP_EARLY_TIME, commonConst.GET_UP_LATE_TIME)) {
return
}
this.settingParams.targetValue = this.currentTime
return
}
if (this.settingParams?.taskID === taskType.sleepEarly) {
if (!this.compareTime(commonConst.SLEEP_EARLY_TIME, commonConst.SLEEP_LATE_TIME)) {
return
}
this.settingParams.targetValue = this.currentTime
return
}
this.settingParams.targetValue = this.currentValue
}
compareTime(startTime: string, endTime: string) {
if (returnTimeStamp(this.currentTime) < returnTimeStamp(startTime) ||
returnTimeStamp(this.currentTime) > returnTimeStamp(endTime)) {
promptAction.showToast({
message: commonConst.CHOOSE_TIME_OUT_RANGE
})
return false
}
return true
}
build() {
Column() {
Row() {
Text($r('app.string.target_setting')).fontSize(commonConst.DEFAULT_20).margin({ right: commonConst.DEFAULT_12 })
Text(this.createSubTitle())
.fontSize(commonConst.DEFAULT_16)
}
.width(commonConst.THOUSANDTH_1000)
.justifyContent(FlexAlign.Start)
if ([taskType.getup, taskType.sleepEarly].indexOf(this.settingParams.taskID) > commonConst.HAS_NO_INDEX) {
TimePicker({
selected: commonConst.DEFAULT_SELECTED_TIME
})
.height(commonConst.THOUSANDTH_800)
.useMilitaryTime(true)
.onChange((value: TimePickerResult) => {
this.currentTime = formatTime(value)
})
} else {
TextPicker({ range: this.settingParams?.taskID === taskType.drinkWater ? this.drinkRange : this.appleRange,
value: this.settingParams.targetValue })
.width(commonConst.THOUSANDTH_900)
.height(commonConst.THOUSANDTH_800)
.onChange((value: string | string[]) => {
this.currentValue = (value as string)?.split(' ')[0]
})
}
Row() {
Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
.onClick(() => {
this.currentTime = commonConst.DEFAULT_TIME;
this.currentValue = commonConst.DEFAULT_TEXT;
this.controller.close()
})
Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
.onClick(() => {
this.setTargetValue()
this.controller.close()
})
}
.justifyContent(FlexAlign.SpaceAround)
.width(commonConst.THOUSANDTH_1000)
.height(commonConst.DEFAULT_28)
.margin({ bottom: commonConst.DEFAULT_20 })
}
.justifyContent(FlexAlign.Center)
.height(commonConst.THOUSANDTH_560)
.padding(commonConst.DEFAULT_12)
}
}
// 实现时间提醒弹窗
@CustomDialog
export struct RemindTimeDialog {
controller: CustomDialogController = new CustomDialogController({
builder: RemindTimeDialog()
})
currentTime: string = commonConst.DEFAULT_TIME
@Consume settingParams: TaskListItem
build() {
Column() {
Column() {
Text($r('app.string.remind_time'))
.fontSize(commonConst.DEFAULT_20)
.margin({ top: commonConst.DEFAULT_10 })
.width(commonConst.THOUSANDTH_1000)
.textAlign(TextAlign.Start)
}
.width(commonConst.THOUSANDTH_900)
TimePicker({
selected: commonConst.DEFAULT_SELECTED_TIME
})
.height(commonConst.THOUSANDTH_800)
.useMilitaryTime(true)
.onChange((value: TimePickerResult) => {
this.currentTime = formatTime(value)
})
Row() {
Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
.onClick(() => {
this.currentTime = commonConst.DEFAULT_TIME
this.controller.close()
})
Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
.onClick(() => {
this.settingParams.startTime = this.currentTime
this.controller.close()
})
}
.justifyContent(FlexAlign.SpaceAround)
.width(commonConst.THOUSANDTH_1000)
.height(commonConst.DEFAULT_28)
.margin({ bottom: commonConst.DEFAULT_20 })
}
.justifyContent(FlexAlign.Center)
.height(commonConst.THOUSANDTH_560)
.padding(commonConst.DEFAULT_12)
}
}
@CustomDialog
export struct FrequencyDialog {
controller: CustomDialogController = new CustomDialogController({
builder: FrequencyDialog()
})
build() {
Column() {
Text('frequency dialog')
}
.justifyContent(FlexAlign.Center)
.height(commonConst.THOUSANDTH_560)
.padding(commonConst.DEFAULT_12)
}
}
频率弹窗和提交完成的实现
TypeScript
// ets/view/TaskDetail.ets
import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import {
TaskChooseItem,
TargetSetItem,
OpenRemindItem,
RemindTimeItem,
FrequencyItem
} from './TaskEditListItem'
import { promptAction, router } from '@kit.ArkUI'
import { taskType } from '../model/TaskInfo'
import { BroadCast, BroadCastType } from '../common/utils/BroadCast'
import { CustomDialogView } from './CustomDialogView'
@Styles
function listItemStyle() {
.backgroundColor($r('app.color.white'))
.height(commonConst.DEFAULT_56)
.borderRadius(commonConst.DEFAULT_10)
.padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}
@Component
export default struct TaskDetail {
@Provide settingParams: TaskListItem = this.parseRouterParams()
@Provide broadCast: BroadCast = new BroadCast()
@Provide frequency: string = commonConst.EVERYDAY
parseRouterParams() {
let params = router.getParams() as Record<string, Object>
const routerParams: TaskListItem = JSON.parse(params.params as string)
return routerParams
}
aboutToAppear() {
this.broadCast.off()
}
finishTaskEdit() {
promptAction.showToast({
message: commonConst.SETTING_FINISHED_MESSAGE
})
}
build() {
Column() {
List({ space: commonConst.LIST_ITEM_SPACE }) {
ListItem() {
TaskChooseItem()
}
.listItemStyle()
ListItem() {
TargetSetItem()
}
.listItemStyle()
.enabled(
this.settingParams?.isOpen &&
(this.settingParams?.taskID !== taskType.smile) &&
(this.settingParams?.taskID !== taskType.brushTeeth)
)
.onClick(() => {
this.broadCast.emit(
BroadCastType.SHOW_TARGET_SETTING_DIALOG)
})
ListItem() {
OpenRemindItem()
}
.listItemStyle()
.enabled(this.settingParams?.isOpen)
ListItem() {
RemindTimeItem()
}
.listItemStyle()
.onClick(() => {
this.broadCast.emit(
BroadCastType.SHOW_REMIND_TIME_DIALOG
)
})
// 1. 引入FrequencyItem
ListItem() {
FrequencyItem()
}
.listItemStyle()
.onClick(() => {
this.broadCast.emit(
BroadCastType.SHOW_FREQUENCY_DIALOG
)
})
}
.width(commonConst.THOUSANDTH_940)
// x. 最后实现完成按钮提交
Button() {
Text($r('app.string.complete')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
}
.width(commonConst.THOUSANDTH_800)
.height(commonConst.DEFAULT_48)
.backgroundColor($r('app.color.borderColor'))
.onClick(() => {
this.finishTaskEdit()
})
.position({
x: commonConst.THOUSANDTH_100,
y: commonConst.THOUSANDTH_800
})
CustomDialogView()
}
.width(commonConst.THOUSANDTH_1000)
}
}
实现频率任务项视图
TypeScript
// ets/view/TaskEditListItem.ets
import { TaskListItem } from '../model/TaskInitList'
import {
DEFAULT_12,
DEFAULT_16,
DEFAULT_20,
DEFAULT_32,
DEFAULT_56,
DEFAULT_8,
PER_DAY,
THOUSANDTH_1000,
} from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'
@Extend(Text)
function targetSetCommon() {
.fontSize(DEFAULT_16)
.flexGrow(1)
.margin({ right: DEFAULT_8 })
.align(Alignment.End)
}
@Extend(Text)
function targetSettingStyle(isOpen: boolean, taskID: number) {
.fontColor(isOpen && taskID !== taskType.smile && taskID !== taskType.brushTeeth ?
$r('app.color.titleColor') :
$r('app.color.disabledColor'))
}
@Extend(Text)
function remindTimeStyle(isOpen: boolean, isAlarm: boolean) {
.fontColor(isOpen && isAlarm ? $r('app.color.titleColor') : $r('app.color.disabledColor'))
}
@Extend(Text)
function frequencyStyle(isOpen: boolean) {
.fontSize(DEFAULT_12)
.flexGrow(1)
.margin({ right: DEFAULT_8 })
.textAlign(TextAlign.End)
.fontColor(isOpen ? $r('app.color.titleColor') : $r('app.color.disabledColor'))
}
@Component
export struct TaskChooseItem {
@Consume settingParams: TaskListItem
build() {
Row() {
Text(this.settingParams.taskName).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
Toggle({ type: ToggleType.Switch, isOn: this.settingParams.isOpen })
.width(DEFAULT_56)
.height(DEFAULT_32)
.selectedColor($r('app.color.blueColor'))
.onChange((isOn: boolean) => {
this.settingParams.isOpen = isOn;
})
}
.width(THOUSANDTH_1000)
.justifyContent(FlexAlign.SpaceBetween)
}
}
@Component
export struct TargetSetItem {
@Consume settingParams: TaskListItem
build() {
Row() {
Text($r('app.string.target_setting'))
.fontSize(DEFAULT_20)
.fontWeight(FontWeight.Medium)
Blank()
.layoutWeight(1)
if (this.settingParams?.unit === '') {
Text(`${this.settingParams?.targetValue}`)
.targetSetCommon()
.targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
} else {
Text(`${this.settingParams?.targetValue} ${this.settingParams?.unit} ${PER_DAY}`)
.targetSetCommon()
.targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
}
Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16);
}
.width(THOUSANDTH_1000)
.justifyContent(FlexAlign.SpaceBetween)
}
}
@Component
export struct OpenRemindItem {
@Consume settingParams: TaskListItem
build() {
Row() {
Text($r('app.string.open_reminder'))
.fontSize(DEFAULT_20)
.fontWeight(FontWeight.Medium)
Blank()
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.settingParams?.isAlarm })
.width(DEFAULT_56)
.height(DEFAULT_32)
.selectedColor($r('app.color.blueColor'))
.onChange((isOn: boolean) => {
this.settingParams.isAlarm = isOn
})
}
.width(THOUSANDTH_1000)
.justifyContent(FlexAlign.SpaceBetween)
}
}
@Component
export struct RemindTimeItem {
@Consume settingParams: TaskListItem
build() {
Row() {
Text($r('app.string.remind_time')).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
Blank()
.layoutWeight(1)
Text(this.settingParams?.startTime)
.targetSetCommon()
.remindTimeStyle(this.settingParams.isOpen, this.settingParams.isAlarm)
Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16)
}
.width(THOUSANDTH_1000)
.justifyContent(FlexAlign.SpaceBetween)
}
}
// 1. 实现频率任务项视图
@Component
export struct FrequencyItem {
@Consume settingParams: TaskListItem
@Consume frequency: string
build() {
Row() {
Text($r('app.string.frequency')).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
Text(this.frequency)
.targetSetCommon()
.frequencyStyle(this.settingParams.isOpen)
Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16)
}
.width(THOUSANDTH_1000)
.justifyContent(FlexAlign.SpaceBetween)
}
}
实现频率设置弹窗
TypeScript
// ets/view/TaskSettingDialog.ets
import * as commonConst from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'
import { FrequencyContentType, TaskListItem } from '../model/TaskInitList'
import { createAppleRange, createDrinkRange, formatTime, returnTimeStamp } from '../viewModel/TaskTargetSetting'
import { promptAction } from '@kit.ArkUI'
import { frequencyRange } from '../viewModel/FrequencySetting'
@CustomDialog
export struct TargetSettingDialog {
controller: CustomDialogController = new CustomDialogController({
builder: TargetSettingDialog()
})
@Consume settingParams: TaskListItem
currentTime: string = commonConst.DEFAULT_TIME
currentValue: string = this.settingParams.taskID === taskType.drinkWater ? commonConst.DEFAULT_TEXT :
commonConst.DEFAULT_APPLE
drinkRange: string[] = createDrinkRange()
appleRange: string[] = createAppleRange()
createSubTitle() {
if (this.settingParams.taskID === taskType.getup) {
return commonConst.GET_UP_TIME_RANGE
}
if (this.settingParams.taskID === taskType.sleepEarly) {
return commonConst.SLEEP_TIME_RANGE
}
return ''
}
setTargetValue() {
if (this.settingParams.taskID === taskType.getup) {
if (!this.compareTime(commonConst.GET_UP_EARLY_TIME, commonConst.GET_UP_LATE_TIME)) {
return
}
this.settingParams.targetValue = this.currentTime
return
}
if (this.settingParams?.taskID === taskType.sleepEarly) {
if (!this.compareTime(commonConst.SLEEP_EARLY_TIME, commonConst.SLEEP_LATE_TIME)) {
return
}
this.settingParams.targetValue = this.currentTime
return
}
this.settingParams.targetValue = this.currentValue
}
compareTime(startTime: string, endTime: string) {
if (returnTimeStamp(this.currentTime) < returnTimeStamp(startTime) ||
returnTimeStamp(this.currentTime) > returnTimeStamp(endTime)) {
promptAction.showToast({
message: commonConst.CHOOSE_TIME_OUT_RANGE
})
return false
}
return true
}
build() {
Column() {
Row() {
Text($r('app.string.target_setting')).fontSize(commonConst.DEFAULT_20).margin({ right: commonConst.DEFAULT_12 })
Text(this.createSubTitle())
.fontSize(commonConst.DEFAULT_16)
}
.width(commonConst.THOUSANDTH_1000)
.justifyContent(FlexAlign.Start)
if ([taskType.getup, taskType.sleepEarly].indexOf(this.settingParams.taskID) > commonConst.HAS_NO_INDEX) {
TimePicker({
selected: commonConst.DEFAULT_SELECTED_TIME
})
.height(commonConst.THOUSANDTH_800)
.useMilitaryTime(true)
.onChange((value: TimePickerResult) => {
this.currentTime = formatTime(value)
})
} else {
TextPicker({ range: this.settingParams?.taskID === taskType.drinkWater ? this.drinkRange : this.appleRange,
value: this.settingParams.targetValue })
.width(commonConst.THOUSANDTH_900)
.height(commonConst.THOUSANDTH_800)
.onChange((value: string | string[]) => {
this.currentValue = (value as string)?.split(' ')[0]
})
}
Row() {
Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
.onClick(() => {
this.currentTime = commonConst.DEFAULT_TIME;
this.currentValue = commonConst.DEFAULT_TEXT;
this.controller.close()
})
Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
.onClick(() => {
this.setTargetValue()
this.controller.close()
})
}
.justifyContent(FlexAlign.SpaceAround)
.width(commonConst.THOUSANDTH_1000)
.height(commonConst.DEFAULT_28)
.margin({ bottom: commonConst.DEFAULT_20 })
}
.justifyContent(FlexAlign.Center)
.height(commonConst.THOUSANDTH_560)
.padding(commonConst.DEFAULT_12)
}
}
@CustomDialog
export struct RemindTimeDialog {
controller: CustomDialogController = new CustomDialogController({
builder: RemindTimeDialog()
})
currentTime: string = commonConst.DEFAULT_TIME
@Consume settingParams: TaskListItem
build() {
Column() {
Column() {
Text($r('app.string.remind_time'))
.fontSize(commonConst.DEFAULT_20)
.margin({ top: commonConst.DEFAULT_10 })
.width(commonConst.THOUSANDTH_1000)
.textAlign(TextAlign.Start)
}
.width(commonConst.THOUSANDTH_900)
TimePicker({
selected: commonConst.DEFAULT_SELECTED_TIME
})
.height(commonConst.THOUSANDTH_800)
.useMilitaryTime(true)
.onChange((value: TimePickerResult) => {
this.currentTime = formatTime(value);
})
Row() {
Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
.onClick(() => {
this.currentTime = commonConst.DEFAULT_TIME
this.controller.close()
})
Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
.onClick(() => {
this.settingParams.startTime = this.currentTime
this.controller.close()
})
}
.justifyContent(FlexAlign.SpaceAround)
.width(commonConst.THOUSANDTH_1000)
.height(commonConst.DEFAULT_28)
.margin({ bottom: commonConst.DEFAULT_20 })
}
.justifyContent(FlexAlign.Center)
.height(commonConst.THOUSANDTH_560)
.padding(commonConst.DEFAULT_12)
}
}
// 1.实现频率设置弹窗
@CustomDialog
export struct FrequencyDialog {
controller: CustomDialogController = new CustomDialogController({
builder: FrequencyDialog()
})
private frequencyChooseRange: FrequencyContentType[] = frequencyRange()
private currentFrequency: string = commonConst.EVERYDAY
@Consume settingParams: TaskListItem
@Consume frequency: string
setFrequency() {
const checkedArr = this.frequencyChooseRange.filter((item: FrequencyContentType) => item.isChecked)
if (checkedArr.length === this.frequencyChooseRange.length || checkedArr.length === commonConst.NO_LENGTH) {
this.currentFrequency = commonConst.EVERYDAY
this.settingParams.frequency = commonConst.INIT_WEEK_IDS
return
}
this.currentFrequency = checkedArr.reduce((sum: string, current: FrequencyContentType) => {
return sum + ' ' + current.label
}, '')
this.settingParams.frequency = checkedArr.reduce((sum: string, current: FrequencyContentType) => {
return sum === '' ? sum + current.id : sum + ',' + current.id
}, '')
}
build() {
Column() {
Column() {
Text($r('app.string.set_your_frequency'))
.fontSize(commonConst.DEFAULT_20)
.margin({ top: commonConst.DEFAULT_10 })
.width(commonConst.THOUSANDTH_1000)
.textAlign(TextAlign.Start)
}
.width(commonConst.THOUSANDTH_900)
List() {
ForEach(this.frequencyChooseRange, (item: FrequencyContentType) => {
ListItem() {
Row() {
Text(item?.label).fontSize(commonConst.DEFAULT_20)
Toggle({ type: ToggleType.Checkbox })
.onChange((isOn: boolean) => {
item.isChecked = isOn
})
}
.width(commonConst.THOUSANDTH_1000)
.justifyContent(FlexAlign.SpaceBetween)
.height(commonConst.DEFAULT_60)
}
})
}
.divider({
strokeWidth: commonConst.DEFAULT_2,
color: $r('app.color.btnBgColor')
})
.flexGrow(1)
.padding(commonConst.DEFAULT_12)
.width(commonConst.THOUSANDTH_1000)
Row() {
Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
.onClick(() => {
this.controller.close()
})
Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
.onClick(() => {
this.setFrequency()
this.frequency = this.currentFrequency
this.controller.close()
})
}
.justifyContent(FlexAlign.SpaceAround)
.width(commonConst.THOUSANDTH_900)
.height(commonConst.DEFAULT_28)
.margin({ bottom: commonConst.DEFAULT_16 })
}
.justifyContent(FlexAlign.Center)
.height(commonConst.THOUSANDTH_940)
.padding(commonConst.DEFAULT_12)
}
}
定义频率设置视图模型
TypeScript
// 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通过调用接口来创建,接口调用形式如下:
TypeScript
Toggle(options: { type: ToggleType, isOn?: boolean })
其中,ToggleType为开关类型,包括Button、Checkbox和Switch,isOn为切换按钮的状态。
API version 11开始,Checkbox默认样式由圆角方形变为圆形。
接口调用有以下两种形式:
- 创建不包含子组件的Toggle。
当ToggleType为Checkbox或者Switch时,用于创建不包含子组件的Toggle:
TypeScript
Toggle({ type: ToggleType.Checkbox, isOn: false })
Toggle({ type: ToggleType.Checkbox, isOn: true })

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

- 创建包含子组件的Toggle。
当ToggleType为Button时,只能包含一个子组件,如果子组件有文本设置,则相应的文本内容会显示在按钮上。
TypeScript
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打开选中后的背景颜色。
TypeScript
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生效。
TypeScript
Toggle({ type: ToggleType.Switch, isOn: false })
.switchPointColor(Color.Pink)
Toggle({ type: ToggleType.Switch, isOn: true })
.switchPointColor(Color.Pink)

添加事件
除支持通用事件外,Toggle还用于选中和取消选中后触发某些操作,可以绑定onChange事件来响应操作后的自定义行为。
TypeScript
Toggle({ type: ToggleType.Switch, isOn: false })
.onChange((isOn: boolean) => {
if(isOn) {
// 需要执行的操作
}
})
案例整理
TypeScript
// ets/pages/toggle/usagePage.ets
@Entry
@Component
struct usagePage {
@State isOn: boolean = true
build() {
Column({ space: 20 }) {
Row() {
Toggle({ type: ToggleType.Checkbox, isOn: false })
Toggle({ type: ToggleType.Checkbox, isOn: true })
}
Row() {
Toggle({ type: ToggleType.Switch, isOn: false })
Toggle({ type: ToggleType.Switch, isOn: true })
}
Row() {
Toggle({ type: ToggleType.Button, isOn: false }) {
Text('status button')
.fontColor('#182431')
.fontSize(12)
}.width(100)
Toggle({ type: ToggleType.Button, isOn: true }) {
Text('status button')
.fontColor('#182431')
.fontSize(12)
}.width(100)
}
Row() {
Toggle({ type: ToggleType.Button, isOn: true }) {
Text('status button')
.fontColor('#182431')
.fontSize(12)
}.width(100).selectedColor(Color.Orange)
Toggle({ type: ToggleType.Checkbox, isOn: true })
.selectedColor(Color.Orange)
Toggle({ type: ToggleType.Switch, isOn: true })
.selectedColor(Color.Orange)
}
Row() {
Toggle({ type: ToggleType.Switch, isOn: false })
.switchPointColor(Color.Orange)
Toggle({ type: ToggleType.Switch, isOn: true })
.switchPointColor(Color.Orange)
}
Row() {
Toggle({ type: ToggleType.Switch, isOn: this.isOn })
.onChange((isOn: boolean) => {
this.isOn = !this.isOn
})
Text(`${this.isOn}`)
}
}
.width('100%')
}
}
场景示例
Toggle用于切换蓝牙开关状态。
TypeScript
// ets/pages/toggle/CasePage.ets
import { promptAction } from '@kit.ArkUI';
@Entry
@Component
struct CasePage {
@State BOnSt: promptAction.ShowToastOptions = { 'message': 'Bluetooth is on.' }
@State BOffSt: promptAction.ShowToastOptions = { 'message': 'Bluetooth is off.' }
build() {
Column() {
Row() {
Text("Bluetooth Mode")
.height(50)
.fontSize(16)
}
Row() {
Text("Bluetooth")
.height(50)
.padding({ left: 10 })
.fontSize(16)
.textAlign(TextAlign.Start)
.backgroundColor(0xFFFFFF)
Toggle({ type: ToggleType.Switch })
.margin({ left: 200, right: 10 })
.onChange((isOn: boolean) => {
if (isOn) {
promptAction.showToast(this.BOnSt)
} else {
promptAction.showToast(this.BOffSt)
}
})
}
.backgroundColor(0xFFFFFF)
}
.padding(10)
.backgroundColor(0xDCDCDC)
.width('100%')
.height('100%')
}
}
基础知识:学习Picker选择器
CalendarPicker
示例
// ets/pages/picker/CalendarPickerPage.ets
@Entry
@Component
struct CalendarPickerPage {
private selectedDate: Date = new Date('2025-03-05')
build() {
Column() {
Text('月历日期选择器').fontSize(30)
Column() {
CalendarPicker({ hintRadius: 10, selected: this.selectedDate })
.edgeAlign(CalendarAlign.END)
.textStyle({ color: "#182431", font: { size: 20, weight: FontWeight.Normal } })
.margin(10)
.onChange((value) => {
console.info("CalendarPicker onChange:" + JSON.stringify(value))
})
}
.alignItems(HorizontalAlign.End)
.width("100%")
}.width('100%')
}
}

DatePicker
示例
// ets/pages/picker/DatePickerPage.ets
@Entry
@Component
struct DatePickerPage {
@State isLunar: boolean = false
private selectedDate: Date = new Date('2025-08-08')
build() {
Column() {
Button('切换公历农历')
.margin({ top: 30, bottom: 30 })
.onClick(() => {
this.isLunar = !this.isLunar
})
DatePicker({
start: new Date('1970-1-1'),
end: new Date('2100-1-1'),
selected: this.selectedDate
})
.disappearTextStyle({ color: Color.Gray, font: { size: '16fp', weight: FontWeight.Bold } })
.textStyle({ color: '#182431', font: { size: '18fp', weight: FontWeight.Normal } })
.selectedTextStyle({ color: '#0000FF', font: { size: '26fp', weight: FontWeight.Regular } })
.lunar(this.isLunar)
.onDateChange((value: Date) => {
this.selectedDate = value
console.info('select current date is: ' + value.toString())
})
}.width('100%')
}
}

TextPicker
示例1(设置选择器列数)
该示例通过配置range实现单列或多列文本选择器。
// ets/pages/picker/TextPicker01Page.ets
class bottom {
bottom: number = 50
}
let bott: bottom = new bottom()
@Entry
@Component
struct TextPicker01Page {
private select: number = 1
private apFruits: string[] = ['apple1', 'apple2', 'apple3', 'apple4']
private orFruits: string[] = ['orange1', 'orange2', 'orange3', 'orange4']
private peFruits: string[] = ['peach1', 'peach2', 'peach3', 'peach4']
private multi: string[][] = [this.apFruits, this.orFruits, this.peFruits]
private cascade: TextCascadePickerRangeContent[] = [
{
text: '辽宁省',
children: [{ text: '沈阳市', children: [{ text: '沈河区' }, { text: '和平区' }, { text: '浑南区' }] },
{ text: '大连市', children: [{ text: '中山区' }, { text: '金州区' }, { text: '长海县' }] }]
},
{
text: '吉林省',
children: [{ text: '长春市', children: [{ text: '南关区' }, { text: '宽城区' }, { text: '朝阳区' }] },
{ text: '四平市', children: [{ text: '铁西区' }, { text: '铁东区' }, { text: '梨树县' }] }]
},
{
text: '黑龙江省',
children: [{ text: '哈尔滨市', children: [{ text: '道里区' }, { text: '道外区' }, { text: '南岗区' }] },
{ text: '牡丹江市', children: [{ text: '东安区' }, { text: '西安区' }, { text: '爱民区' }] }]
}
]
build() {
Column() {
TextPicker({ range: this.apFruits, selected: this.select })
.onChange((value: string | string[], index: number | number[]) => {
console.info('Picker item changed, value: ' + value + ', index: ' + index)
}).margin(bott)
TextPicker({ range: this.multi })
.onChange((value: string | string[], index: number | number[]) => {
console.info('TextPicker 多列:onChange ' + JSON.stringify(value) + ', ' + 'index: ' + JSON.stringify(index))
}).margin(bott)
TextPicker({ range: this.cascade })
.onChange((value: string | string[], index: number | number[]) => {
console.info('TextPicker 多列联动:onChange ' + JSON.stringify(value) + ', ' + 'index: ' +
JSON.stringify(index))
})
}
}
}

示例2(设置文本样式)
该示例通过配置disappearTextStyle、textStyle、selectedTextStyle实现文本选择器中的文本样式。
// ets/pages/picker/TextPicker02Page.ets
@Entry
@Component
struct TextPicker02Page {
private select: number = 1
private fruits: string[] = ['apple1', 'orange2', 'peach3', 'grape4']
build() {
Column() {
TextPicker({ range: this.fruits, selected: this.select })
.onChange((value: string | string[], index: number | number[]) => {
console.info('Picker item changed, value: ' + value + ', index: ' + index)
})
.disappearTextStyle({ color: Color.Red, font: { size: 15, weight: FontWeight.Lighter } })
.textStyle({ color: Color.Black, font: { size: 20, weight: FontWeight.Normal } })
.selectedTextStyle({ color: Color.Blue, font: { size: 30, weight: FontWeight.Bolder } })
}
.width('100%')
.height('100%')
}
}

示例3(设置无分割线样式)
该示例通过配置divider为null实现无分割线样式的文本选择器。
// ets/pages/picker/TextPicker03Page.ets
@Entry
@Component
struct TextPicker03Page {
private select: number = 1
private fruits: string[] = ['apple1', 'orange2', 'peach3', 'grape4']
build() {
Column() {
TextPicker({ range: this.fruits, selected: this.select })
.onChange((value: string | string[], index: number | number[]) => {
console.info('Picker item changed, value: ' + value + ', index: ' + index)
})
.disappearTextStyle({color: Color.Red, font: {size: 15, weight: FontWeight.Lighter}})
.textStyle({color: Color.Black, font: {size: 20, weight: FontWeight.Normal}})
.selectedTextStyle({color: Color.Blue, font: {size: 30, weight: FontWeight.Bolder}})
.divider(null)
}.width('100%').height('100%')
}
}

示例4(设置分割线样式)
该示例通过配置divider的DividerOptions类型实现分割线样式的文本选择器。
// ets/pages/picker/TextPicker04Page.ets
@Entry
@Component
struct TextPicker04Page {
private select: number = 1
private fruits: string[] = ['apple1', 'orange2', 'peach3', 'grape4']
build() {
Column() {
TextPicker({ range: this.fruits, selected: this.select })
.onChange((value: string | string[], index: number | number[]) => {
console.info('Picker item changed, value: ' + value + ', index: ' + index)
})
.disappearTextStyle({ color: Color.Red, font: { size: 15, weight: FontWeight.Lighter } })
.textStyle({ color: Color.Black, font: { size: 20, weight: FontWeight.Normal } })
.selectedTextStyle({ color: Color.Blue, font: { size: 30, weight: FontWeight.Bolder } })
.divider({
strokeWidth: 10,
color: Color.Red,
startMargin: 10,
endMargin: 20
} as DividerOptions)
}
.width('100%')
.height('100%')
}
}

示例5(设置渐隐效果)
该示例通过gradientHeight自定义TextPicker的渐隐效果高度。
// ets/pages/picker/TextPicker05Page.ets
@Entry
@Component
struct TextPicker05Page {
private select: number = 1
private fruits: string[] = ['apple1', 'orange2', 'peach3', 'grape4']
build() {
Column() {
TextPicker({ range: this.fruits, selected: this.select })
.onChange((value: string | string[], index: number | number[]) => {
console.info('Picker item changed, value: ' + value + ', index: ' + index)
})
.disappearTextStyle({ color: Color.Red, font: { size: 15, weight: FontWeight.Lighter } })
.textStyle({ color: Color.Black, font: { size: 20, weight: FontWeight.Normal } })
.selectedTextStyle({ color: Color.Blue, font: { size: 30, weight: FontWeight.Bolder } })
.gradientHeight(100)
}.width('100%').height('100%')
}
}

TimePicker
示例
// ets/pages/picker/TimePicker.ets
@Entry
@Component
struct TimePickerExample {
@State isMilitaryTime: boolean = false
private selectedTime: Date = new Date('2025-07-22T08:00:00')
build() {
Column() {
Button('切换12小时制/24小时制')
.margin(30)
.onClick(() => {
this.isMilitaryTime = !this.isMilitaryTime
})
TimePicker({
selected: this.selectedTime,
format: TimePickerFormat.HOUR_MINUTE_SECOND
})
.useMilitaryTime(this.isMilitaryTime)
.onChange((value: TimePickerResult) => {
if(value.hour >= 0) {
this.selectedTime.setHours(value.hour, value.minute)
console.info(`${value}`)
}
})
.disappearTextStyle({color: Color.Red, font: {size: 15, weight: FontWeight.Lighter}})
.textStyle({color: Color.Black, font: {size: 20, weight: FontWeight.Normal}})
.selectedTextStyle({color: Color.Blue, font: {size: 30, weight: FontWeight.Bolder}})
}.width('100%')
}
}
