iT邦幫忙

2024 iThome 鐵人賽

DAY 15
0
Mobile Development

github裡永遠有一個還沒做的SideProject :用Kotlin來開發點沒用的酷東西系列 第 15

Day15:製作時鐘元件和用animateFloatAsState製作翻頁動畫

  • 分享至 

  • xImage
  •  

前言

昨天我們將日曆元件的部分告一段落,今天的內容我們來實現左上方的時鐘元件功能,並在時鐘數字變化時以翻頁的形式呈現,並把日曆元件也修改成夜間模式,

時鐘元件的建立

1.建立FlipClock.kt檔案

程式碼如下

  • kotlin
// 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 組件顯示字符。
效果如下圖所示
https://ithelp.ithome.com.tw/upload/images/20240929/20162649okbVF23fCn.png

使用animateFloatAsState添加動畫效果並調整配色

如上圖所示,我們完成時鐘元件後可以為他添加一點翻頁效果,使用animateFloatAsState為數字翻轉過程添加動畫。
animateFloatAsState是Jetpack Compose提供的一個用於浮點數動畫的API,通常用於將浮點數的值過渡成另一個值,並在動畫播放期間產生一個可觀察的狀態值。它會根據給定的目標值進行自動插值,並將插值結果更新到 Compose 界面中,讓你可以輕鬆地在組件的屬性之間過渡時添加動畫效果。它能夠對Float值進行動畫插值,在屬性值改變時產生平滑的動畫過渡效果。
語法如下

  • kotlin
val animatedFloat by animateFloatAsState(
    targetValue = targetValue, // 目標浮點數值
    animationSpec = tween(durationMillis = 1000), // 動畫規格(可選)
    finishedListener = { /* 動畫完成後的動作 */ } // 動畫完成時的回調(可選)
)

所以我們可以修改一下FlipDigit(),程式碼如下

  • kotlin
@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用來調整翻頁的深度效果,防止動畫失真。修改完後成果如下
https://ithelp.ithome.com.tw/upload/images/20240929/20162649eYcg5v8JXT.png

後話

不知不覺鐵人賽也過了一半了,今天建構了時鐘元件,因為搬家的原因離預計在第二周把元件都設計完還少了個代辦事項,我會盡量補上,今天的內容就到這邊了,感謝你的觀看,我們明天再見


上一篇
Day14:建構單日行事曆頁面和使用夜間模式來設定UI
下一篇
Day16:單日行事曆UI介面的建構
系列文
github裡永遠有一個還沒做的SideProject :用Kotlin來開發點沒用的酷東西30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言