昨天我們將日曆元件的部分告一段落,今天的內容我們來實現左上方的時鐘元件功能,並在時鐘數字變化時以翻頁的形式呈現,並把日曆元件也修改成夜間模式,
程式碼如下
// FlipClock.kt
package com.example.a2024ironman
import androidx.compose.animation.core.*
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import java.text.SimpleDateFormat
import java.util.*
@Composable
fun FlipClock(modifier: Modifier = Modifier) {
var currentTime by remember { mutableStateOf(getCurrentTime()) }
// 更新時間,每秒更新一次
LaunchedEffect(Unit) {
while (true) {
currentTime = getCurrentTime()
kotlinx.coroutines.delay(1000L)
}
}
// 顯示時間
Row(
modifier = modifier
.background(MaterialTheme.colorScheme.surface)
.padding(16.dp)
) {
FlipDigit(digit = currentTime.first, Modifier.weight(1f))
Spacer(modifier = Modifier.width(4.dp))
FlipDigit(digit = currentTime.second, Modifier.weight(1f))
Spacer(modifier = Modifier.width(4.dp))
FlipDigit(digit = currentTime.third, Modifier.weight(1f))
Spacer(modifier = Modifier.width(4.dp))
FlipDigit(digit = currentTime.fourth, Modifier.weight(1f))
}
}
@Composable
fun FlipDigit(digit: Char, modifier: Modifier = Modifier) {
Box(
modifier = modifier
.background(Color.Black)
.padding(8.dp),
contentAlignment = Alignment.Center
) {
Text(
text = digit.toString(),
style = TextStyle(
color = Color.White,
fontSize = 48.sp
)
)
}
}
fun getCurrentTime(): ClockTime {
val date = Date()
val formatter = SimpleDateFormat("HHmm", Locale.getDefault())
val time = formatter.format(date)
return ClockTime(time[0], time[1], time[2], time[3])
}
data class ClockTime(val first: Char, val second: Char, val third: Char, val fourth: Char)
上面的程式碼是使用LaunchedEffect
每秒更新currentTime
,取得最新的時間。
Row 組件依次排列四個 FlipDigit,分別顯示當前小時和分鐘的每個數字。
FlipDigit 負責單個數字的呈現,利用 Text 組件顯示字符。
效果如下圖所示
如上圖所示,我們完成時鐘元件後可以為他添加一點翻頁效果,使用animateFloatAsState為數字翻轉過程添加動畫。animateFloatAsState
是Jetpack Compose提供的一個用於浮點數動畫的API,通常用於將浮點數的值過渡成另一個值,並在動畫播放期間產生一個可觀察的狀態值。它會根據給定的目標值進行自動插值,並將插值結果更新到 Compose 界面中,讓你可以輕鬆地在組件的屬性之間過渡時添加動畫效果。它能夠對Float值進行動畫插值,在屬性值改變時產生平滑的動畫過渡效果。
語法如下
val animatedFloat by animateFloatAsState(
targetValue = targetValue, // 目標浮點數值
animationSpec = tween(durationMillis = 1000), // 動畫規格(可選)
finishedListener = { /* 動畫完成後的動作 */ } // 動畫完成時的回調(可選)
)
所以我們可以修改一下FlipDigit()
,程式碼如下
@Composable
fun FlipDigit(digit: Char, modifier: Modifier = Modifier) {
var currentDigit by remember { mutableStateOf(digit) }
val rotationY = remember { Animatable(0f) }
LaunchedEffect(digit) {
// 觸發動畫時,旋轉角度從 0 -> -90 -> +90 -> 0
rotationY.animateTo(
targetValue = 90f,
animationSpec = tween(durationMillis = 150, easing = LinearEasing)
)
// 切換到新的數字
currentDigit = digit
rotationY.animateTo(
targetValue = 0f,
animationSpec = tween(durationMillis = 150, easing = LinearEasing)
)
}
Box(
modifier = modifier
.fillMaxSize()
.graphicsLayer {
rotationY = rotationY.value // 設定 Y 軸翻頁動畫
cameraDistance = 12 * density // 設定相機距離,防止翻頁時失真
},
contentAlignment = Alignment.Center
) {
Text(
text = currentDigit.toString(),
style = TextStyle(
color = Color.White,
fontSize = 120.sp
)
)
}
}
在上面的程式碼中,我們使用rotationX
屬性來控制上下翻頁效果,這將使動畫看起來像是從下到上翻轉。rotationAnimation.animateTo(targetValue = -90f)
和rotationAnimation.animateTo(targetValue = 0f)
這兩段是為了使得動畫看起來從下往上翻頁。cameraDistance = 12 * density
用來調整翻頁的深度效果,防止動畫失真。修改完後成果如下
不知不覺鐵人賽也過了一半了,今天建構了時鐘元件,因為搬家的原因離預計在第二周把元件都設計完還少了個代辦事項,我會盡量補上,今天的內容就到這邊了,感謝你的觀看,我們明天再見