您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“Android Compose之Animatable動(dòng)畫停止怎么使用”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Android Compose之Animatable動(dòng)畫停止怎么使用”吧!
顧名思義即動(dòng)畫在運(yùn)行過程中被打斷,那么是被什么打斷呢?被同一個(gè) Animatable
的另一個(gè)動(dòng)畫打斷,簡(jiǎn)單的說就是當(dāng) Animatable
在執(zhí)行某一個(gè)動(dòng)畫的過程中,此時(shí)再使用同一個(gè) Animatable
去開啟另一個(gè)動(dòng)畫,此時(shí)就會(huì)打斷正在運(yùn)行中的動(dòng)畫,即停止正在運(yùn)行中的話執(zhí)行新的動(dòng)畫。
那么為什么要打斷正在運(yùn)行中的動(dòng)畫呢?不能兩個(gè)動(dòng)畫一起執(zhí)行嗎?假設(shè)一個(gè)動(dòng)畫是將方塊移動(dòng)到 200dp 的位置,另一個(gè)動(dòng)畫是將方塊移動(dòng)到 0dp 的位置,如果不會(huì)被打斷兩個(gè)動(dòng)畫可以同時(shí)執(zhí)行,那就會(huì)出現(xiàn)方塊一會(huì)兒往 200dp 位置一會(huì)兒往 0dp 移動(dòng)的閃爍情況,顯然動(dòng)畫效果不符合預(yù)期,所以被設(shè)計(jì)成了后一個(gè)動(dòng)畫會(huì)打斷前一個(gè)動(dòng)畫。
下面就用一個(gè)實(shí)例演示動(dòng)畫的打斷,代碼如下:
// 方塊顏色,默認(rèn)為藍(lán)色 var backgroundColor by remember { mutableStateOf(Color.Blue) } // 動(dòng)畫實(shí)例 val animatable = remember { Animatable(10.dp, Dp.VectorConverter) } // 獲取協(xié)程作用域 val scope = rememberCoroutineScope() Box { // 動(dòng)畫方塊 Box( Modifier // 使用動(dòng)畫值 .padding(start = animatable.value, top = 30.dp) .size(100.dp, 100.dp) .background(backgroundColor) .clickable { // 點(diǎn)擊事件 // 啟動(dòng)動(dòng)畫 scope.launch { animatable.animateTo(200.dp, // 為了方便看到效果,動(dòng)畫時(shí)間設(shè)置為 1000ms animationSpec = tween(durationMillis = 1000) ) } } ) // 按鈕,用于開啟新動(dòng)畫 Button(onClick = { // 修改方塊顏色,方便觀察區(qū)分兩個(gè)動(dòng)畫 backgroundColor = Color.Cyan // 啟動(dòng)新動(dòng)畫 scope.launch { animatable.animateTo(50.dp, animationSpec = tween(durationMillis = 1000)) } }, Modifier.padding(top = 170.dp, start = 70.dp)) { Text(text = "Next", style = TextStyle(fontSize = 10.sp)) } }
界面上添加了一個(gè)方塊和一個(gè)按鈕,點(diǎn)擊方塊執(zhí)行動(dòng)畫移動(dòng)到 200dp 位置,點(diǎn)擊按鈕執(zhí)行動(dòng)畫移動(dòng)到 50dp 位置,為了區(qū)分兩個(gè)動(dòng)畫動(dòng)畫執(zhí)行時(shí)為方塊設(shè)置了不同的顏色
從上面的效果可以看出,對(duì)同一個(gè) Animatable
開啟新的動(dòng)畫確實(shí)會(huì)打斷正在運(yùn)行的動(dòng)畫。
除了上面演示的 animateTo
可以打斷動(dòng)畫以外,Animatable
的snapTo
、animateDecay
同樣可以打斷動(dòng)畫。
前面介紹的是新動(dòng)畫打斷正在運(yùn)行的動(dòng)畫,那么如果我們想主動(dòng)停止一個(gè)Animatable
動(dòng)畫該怎么辦呢?很簡(jiǎn)單,Animatable
提供了stop
方法用于停止動(dòng)畫。
示例代碼如下:
var backgroundColor by remember { mutableStateOf(Color.Blue) } // 動(dòng)畫實(shí)例 val animatable = remember { Animatable(10.dp, Dp.VectorConverter) } val scope = rememberCoroutineScope() Box { // 動(dòng)畫方塊 Box( Modifier // 使用動(dòng)畫值 .padding(start = animatable.value, top = 30.dp) .size(100.dp, 100.dp) .background(backgroundColor) .clickable { // 點(diǎn)擊事件,開啟動(dòng)畫 scope.launch { animatable.animateTo(200.dp, // 為了方便觀察動(dòng)畫效果,動(dòng)畫時(shí)長(zhǎng)設(shè)置為 1000ms animationSpec = tween(durationMillis = 1000) ) } } ) // 停止按鈕 Button(onClick = { // 修改方塊顏色 backgroundColor = Color.Cyan // 停止動(dòng)畫 scope.launch { animatable.stop() } }, Modifier.padding(top = 170.dp, start = 70.dp)) { Text(text = "Stop", style = TextStyle(fontSize = 10.sp)) } }
需要注意的是 stop
方法也是一個(gè)掛起函數(shù),需要在協(xié)程中執(zhí)行
Animatable
可以通過 updateBounds
函數(shù)為動(dòng)畫設(shè)置邊界值,當(dāng)動(dòng)畫運(yùn)動(dòng)到邊界時(shí)會(huì)立即停止動(dòng)畫,updateBounds
定義如下:
fun updateBounds(lowerBound: T? = this.lowerBound, upperBound: T? = this.upperBound)
updateBounds
方法有兩個(gè)參數(shù) lowerBound
、upperBound
分別為動(dòng)畫的邊界下限值和上限值,默認(rèn)為 null
即不做限制,可以單獨(dú)設(shè)置上限和下限的值,當(dāng)設(shè)置對(duì)應(yīng)值后,動(dòng)畫運(yùn)行過程中動(dòng)畫值達(dá)到邊界值時(shí)就會(huì)立即停止動(dòng)畫。
示例代碼如下:
// 創(chuàng)建狀態(tài) 通過狀態(tài)驅(qū)動(dòng)動(dòng)畫 var moveToRight by remember { mutableStateOf(false) } // 動(dòng)畫實(shí)例 val animatable = remember { Animatable(10.dp, Dp.VectorConverter) } // 設(shè)置動(dòng)畫邊界 animatable.updateBounds(lowerBound = 10.dp, upperBound = 200.dp) val scope = rememberCoroutineScope() Box( Modifier // 使用動(dòng)畫值 .padding(start = animatable.value, top = 30.dp) .size(100.dp, 100.dp) .background(Color.Blue) .clickable { // 點(diǎn)擊事件 // 修改狀態(tài) moveToRight = !moveToRight scope.launch { // 執(zhí)行動(dòng)畫 animatable.animateTo( // 根據(jù)狀態(tài)設(shè)置動(dòng)畫的目標(biāo)值,分別是向右到 400dp 和 向左到 -100dp 位置 if (moveToRight) 400.dp else -100.dp, animationSpec = tween(durationMillis = 1000) ) } } )
上面代碼分別設(shè)置了下限值為 10dp、上限值為 200dp,同時(shí)動(dòng)畫目標(biāo)值分別設(shè)置為 -100dp 和 400dp:
可以看出來,雖然動(dòng)畫目標(biāo)設(shè)置分別設(shè)置了 -100dp 和 400dp,但是因?yàn)槲覀冊(cè)O(shè)置了邊界值為 10dp 和 200dp,所以動(dòng)畫向右運(yùn)動(dòng)時(shí)到達(dá)邊界值即 200dp 位置時(shí)就停止了,向左同樣的到達(dá)邊界值 10dp 也停止了動(dòng)畫,這就是動(dòng)畫邊界的作用。
之前介紹了 Compose 動(dòng)畫是可以作用于多維數(shù)值的,比如作用于 Size、Offset、React 等數(shù)據(jù)時(shí)就是多維的動(dòng)畫,此時(shí)對(duì)動(dòng)畫設(shè)置邊界后,動(dòng)畫目標(biāo)值只要有其中一維的數(shù)值達(dá)到邊界就會(huì)立即停止,并不會(huì)等到所有維的數(shù)值都達(dá)到邊界才會(huì)停止。
下面用一個(gè)示例來舉例說明,還是上面的方塊動(dòng)畫,上面只進(jìn)行了橫向的動(dòng)畫,如果我們要同時(shí)進(jìn)行橫向和豎向的動(dòng)畫,可以使用 Offset 來進(jìn)行動(dòng)畫,然后對(duì)其進(jìn)行邊界設(shè)置來觀察效果,代碼如下:
// 創(chuàng)建狀態(tài) 通過狀態(tài)驅(qū)動(dòng)動(dòng)畫 var moveToRight by remember { mutableStateOf(false) } // 動(dòng)畫實(shí)例 val animatable = remember { Animatable(Offset(10f, 30f), Offset.VectorConverter) } // 設(shè)置邊界值,下限:Offset(10f, 30f) 上限:Offset(400f, 200f) animatable.updateBounds(lowerBound = Offset(10f, 30f), upperBound = Offset(400f, 200f)) val scope = rememberCoroutineScope() Box { Box( Modifier // 使用動(dòng)畫值 .padding(start = animatable.value.x.dp, top = animatable.value.y.dp) .size(100.dp, 100.dp) .background(Color.Blue) .clickable { // 修改狀態(tài) moveToRight = !moveToRight scope.launch { animatable.animateTo( // 根據(jù)狀態(tài)設(shè)置動(dòng)畫的目標(biāo)值 // 分別為向右和向下的 Offset(400f,400f) // 向上和向左的 Offset(-100f,0f) if (moveToRight) Offset(400f,400f) else Offset(-100f,0f), animationSpec = tween(durationMillis = 1000) ) } } ) }
可以發(fā)現(xiàn),上限設(shè)置為 Offset(400f, 200f)
即 x 軸最大為 400dp、y 軸最大為 200dp,動(dòng)畫目標(biāo)值為Offset(400f,400f)
,當(dāng)方塊移動(dòng)到 y 坐標(biāo)為 200dp 時(shí) y 坐標(biāo)的值達(dá)到邊界值,動(dòng)畫就停止了,此時(shí) x 坐標(biāo)值并未達(dá)到邊界值。同樣的往回執(zhí)行時(shí) x 坐標(biāo)先觸發(fā)達(dá)到邊界值 10dp 時(shí)停止了動(dòng)畫。
如果就是想讓動(dòng)畫都達(dá)到邊界才停止,此時(shí)不應(yīng)該采用多維動(dòng)畫的方式,而是應(yīng)該使用多個(gè)單維動(dòng)畫,對(duì)其分別設(shè)置邊界即可。
動(dòng)畫停止可分為異常停止和正常停止,其中打斷和主動(dòng)停止動(dòng)畫屬于異常停止,動(dòng)畫運(yùn)行完成或達(dá)到邊界后停止屬于正常停止。
一個(gè)動(dòng)畫打斷另一個(gè)動(dòng)畫,或調(diào)用 stop
主動(dòng)停止動(dòng)畫都屬于異常停止,此時(shí)動(dòng)畫會(huì)拋出 CancellationException
的異常,在代碼中可通過捕獲該異常來監(jiān)聽動(dòng)畫的異常停止,代碼如下:
scope.launch { try { // 異常停止時(shí)動(dòng)畫會(huì)拋出異常,不會(huì)正常返回動(dòng)畫結(jié)果 val animationResult = animatable.animateTo(200.dp, animationSpec = tween(durationMillis = 1000) ) // 動(dòng)畫異常停止時(shí)會(huì)拋出異常,下面的代碼不會(huì)被執(zhí)行 // do something } catch (e: CancellationException) { Log.e("ANIMATOIN", "動(dòng)畫異常停止") } }
動(dòng)畫觸達(dá)邊界停止屬于正常停止,此時(shí)動(dòng)畫會(huì)正常返回結(jié)果,從結(jié)果中的 endReason
可判斷動(dòng)畫是否為觸達(dá)邊界停止,代碼如下:
scope.launch { val animationResult = animatable.animateTo(200.dp, animationSpec = tween(durationMillis = 1000) ) // 判斷動(dòng)畫是否觸達(dá)邊界停止 if(animationResult.endReason == AnimationEndReason.BoundReached){ // do something } }
同時(shí)動(dòng)畫結(jié)果還能拿到動(dòng)畫停止時(shí)的速度等數(shù)據(jù),這樣就能通過監(jiān)聽動(dòng)畫邊界停止進(jìn)行自定義的處理,比如結(jié)合上一篇介紹的衰減動(dòng)畫讓動(dòng)畫到達(dá)邊界后反向運(yùn)動(dòng),代碼如下:
@Preview @Composable fun AnimationBound3() { // 動(dòng)畫實(shí)例 val animatable = remember { Animatable(10.dp, Dp.VectorConverter) } // 設(shè)置邊界值 animatable.updateBounds(upperBound = 200.dp, lowerBound = 10.dp) val scope = rememberCoroutineScope() val splineBasedDecay = rememberSplineBasedDecay<Dp>() Box( Modifier // 使用動(dòng)畫值 .padding(start = animatable.value, top = 30.dp) .size(100.dp, 100.dp) .background(Color.Blue) .clickable { scope.launch { // 啟動(dòng)動(dòng)畫,設(shè)置初始速度為 3000dp val animationResult = animatable.animateDecay(3000.dp, splineBasedDecay) // 判斷是否為到達(dá)邊界停止 if(animationResult.endReason == AnimationEndReason.BoundReached){ // 執(zhí)行反向動(dòng)畫,初始速度取動(dòng)畫結(jié)束時(shí)速度的負(fù)數(shù) reverseAnimation(animatable, -animationResult.endState.velocity, splineBasedDecay) } } } ) } /// 反向執(zhí)行動(dòng)畫 private suspend fun reverseAnimation( animatable: Animatable<Dp, AnimationVector1D>, initialVelocity: Dp, splineBasedDecay: DecayAnimationSpec<Dp> ) { val result = animatable.animateDecay(initialVelocity, splineBasedDecay) // 判斷是邊界停止時(shí)遞歸執(zhí)行反向動(dòng)畫直到動(dòng)畫非邊界停止 if(result.endReason == AnimationEndReason.BoundReached){ reverseAnimation(animatable, -result.endState.velocity, splineBasedDecay) } }
到此,相信大家對(duì)“Android Compose之Animatable動(dòng)畫停止怎么使用”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。