前言 简单动画推荐使用 animate*AsState 或 rememberInfiniteTransition,复杂交互动画则适合 Animatable 或 updateTransition。
个人比较喜欢使用Animatable,灵活方便。
倒计时组件 这里动画执行完,再改变的时间数字,没有用协程分别处理是因为可能出现动画执行没完成或超出才停止的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 import android.util.Logimport androidx.compose.animation.core.Animatableimport androidx.compose.animation.core.FastOutSlowInEasingimport androidx.compose.animation.core.tweenimport androidx.compose.foundation.layout.Boximport androidx.compose.foundation.layout.heightimport androidx.compose.foundation.layout.widthimport androidx.compose.runtime.Composableimport androidx.compose.runtime.LaunchedEffectimport androidx.compose.runtime.getValueimport androidx.compose.runtime.keyimport androidx.compose.runtime.mutableIntStateOfimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.setValueimport androidx.compose.ui.Alignmentimport androidx.compose.ui.Modifierimport androidx.compose.ui.draw.rotateimport androidx.compose.ui.text.font.FontWeightimport androidx.compose.ui.unit.dpimport androidx.compose.ui.unit.spimport com.xhkjedu.zxs_android.Rimport com.xhkjedu.zxs_android.common.ZThemeimport com.xhkjedu.zxs_android.componts.common.image.ZImgLocalBgimport com.xhkjedu.zxs_android.componts.common.text.ZTextColorSizeComp@Composable fun ZCountdownTimerComp (sec: Int = 5 , timeEndCb: () -> Unit ) { val TAG = "DjsComp" val rotation = remember { Animatable(0f ) } val numState = remember { mutableIntStateOf(sec) } var isAnimating by remember { mutableStateOf(true ) } suspend fun anmiOneAction () { rotation.animateTo( targetValue = 360f , animationSpec = tween( 1000 , easing = FastOutSlowInEasing ) ) rotation.snapTo(0f ) } LaunchedEffect(sec) { Log.i(TAG, "ZCountdownTimerComp: ${sec} " ) numState.intValue = sec if (sec > 0 ) { isAnimating = true } else { isAnimating = false } while (isAnimating) { anmiOneAction() numState.intValue -= 1 if (numState.intValue <= 0 ) { anmiOneAction() isAnimating = false timeEndCb() } } } Box( modifier = Modifier .width(94. dp) .height(94. dp), contentAlignment = Alignment.Center ) { ZImgLocalBg(R.drawable.djs_bg) Box( modifier = Modifier .width(72. dp) .height(72. dp) .rotate(rotation.value) ) { ZImgLocalBg(R.drawable.djs_mid) } ZTextColorSizeComp( text = "${numState.intValue} " , fontSize = 46. sp, fontColor = ZTheme.ColorWhite, fontWeight = FontWeight.Bold, modifier = Modifier .align(Alignment.Center) ) } } @Composable fun ZCountdownTimerHostComp ( sec: Int = 5 , keyReset: Any = System.currentTimeMillis() , timeEndCb: () -> Unit ) { key(keyReset) { ZCountdownTimerComp(sec = sec, timeEndCb = timeEndCb) } }
重新执行 当我们需要重新计时的时候,因为传参是一样的,不会触发重组。
推荐做法:
用 key + 外部状态控制重置
示例
1 2 3 4 5 6 7 8 9 10 @Composable fun ZCountdownTimerHostComp ( sec: Int = 5 , keyReset: Any = System.currentTimeMillis() , timeEndCb: () -> Unit ) { key(keyReset) { ZCountdownTimerComp(sec = sec, timeEndCb = timeEndCb) } }
使用示例
1 2 3 4 5 6 val numState = remember { mutableIntStateOf(5 ) }val timeunix = remember { mutableStateOf(System.currentTimeMillis()) }ZCountdownTimerHostComp(numState.intValue, keyReset = timeunix.value) { timeunix.value = System.currentTimeMillis() numState.intValue = 5 }