Compose 代码实现帧动画 还有derivedStateOf rememberSaveable 的使用

174 阅读1分钟
package com.gy.composestudy.view

import androidx.compose.material.Icon
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import kotlinx.coroutines.delay

private const val TAG = "FrameAnimIcon"

data class FrameAnimationIconState(
    val frameIds: List<Int> = listOf(),
    var initialPlaying: Boolean = false,
    var duration: Long = 500
) {
    var currentFrameIdx by mutableStateOf(0)
    var isPlaying by mutableStateOf(initialPlaying)
    fun next(){
        currentFrameIdx++
        if(currentFrameIdx == frameIds.size) currentFrameIdx = 0
    }
    fun play(){
        isPlaying = true
    }
    fun pause(){
        isPlaying = false
    }
    fun reset(idx : Int = 0){
        currentFrameIdx = idx
        isPlaying = false
    }
}

/**
 * 帧动画Icon对应状态,可用来手动控制动画
 * @param frameIds List<Int> 每一帧对应的 DrawableId
 * @param initialPlaying Boolean 最初时是播放还是暂停
 * @param duration Long 从第一帧到最后一帧一共要多久
 * @return FrameAnimationIconState
 */
@Composable
fun rememberFrameAnimIconState(
    frameIds: List<Int> = listOf(),
    initialPlaying: Boolean = false,
    duration: Long = 500
) = rememberSaveable(saver = listSaver(
    save = { listOf(it.isPlaying)},
    restore = { FrameAnimationIconState(frameIds, it[0], duration) }
)) {
    FrameAnimationIconState(frameIds, initialPlaying, duration)
}

/**
 * 简单的帧动画Icon
 * @param modifier Modifier
 * @param state FrameAnimationIconState, see [rememberFrameAnimIconState]
 * @param contentDescription String
 * @param tint Color
 */
@Composable
fun FrameAnimationIcon(
    modifier: Modifier = Modifier,
    state: FrameAnimationIconState = rememberFrameAnimIconState(),
    contentDescription: String = "",
    tint: Color = Color.Unspecified
) {
    val currentFrame by remember { derivedStateOf {
        state.currentFrameIdx
    }}

    LaunchedEffect(state.isPlaying){
//        Log.d(TAG, "FrameAnimationIcon: Launched $state")
        while (state.isPlaying && state.frameIds.isNotEmpty()){
            delay(state.duration / state.frameIds.size)
            state.next()
//            Log.d(TAG, "FrameAnimationIcon: 当前帧数:${state.currentFrameIdx}")
        }
    }

    Icon(painter = painterResource(id = state.frameIds[currentFrame]), contentDescription = contentDescription, tint = tint, modifier = modifier)
}

原文地址: juejin.cn/post/718693…

加油.png