在鴻蒙next系統上,通過ArkTS寫了個時鐘顯示頁面,集成在【圖影工具箱】應用中,應用商店可以下載使用。
這個頁面實現起來比較簡單,就是左邊一個模擬時鐘,右邊一個數字時鐘(包含時間和日期的文字),每秒更新一次模擬時鐘和數字時鐘。
本文基于鴻蒙ArkUI框架,實現了一個支持??模擬/數字雙模式顯示??、??手勢交互??與??動態主題切換??的時鐘應用。以下從技術架構、核心功能與創新交互三個維度展開解析。
??一、項目架構與技術棧??
-
??基礎框架??
- 使用ArkUI聲明式開發范式(基于TypeScript),通過
@Entry
和@Component
裝飾器定義頁面組件。 - 狀態管理:
@State
驅動UI動態更新(如時間文本、時鐘縮放比例、背景色)。
- 使用ArkUI聲明式開發范式(基于TypeScript),通過
-
??依賴模塊??
import { common } from '@kit.AbilityKit'; // 系統能力庫 import { window } from '@kit.ArkUI'; // 窗口管理 import { AnalogClockComponent } from './AnalogClockComponent'; // 自定義模擬時鐘組件
- ??工具類封裝??:
DateUtil
:時間格式化(getFormatDateStr
生成HH:mm:ss
和yyyy年MM月dd日
)。AppUtil
:設備控制(息屏保持、強制橫屏)。
- ??工具類封裝??:
??二、核心功能實現??
??1. 雙模式時鐘動態切換??
-
??數字時鐘??:通過
Text
組件動態綁定時間與日期:@State timeText: string = ''; // 時間文本(HH:mm:ss) @State dateText: string = ''; // 日期文本(yyyy年MM月dd日)
每秒更新數據:
setInterval(() => this.load(), 1000); // 定時更新
-
??模擬時鐘??:封裝為獨立組件
AnalogClockComponent
:if (this.showClock) {AnalogClockComponent().scale({ x: this.clockScale, y: this.clockScale }) // 支持縮放.gesture(PinchGesture().onActionUpdate((event) => { // 捏合手勢調整縮放比例(0.5~1.5倍)this.clockScale = Math.max(0.5, Math.min(1.5, event.scale * this.lastClockScale));})) }
??2. 交互設計??
- ??顯隱控制??:頂部按鈕切換模擬時鐘顯示狀態:
Button().onClick(() => this.showClock = !this.showClock)
- ??主題切換??:點擊屏幕切換黑/白主題:
.onClick(() => {this.flag = !this.flag;this.fontColor = this.flag ? TimePage.blackColor : TimePage.whiteColor;this.bgColor = this.flag ? TimePage.whiteColor : TimePage.greyColor; })
- ??動畫優化??:黑白主題切換時添加緩動動畫:
Text(this.timeText).animation({ duration: 2000, curve: Curve.EaseOut })
??3. 設備適配與性能??
- ??橫屏沉浸式體驗??:
AppUtil.setPreferredOrientation(window.Orientation.AUTO_ROTATION_LANDSCAPE);
- ??保持不息屏??:
AppUtil.setWindowKeepScreenOn(true);
??三、關鍵技術點解析??
-
??狀態驅動UI更新??
@State
變量(如timeText
、bgColor
)變化自動觸發UI重渲染。- 通過
setInterval
更新狀態,實現每秒刷新時間。
-
??手勢識別與動畫??
- ??捏合縮放??:
PinchGesture
監聽手勢事件,動態計算縮放比例。 - ??主題切換動畫??:頁面背景色與文字顏色變化時應用2000ms漸變動畫。
- ??捏合縮放??:
-
??組件化設計??
- 模擬時鐘獨立封裝為
AnalogClockComponent
,通過Props控制尺寸與縮放,符合高內聚原則。 - 工具類
DateUtil
解耦時間邏輯,提升代碼復用性。
- 模擬時鐘獨立封裝為
??四、優化策略與設計理念??
-
??視覺層次設計??
- ??主次分明??:數字時鐘使用
100px
大字體突出時間,日期文本使用30px
小字體。 - ??色彩對比??:深灰背景(
#fefefe
)與白色文字確保可讀性,夜間模式切換為深色文字+白色背景。
- ??主次分明??:數字時鐘使用
-
??響應式交互??
- 手勢操作實時反饋(捏合縮放),限制縮放范圍避免UI變形。
- 控件位置優化:控制按鈕置于右上角,避免遮擋核心內容。
-
??性能與功耗??
- 頁面不可見時(
onPageHide
)清除定時器,減少資源占用。 - 橫屏模式適配多種設備尺寸,通過
Stack
+Row/Column
布局保證元素對齊。
- 頁面不可見時(
??五、擴展方向??
- ??多時區支持??:擴展
DateUtil
,增加時區切換功能。 - ??動態背景??:根據時間自動切換日出/日落主題色。
??結語??
本文實現的ArkUI時鐘界面,通過??狀態驅動??、??手勢交互??與??組件化設計??,平衡了功能性與視覺體驗。其設計模式可復用于其他實時數據展示場景(如天氣儀表盤、健身數據統計)。未來可結合鴻蒙分布式能力,實現跨設備時鐘同步控制。
??設計原則總結??:
原則 | 實現方式 |
---|---|
??信息直觀性?? | 雙模式時鐘互補顯示 |
??操作便捷性?? | 手勢縮放+一鍵切換 |
??視覺舒適性?? | 動態主題+緩動動畫 |
??性能優化?? | 定時器生命周期管理 |
源碼中的
AnalogClockComponent
實現可參考《ArkUI自定義組件之模擬時》,重點利用Canvas
繪圖與旋轉動畫實現指針效果。
具體代碼如下:
import { common } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { AppUtil } from './Utils/AppUtil';
import { DateUtil } from './Utils/DateUtil';
import { AnalogClockComponent } from './ClockPage/AnalogClockComponent';@Entry
@Component
struct TimePage {context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;intervalID: number = -1;@State timeText: string = '';@State dateText: string = '';@State showClock: boolean = true;@State lastClockScale: number = 1.35;@State clockScale: number = 1.35;bigFontSize: number = 100;smallFontSize: number = 30;static readonly whiteColor: string = '#fefefe';static readonly greyColor: string | Resource = $r('app.color.body_color');static readonly blackColor: string | Resource = '#000';@State bgColor: string | Resource = TimePage.greyColor;@State fontColor: string | Resource = TimePage.whiteColor;@State flag: boolean = falsenowDate = Date.now();load() {this.nowDate = Date.now();this.timeText = DateUtil.getFormatDateStr(this.nowDate, "HH:mm:ss");this.dateText = DateUtil.getFormatDateStr(this.nowDate, "yyyy年MM月dd日")}onPageShow(): void {this.load();this.intervalID = setInterval(() => {this.load();}, 1000);AppUtil.setWindowKeepScreenOn(true);AppUtil.setPreferredOrientation(window.Orientation.AUTO_ROTATION_LANDSCAPE);}onPageHide(): void {clearInterval(this.intervalID);}build() {Stack({ alignContent: Alignment.Top }) {// 主要內容區域Row() {if (this.showClock) {// 模擬時鐘組件Column() {AnalogClockComponent().width('60%').aspectRatio(1)}.scale({ x: this.clockScale, y: this.clockScale }).width('30%').height('100%').justifyContent(FlexAlign.Center).align(Alignment.Center).margin({ left: 30 }).gesture(PinchGesture().onActionStart((event: GestureEvent) => {this.lastClockScale = this.clockScale;}).onActionUpdate((event: GestureEvent) => {this.clockScale = Math.max(0.5, Math.min(1.5, event.scale * this.lastClockScale));}))}// 數字時間顯示Column() {Text(this.timeText).fontSize(this.bigFontSize).fontColor(this.fontColor).animation({duration: 2000,curve: Curve.EaseOut,iterations: 1,playMode: PlayMode.Normal})Text(this.dateText).fontSize(this.smallFontSize).fontColor(this.fontColor).animation({duration: 2000,curve: Curve.Ease,iterations: 1,playMode: PlayMode.Normal})}.layoutWeight(1).height('100%').justifyContent(FlexAlign.Center)}.width('100%').height('100%')// 頂部控制欄Row() {Button() {Image(this.showClock ? $r('app.media.ic_visibility_off') : $r('app.media.ic_visibility')).width(24).height(24).fillColor(this.fontColor).opacity(0.3)}.width(48).height(48).borderRadius(24).backgroundColor('transparent').onClick(() => {this.showClock = !this.showClock;})}.width('100%').justifyContent(FlexAlign.End).padding({ right: 20, top: 20 })}.onClick(() => {if (this.flag) {this.fontColor = TimePage.whiteColor;this.bgColor = TimePage.greyColor;} else {this.fontColor = TimePage.blackColor;this.bgColor = TimePage.whiteColor;}this.flag = !this.flag;}).backgroundColor(this.bgColor).animation({duration: 2000,curve: Curve.Ease,iterations: 1,playMode: PlayMode.Normal}).width('100%').height('100%')}
}