Chapter 4 UI Component Introduction and Practical Applications (Part 3)
Through the learning in the previous chapters, we have gained a preliminary understanding of some basic components and layout methods in OpenHarmony. In this chapter, we will combine the use of multiple components through a complete digital clock example to further consolidate the learning content. This example will demonstrate how to use OpenHarmony's animation functions, state management, as well as components like sliders and toggle switches, helping you better understand and apply the ArkUI framework.
1. Example Demonstration
After learning the previous chapters, we also have a general understanding of some basic components. To learn more, you can visit the HarmonyOS official website.
We will implement a dynamic digital clock that can display real-time updated time and includes the following:
Core Functions: Time Dynamic Update - Achieve digit flip effect through animation. Brightness Adjustment - Use slider component to dynamically set screen brightness. Fullscreen Mode Toggle - Use switch component to toggle between fullscreen and normal modes.
Key Components: Text - Used to display clock numbers. Slider - Slider for adjusting screen brightness. Toggle - Switch for fullscreen toggle function. Animation - Achieve digit flip effect.
### 1.1 Digital Clock Main Code: //修改时间
changeTime(): Array<number> {
const time = new Date()
const hour = time.getHours()
const hourOne = Math.floor(hour / 10)
const hourTwo = hour % 10
const minutesOne = Math.floor(time.getMinutes() / 10)
const minutesTwo = time.getMinutes() % 10
const secondsOne = Math.floor(time.getSeconds() / 10)
const secondsTwo = time.getSeconds() % 10
return [hourOne, hourTwo, minutesOne, minutesTwo, secondsOne, secondsTwo]
}
@Builder box(num : number) {
Column() {
Divider()
.zIndex(5)
.strokeWidth(2)
.color(Color.White)
.position({ x: 0, y: '50%' })
Text(`${this.timeOne[num]}`)
.zIndex(1)
.height(108)
.width('100%')
.borderRadius(8)
.fontWeight(700)
.padding({ top: 0 })
.fontSize(90)
.position({ x: 0, y: 0 })
.fontColor(Color.White)
.fontFamily('Monospace')
.backgroundColor($r('app.color.text_bg'))
.textAlign(TextAlign.Center)
Text(`${this.timeTwo[num]}`)
.zIndex(2)
.height(64)
.width('100%')
.fontWeight(700)
.borderRadius(8)
.padding({ top: 3 })
.fontSize(90)
.position({ x: 0, y: 0 })
.fontColor(Color.White)
.fontFamily('Monospace')
.backgroundColor($r('app.color.text_bg'))
.textAlign(TextAlign.Center)
Text(`${this.timeThree[num]}`)
.zIndex(4)
.height(64)
.width('100%')
.fontWeight(700)
.borderRadius(8)
.padding({ top: 3 })
.fontSize(90)
.position({ x: 0, y: 0 })
.fontColor(Color.White)
.fontFamily('Monospace')
.textAlign(TextAlign.Center)
.backgroundColor($r('app.color.text_bg'))
.rotate({
x: 1,
y: 0,
z: 0,
centerX: '50%',
centerY: '100%',
angle: this.angleOne[num]
})
Text(`${this.timeFour[num]}`)
.zIndex(3)
.height(108)
.width('100%')
.fontWeight(700)
.borderRadius(8)
.padding({ top: 0 })
.fontSize(90)
.position({ x: 0, y: 0 })
.fontColor(Color.White)
.fontFamily('Monospace')
.backgroundColor($r('app.color.text_bg'))
.textAlign(TextAlign.Center)
.rotate({
x: 1,
y: 0,
z: 0,
centerX: '50%',
centerY: '50%',
angle: this.angleTwo[num]
})
}
.height(108)
.width('50%')
}
### 1.2 Animation Rotation Code:
### 1.2 动画旋转部分代码:
```typescript
animationOne(i : number): void {
animateTo({
duration: 250,
delay: 0,
iterations: 1,
onFinish: () => {
this.animationTwo(i)
this.angleOne[i] = 0
}
}, () => this.angleOne[i] = 90)
}
animationTwo(i : number): void {
animateTo({
duration: 250,
delay: 0,
iterations: 1,
onFinish: () => {
this.angleTwo[i] = -90
}
}, () => this.angleTwo[i] = 0)
}1.3 Fullscreen Code:
this.isFullScreen = !this.isFullScreen
try {
let win = await window.getLastWindow(getContext(this))
let names = this.isFullScreen ? [] : ['status', 'navigation']
await win.setWindowSystemBarEnable(names as Array<'status' | 'navigation'>)
}Complete Code Display:
import { brightness } from '@kit.BasicServicesKit';
import { LockSwitch } from './LockSwitch';
import { window } from '@kit.ArkUI';
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
@State timeOne: Array<number> = []
@State timeTwo: Array<number> = []
@State timeThree: Array<number> = []
@State timeFour: Array<number> = []
@State angleOne: Array<number> = [0, 0, 0, 0, 0, 0]
@State angleTwo: Array<number> = [-90, -90, -90, -90, -90, -90]
@State isFullScreen: boolean = false
@State inSetValue: number = 40
aboutToAppear() {
const arr = this.changeTime()
this.timeOne = [...arr]
this.timeTwo = [...arr]
this.timeThree = [...arr]
this.timeFour = [...arr]
setInterval(() => {
const time = new Date()
if (this.timeOne[5] != time.getSeconds() % 10) {
const arr = this.changeTime()
for (let i = 0;i < 6; i++) {
if (arr[i] != this.timeFour[i]) {
this.timeFour[i] = arr[i]
this.animationOne(i)
setTimeout(() => {
this.timeTwo[i] = arr[i]
}, 100)
setTimeout(() => {
this.timeThree[i] = arr[i]
}, 150)
setTimeout(() => {
this.timeOne[i] = arr[i]
}, 240)
}
}
}
}, 1000)
}
animationOne(i : number): void {
animateTo({
duration: 250,
delay: 0,
iterations: 1,
onFinish: () => {
this.animationTwo(i)
this.angleOne[i] = 0
}
}, () => this.angleOne[i] = 90)
}
animationTwo(i : number): void {
animateTo({
duration: 250,
delay: 0,
iterations: 1,
onFinish: () => {
this.angleTwo[i] = -90
}
}, () => this.angleTwo[i] = 0)
}
setBrightness(): void {
brightness.setValue(this.inSetValue)
}
//修改时间
changeTime(): Array<number> {
const time = new Date()
const hour = time.getHours()
const hourOne = Math.floor(hour / 10)
const hourTwo = hour % 10
const minutesOne = Math.floor(time.getMinutes() / 10)
const minutesTwo = time.getMinutes() % 10
const secondsOne = Math.floor(time.getSeconds() / 10)
const secondsTwo = time.getSeconds() % 10
return [hourOne, hourTwo, minutesOne, minutesTwo, secondsOne, secondsTwo]
}
@Builder box(num : number) {
Column() {
Divider()
.zIndex(5)
.strokeWidth(2)
.color(Color.White)
.position({ x: 0, y: '50%' })
Text(`${this.timeOne[num]}`)
.zIndex(1)
.height(108)
.width('100%')
.borderRadius(8)
.fontWeight(700)
.padding({ top: 0 })
.fontSize(90)
.position({ x: 0, y: 0 })
.fontColor(Color.White)
.fontFamily('Monospace')
.backgroundColor($r('app.color.text_bg'))
.textAlign(TextAlign.Center)
Text(`${this.timeTwo[num]}`)
.zIndex(2)
.height(64)
.width('100%')
.fontWeight(700)
.borderRadius(8)
.padding({ top: 3 })
.fontSize(90)
.position({ x: 0, y: 0 })
.fontColor(Color.White)
.fontFamily('Monospace')
.backgroundColor($r('app.color.text_bg'))
.textAlign(TextAlign.Center)
Text(`${this.timeThree[num]}`)
.zIndex(4)
.height(64)
.width('100%')
.fontWeight(700)
.borderRadius(8)
.padding({ top: 3 })
.fontSize(90)
.position({ x: 0, y: 0 })
.fontColor(Color.White)
.fontFamily('Monospace')
.textAlign(TextAlign.Center)
.backgroundColor($r('app.color.text_bg'))
.rotate({
x: 1,
y: 0,
z: 0,
centerX: '50%',
centerY: '100%',
angle: this.angleOne[num]
})
Text(`${this.timeFour[num]}`)
.zIndex(3)
.height(108)
.width('100%')
.fontWeight(700)
.borderRadius(8)
.padding({ top: 0 })
.fontSize(90)
.position({ x: 0, y: 0 })
.fontColor(Color.White)
.fontFamily('Monospace')
.backgroundColor($r('app.color.text_bg'))
.textAlign(TextAlign.Center)
.rotate({
x: 1,
y: 0,
z: 0,
centerX: '50%',
centerY: '50%',
angle: this.angleTwo[num]
})
}
.height(108)
.width('50%')
}
build() {
Column() {
Row() {
Row({ space: 2 }) {
this.box(0)
this.box(1)
}
.width('30%')
.height('30%')
Image($r('app.media.dot'))
.width(20)
.height(50)
Row({ space: 2 }) {
this.box(2)
this.box(3)
}
.width('30%')
.height('30%')
Image($r('app.media.dot'))
.width(20)
.height(50)
Row({ space: 2 }) {
this.box(4)
this.box(5)
}
.width('30%')
.height('30%')
}
.id('currentTimeBox')
.width('50%')
.height('50%')
Column({ space: 10 }) {
Divider()
.strokeWidth(5)
.color($r('app.color.divider_bg'))
Row() {
Text($r("app.string.fullScreen")).fontSize(18)
Blank()
Toggle({ type: ToggleType.Switch, isOn: this.isFullScreen })
.switchPointColor(0xe5ffffff)
.onChange(async () => {
this.isFullScreen = !this.isFullScreen
try {
let win = await window.getLastWindow(getContext(this))
let names = this.isFullScreen ? [] : ['status', 'navigation']
await win.setWindowSystemBarEnable(names as Array<'status' | 'navigation'>)
} catch (err) {
console.info(`setFullScreen fail, code = ${err.code}`)
}
})
}
.width('90%')
.height(62)
.borderRadius(24)
.margin({ top: 10 })
.backgroundColor(Color.White)
.padding({ left: '3%', right: '3%' })
Divider()
.strokeWidth(5)
.color($r('app.color.divider_bg'))
Row() {
Lock()
}
.width('90%')
.height(62)
.borderRadius(24)
.margin({ top: 10 })
.backgroundColor(Color.White)
.padding({ left: '3%', right: '3%' })
Divider()
.strokeWidth(5)
.color($r('app.color.divider_bg'))
Row() {
Text($r("app.string.brightness"))
.fontSize(18)
Slider({
value: this.inSetValue,
min: 0,
max: 255,
step: 5,
style: SliderStyle.OutSet
})
.id('brightness')
.width('90%')
.showTips(false)
.showSteps(false)
.blockColor(0xCCCCCC)
.trackColor(Color.Black)
.selectedColor(0xCCCCCC)
.onChange((value: number, mode: SliderChangeMode) => {
this.inSetValue = value
this.setBrightness()
})
}
.width('90%')
.height(62)
.borderRadius(24)
.margin({ top: 10 })
.backgroundColor(Color.White)
.padding({ left: '3%', right: '3%' })
Divider()
.strokeWidth(5)
.color($r('app.color.divider_bg'))
}
.height('100%')
}
.width('100%')
.height('100%')
.backgroundColor('#F1F3F5')
.justifyContent(FlexAlign.Center)
}
}
@Component
struct Lock {
@State isComTime: boolean = true
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Column() {
LockSwitch({ isComTime: $isComTime })
}
.width('100%')
}
}
}Source Code Address: Sample Source Code
