代碼功能概述
這段代碼實現了一個功能完整的鴻蒙時鐘應用,全面展示了ArkTS在時間處理、多時區顯示、鬧鐘設置和界面動畫等方面的核心能力。主要功能包括:
- 模擬時鐘:顯示帶有時針、分針、秒針的模擬時鐘
- 數字時鐘:顯示精確到秒的數字時間
- 多時區支持:顯示不同時區的當前時間
- 鬧鐘功能:設置和管理多個鬧鐘
- 倒計時器:支持自定義時間的倒計時功能
- 世界時鐘:同時顯示多個主要城市的時間
- 時間設置:模擬調整系統時間
通過這個示例,可以深入理解ArkTS如何實現時間處理、動畫效果和複雜的狀態管理。
2. 代碼邏輯分析
應用採用"時間驅動UI"的架構設計:
- 初始化階段:應用啓動時,初始化時間數據和多時區信息
- 狀態管理:使用多個
@State裝飾器管理當前時間、時區、鬧鐘列表和倒計時狀態 - 實時更新時間:
- 使用定時器每秒更新當前時間 → 刷新數字顯示和模擬時鐘指針
- 多時區計算 → 根據時區偏移計算各城市時間 → 更新顯示
- 鬧鐘管理:
- 添加鬧鐘 → 設置時間、重複規則 → 添加到鬧鐘列表
- 啓用/禁用鬧鐘 → 切換鬧鐘狀態 → 更新界面
- 鬧鐘觸發 → 檢查當前時間 → 顯示提醒(模擬)
- 倒計時功能:
- 設置倒計時時間 → 啓動倒計時 → 每秒更新剩餘時間
- 倒計時結束 → 觸發提醒 → 重置狀態
- 界面動畫:
- 模擬時鐘指針的平滑轉動動畫
- 界面切換的過渡效果
-
完整代碼
@Entry
@Component
struct ClockTutorial {
@State currentTime: Date = new Date();
@State selectedTimezone: string = 'Asia/Shanghai';
@State isAnalogMode: boolean = true;
@State alarms: Alarm[] = [];
@State countdownTime: number = 0;
@State isCountingDown: boolean = false;
@State worldClocks: WorldClock[] = [
{ city: '北京', timezone: 'Asia/Shanghai', time: '' },
{ city: '紐約', timezone: 'America/New_York', time: '' },
{ city: '倫敦', timezone: 'Europe/London', time: '' },
{ city: '東京', timezone: 'Asia/Tokyo', time: '' }
];
aboutToAppear() {
this.startClock();
this.loadSampleAlarms();
}
build() {
Column({ space: 0 }) {
// 模式切換
this.BuildModeToggle()
// 時鐘顯示區域
if (this.isAnalogMode) {
this.BuildAnalogClock()
} else {
this.BuildDigitalClock()
}
// 世界時鐘
this.BuildWorldClocks()
// 功能按鈕
this.BuildFunctionButtons()
// 鬧鐘列表
this.BuildAlarmList()
}
.width('100%')
.height('100%')
.padding(20)
.backgroundColor('#1A1A2E')
}
@Builder BuildModeToggle() {
Row({ space: 20 }) {
Button('模擬時鐘')
.onClick(() => {
this.isAnalogMode = true;
})
.backgroundColor(this.isAnalogMode ? '#4A90E2' : '#2D3748')
.fontColor('#FFFFFF')
.borderRadius(20)
.padding({ left: 20, right: 20 })
.height(40)
Button('數字時鐘')
.onClick(() => {
this.isAnalogMode = false;
})
.backgroundColor(!this.isAnalogMode ? '#4A90E2' : '#2D3748')
.fontColor('#FFFFFF')
.borderRadius(20)
.padding({ left: 20, right: 20 })
.height(40)
}
.width('100%')
.justifyContent(FlexAlign.Center)
.margin({ bottom: 20 })
}
@Builder BuildAnalogClock() {
const hours = this.currentTime.getHours() % 12;
const minutes = this.currentTime.getMinutes();
const seconds = this.currentTime.getSeconds();
Stack({ alignContent: Alignment.Center }) {
// 時鐘外圈
Circle({ width: 280, height: 280 })
.fill('#0F3460')
.stroke('#4A90E2')
.strokeWidth(4)
// 時鐘刻度
ForEach(Array.from({ length: 12 }), (_, index: number) => {
const angle = (index * 30) * Math.PI / 180;
const x1 = 140 + 120 * Math.sin(angle);
const y1 = 140 - 120 * Math.cos(angle);
const x2 = 140 + 130 * Math.sin(angle);
const y2 = 140 - 130 * Math.cos(angle);
Line()
.startPoint({ x: x1, y: y1 })
.endPoint({ x: x2, y: y2 })
.strokeWidth(2)
.strokeColor('#FFFFFF')
})
// 時針
Line()
.startPoint({ x: 140, y: 140 })
.endPoint({
x: 140 + 60 * Math.sin((hours * 30 + minutes * 0.5) * Math.PI / 180),
y: 140 - 60 * Math.cos((hours * 30 + minutes * 0.5) * Math.PI / 180)
})
.strokeWidth(6)
.strokeColor('#FFFFFF')
.lineCap(LineCapStyle.Round)
// 分針
Line()
.startPoint({ x: 140, y: 140 })
.endPoint({
x: 140 + 80 * Math.sin(minutes * 6 * Math.PI / 180),
y: 140 - 80 * Math.cos(minutes * 6 * Math.PI / 180)
})
.strokeWidth(4)
.strokeColor('#4A90E2')
.lineCap(LineCapStyle.Round)
// 秒針
Line()
.startPoint({ x: 140, y: 140 })
.endPoint({
x: 140 + 90 * Math.sin(seconds * 6 * Math.PI / 180),
y: 140 - 90 * Math.cos(seconds * 6 * Math.PI / 180)
})
.strokeWidth(2)
.strokeColor('#FF6B6B')
.lineCap(LineCapStyle.Round)
// 中心點
Circle({ width: 12, height: 12 })
.fill('#FF6B6B')
}
.width(280)
.height(280)
.margin({ bottom: 20 })
}
@Builder BuildDigitalClock() {
Column({ space: 10 }) {
Text(this.formatTime(this.currentTime))
.fontSize(64)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
Text(this.formatDate(this.currentTime))
.fontSize(18)
.fontColor('#A0AEC0')
Text(this.getWeekday(this.currentTime))
.fontSize(16)
.fontColor('#4A90E2')
}
.width('100%')
.height(160)
.justifyContent(FlexAlign.Center)
.margin({ bottom: 20 })
}
@Builder BuildWorldClocks() {
Column({ space: 10 }) {
Text('世界時間')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#FFFFFF')
.alignSelf(ItemAlign.Start)
.margin({ bottom: 10 })
Grid() {
ForEach(this.worldClocks, (clock: WorldClock) => {
GridItem() {
this.BuildWorldClockItem(clock)
}
})
}
.columnsTemplate('1fr 1fr')
.rowsTemplate('1fr 1fr')
.columnsGap(15)
.rowsGap(15)
.width('100%')
}
.width('100%')
.margin({ bottom: 20 })
}
@Builder BuildWorldClockItem(clock: WorldClock) {
Column({ space: 8 }) {
Text(clock.city)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor('#FFFFFF')
.alignSelf(ItemAlign.Start)
Text(clock.time)
.fontSize(14)
.fontColor('#A0AEC0')
.alignSelf(ItemAlign.Start)
Text(clock.timezone)
.fontSize(12)
.fontColor('#4A90E2')
.alignSelf(ItemAlign.Start)
}
.width('100%')
.height(80)
.padding(12)
.backgroundColor('#2D3748')
.borderRadius(12)
}
@Builder BuildFunctionButtons() {
Row({ space: 15 }) {
Button('鬧鐘')
.onClick(() => {
this.showAlarmDialog();
})
.backgroundColor('#4A90E2')
.fontColor('#FFFFFF')
.borderRadius(12)
.layoutWeight(1)
.height(50)
Button('倒計時')
.onClick(() => {
this.startCountdown(300); // 5分鐘倒計時
})
.backgroundColor(this.isCountingDown ? '#FF6B6B' : '#2D3748')
.fontColor('#FFFFFF')
.borderRadius(12)
.layoutWeight(1)
.height(50)
Button('秒錶')
.onClick(() => {
this.startStopwatch();
})
.backgroundColor('#2D3748')
.fontColor('#FFFFFF')
.borderRadius(12)
.layoutWeight(1)
.height(50)
}
.width('100%')
.margin({ bottom: 20 })
}
@Builder BuildAlarmList() {
Column({ space: 10 }) {
Row({ space: 10 }) {
Text('鬧鐘')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#FFFFFF')
.layoutWeight(1)
Text(`${this.alarms.length} 個`)
.fontSize(14)
.fontColor('#A0AEC0')
}
.width('100%')
if (this.alarms.length === 0) {
this.BuildEmptyState('暫無鬧鐘', '點擊上方"鬧鐘"按鈕添加')
} else {
ForEach(this.alarms.slice(0, 3), (alarm: Alarm) => {
this.BuildAlarmItem(alarm)
})
}
}
.width('100%')
}
@Builder BuildAlarmItem(alarm: Alarm) {
Row({ space: 15 }) {
Column({ space: 5 }) {
Text(this.formatAlarmTime(alarm.hour, alarm.minute))
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.alignSelf(ItemAlign.Start)
Text(this.getAlarmDays(alarm.days))
.fontSize(12)
.fontColor('#A0AEC0')
.alignSelf(ItemAlign.Start)
}
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: alarm.enabled })
.onChange((value: boolean) => {
alarm.enabled = value;
})
.width(40)
Button('刪除')
.onClick(() => {
this.deleteAlarm(alarm.id);
})
.backgroundColor('transparent')
.fontColor('#FF6B6B')
.fontSize(12)
.padding(8)
}
.width('100%')
.padding(15)
.backgroundColor('#2D3748')
.borderRadius(12)
.margin({ bottom: 10 })
}
@Builder BuildEmptyState(title: string, message: string) {
Column({ space: 10 }) {
Text('⏰')
.fontSize(32)
.opacity(0.5)
Text(title)
.fontSize(16)
.fontColor('#A0AEC0')
Text(message)
.fontSize(14)
.fontColor('#718096')
}
.width('100%')
.height(100)
.justifyContent(FlexAlign.Center)
.backgroundColor('#2D3748')
.borderRadius(12)
}
private startClock(): void {
// 更新時間
setInterval(() => {
this.currentTime = new Date();
this.updateWorldClocks();
this.checkAlarms();
if (this.isCountingDown && this.countdownTime > 0) {
this.countdownTime--;
if (this.countdownTime === 0) {
this.countdownComplete();
}
}
}, 1000);
}
private updateWorldClocks(): void {
const now = this.currentTime;
this.worldClocks.forEach(clock => {
const offset = this.getTimezoneOffset(clock.timezone);
const cityTime = new Date(now.getTime() + offset * 60 * 60 * 1000);
clock.time = this.formatTime(cityTime);
});
}
private getTimezoneOffset(timezone: string): number {
// 簡化的時區偏移計算
const offsets: { [key: string]: number } = {
'Asia/Shanghai': 8,
'America/New_York': -5,
'Europe/London': 0,
'Asia/Tokyo': 9
};
return offsets[timezone] || 0;
}
private formatTime(date: Date): string {
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
const seconds = date.getSeconds().toString().padStart(2, '0');
return `${hours}:${minutes}:${seconds}`;
}
private formatDate(date: Date): string {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
}
private getWeekday(date: Date): string {
const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
return weekdays[date.getDay()];
}
private loadSampleAlarms(): void {
this.alarms = [
{ id: '1', hour: 7, minute: 30, days: [1, 2, 3, 4, 5], enabled: true },
{ id: '2', hour: 9, minute: 0, days: [0, 6], enabled: false },
{ id: '3', hour: 22, minute: 0, days: [0, 1, 2, 3, 4, 5, 6], enabled: true }
];
}
private formatAlarmTime(hour: number, minute: number): string {
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
}
private getAlarmDays(days: number[]): string {
if (days.length === 7) return '每天';
if (days.length === 5 && !days.includes(0) && !days.includes(6)) return '工作日';
if (days.length === 2 && days.includes(0) && days.includes(6)) return '週末';
const dayNames = ['日', '一', '二', '三', '四', '五', '六'];
return days.map(day => dayNames[day]).join(', ');
}
private showAlarmDialog(): void {
// 模擬顯示鬧鐘設置對話框
console.log('顯示鬧鐘設置對話框');
// 這裏可以添加新的鬧鐘
const newAlarm: Alarm = {
id: Date.now().toString(),
hour: 8,
minute: 0,
days: [1, 2, 3, 4, 5],
enabled: true
};
this.alarms.push(newAlarm);
}
private deleteAlarm(id: string): void {
this.alarms = this.alarms.filter(alarm => alarm.id !== id);
}
private startCountdown(seconds: number): void {
if (this.isCountingDown) {
this.isCountingDown = false;
this.countdownTime = 0;
} else {
this.isCountingDown = true;
this.countdownTime = seconds;
}
}
private countdownComplete(): void {
this.isCountingDown = false;
// 模擬倒計時完成提醒
console.log('倒計時完成!');
}
private startStopwatch(): void {
// 模擬秒錶功能
console.log('啓動秒錶');
}
private checkAlarms(): void {
const now = this.currentTime;
const currentHour = now.getHours();
const currentMinute = now.getMinutes();
const currentDay = now.getDay();
this.alarms.forEach(alarm => {
if (alarm.enabled &&
alarm.hour === currentHour &&
alarm.minute === currentMinute &&
alarm.days.includes(currentDay)) {
this.triggerAlarm(alarm);
}
});
}
private triggerAlarm(alarm: Alarm): void {
// 模擬鬧鐘觸發
console.log(`鬧鐘觸發: ${this.formatAlarmTime(alarm.hour, alarm.minute)}`);
// 這裏可以添加聲音或震動提醒
}
}
class Alarm {
id: string = '';
hour: number = 0;
minute: number = 0;
days: number[] = [];
enabled: boolean = true;
}
class WorldClock {
city: string = '';
timezone: string = '';
time: string = '';
}
想入門鴻蒙開發又怕花冤枉錢?別錯過!現在能免費系統學 -- 從 ArkTS 面向對象核心的類和對象、繼承多態,到吃透鴻蒙開發關鍵技能,還能衝刺鴻蒙基礎 +高級開發者證書,更驚喜的是考證成功還送好禮!快加入我的鴻蒙班,一起從入門到精通,班級鏈接:點擊https://developer.huawei.com/consumer/cn/training/classDetail/b7365031334e4353a9a0fd6785bb0791?type=1?ha_source=hmosclass&ha_sourceId=89000248免費進入