Compose開(kāi)發(fā)之動(dòng)畫藝術(shù)探索及實(shí)現(xiàn)示例
基石
想要蓋起高樓的話肯定要打好基石,同樣,想要學(xué)好 Compose 動(dòng)畫也需要一些“基石”,一起來(lái)看看吧!
Kotlin
Compose 是使用 Kotlin 來(lái)進(jìn)行編寫的,所以如果到現(xiàn)在還沒(méi)學(xué)習(xí) Kotlin 的要抓緊了,不是說(shuō) Java 不好,只不過(guò)官方現(xiàn)在新的庫(kù)基本都是 Kotlin 編寫的,雖然大部分都支持 Java ,但總有例外,就比如要說(shuō)的 Compose 就沒(méi)有辦法使用 Java 來(lái)進(jìn)行編寫,也不是官方故意的,實(shí)在是因?yàn)楹枚嗾Z(yǔ)言特性的關(guān)系;當(dāng)然,不學(xué) Kotlin 肯定也沒(méi)問(wèn)題!但此專欄的內(nèi)容都使用的是 Kotlin ??。
Compose 基礎(chǔ)
前面一直在說(shuō) Compose 中的動(dòng)畫,所以 Compose 基礎(chǔ)如果不知道的話就不太好玩了,大家可以去看我的書或者直接去看官方文檔,都可以把基礎(chǔ)搞定。
這個(gè)和上面 Kotlin 一樣,沒(méi)有 Compose 基礎(chǔ)的話看 Compose 的動(dòng)畫肯定看的云里霧里,雖然代碼看著都不難,但會(huì)感覺(jué)很怪。
有上面所說(shuō)的兩個(gè)“基石”就足夠了,一定先打好地基再去蓋樓!
什么是動(dòng)畫
對(duì)啊,什么是動(dòng)畫呢?說(shuō)到動(dòng)畫我首先想到的就是動(dòng)畫片,哈哈哈,其實(shí)原理都差不多,最開(kāi)始的動(dòng)畫不就是繪制一張張漸變的圖片,然后一秒切換24張圖片么!
來(lái)看下百度百科是怎么說(shuō)的:
一般二維動(dòng)畫,都是以一秒24幀為標(biāo)準(zhǔn),以保證畫面播放流暢,但由于現(xiàn)代科技的發(fā)達(dá),動(dòng)畫幀數(shù)可以不用達(dá)到一秒24幀。
在之前的安卓中一般有以下三種動(dòng)畫:
View Animation: 視圖動(dòng)畫在古老的安卓版本系統(tǒng)中就已經(jīng)提供了,只能被用來(lái)設(shè)置 View 的動(dòng)畫,目前基本沒(méi)啥人使用了。
Drawable Animation: 也就是咱們常說(shuō)的幀動(dòng)畫,幀動(dòng)畫其實(shí)可以劃分到視圖動(dòng)畫的類別,專門用來(lái)顯示多個(gè) Drawable 的 Resources,就像放幻燈片一樣。
Property Animation: 屬性動(dòng)畫只對(duì) Android 3.0(API 11) 以上版本的安卓系統(tǒng)才有效,這種動(dòng)畫可以設(shè)置給任何Object,包括那些還沒(méi)有渲染到屏幕上的對(duì)象。這種動(dòng)畫是可擴(kuò)展的,可以讓你自定義任何類型和屬性的動(dòng)畫,目前使用最多的應(yīng)該就屬屬性動(dòng)畫了。
上面就是之前安卓中的動(dòng)畫分類,由于咱們此專欄主要說(shuō) Compose 的動(dòng)畫,所以對(duì)于之前安卓中的動(dòng)畫就不做過(guò)多介紹,還有就是大家應(yīng)該都會(huì),就不在此班門弄斧了。
瞅下 Compose 的動(dòng)畫
為啥說(shuō)瞅下 Compose 的動(dòng)畫呢,因?yàn)閯?dòng)畫從來(lái)都不是一個(gè)簡(jiǎn)單的東西,簡(jiǎn)單使用都沒(méi)問(wèn)題,但要真的想要玩的好、玩的花并不容易。今天先帶大家來(lái)看看 Compose 中一些開(kāi)箱即用的動(dòng)畫 api,動(dòng)畫效果很不錯(cuò)呦!哈哈哈!
可見(jiàn)性動(dòng)畫——AnimatedVisibility
這個(gè)動(dòng)畫是 Compose 提供給我們的一個(gè)動(dòng)畫 api,顧名思義,這個(gè) api 可以為內(nèi)容的出現(xiàn)和消失添加動(dòng)畫效果,廢話不多說(shuō),先上實(shí)現(xiàn)效果!

上圖中的動(dòng)畫效果如果在原生安卓中實(shí)現(xiàn)的話雖然不說(shuō)很難但也絕對(duì)不輕松,但在 Compsoe 中就不一樣了,甚至可以說(shuō)簡(jiǎn)單的過(guò)分,來(lái)看下實(shí)現(xiàn)的關(guān)鍵代碼有多少吧:
val visible = remember { mutableStateOf(true) }
AnimatedVisibility(visible = visible.value,) {
Text(text = "天青色等煙雨,而我在等你,炊煙裊裊升起,隔江千萬(wàn)里")
}
對(duì),你沒(méi)看錯(cuò),只有四行代碼,而且還有一行是括號(hào),一行是內(nèi)容,其實(shí)只有兩行關(guān)鍵代碼!
如果這個(gè)時(shí)候吃驚就不合適了,這只是 Compose 中動(dòng)畫的基操,Compose 還為我們提供了很多常用的動(dòng)畫效果供我們使用,接著來(lái)看實(shí)現(xiàn)效果吧!

看著上面的效果是不是感覺(jué)挺好,但一共也沒(méi)多添加幾行代碼,還是來(lái)看下關(guān)鍵實(shí)現(xiàn)代碼吧!
VisibleItem() VisibleItem(fadeIn(), fadeOut()) VisibleItem(scaleIn(), scaleOut()) VisibleItem(expandIn(), shrinkOut()) VisibleItem(expandHorizontally(), shrinkHorizontally()) VisibleItem(expandVertically(), shrinkVertically())
每一行對(duì)應(yīng)著一條 Item,下面來(lái)看下 VisibleItem 可組合項(xiàng)的代碼吧:
@Composable
private fun VisibleItem(
enter: EnterTransition = fadeIn() + expandHorizontally(),
exit: ExitTransition = fadeOut() + shrinkHorizontally(),
) {
val visible = remember { mutableStateOf(true) }
Card(modifier = Modifier.padding(10.dp)) {
Row{
Image(
painter = painterResource(id = R.drawable.ic_head),
contentDescription = "head",
)
AnimatedVisibility(
visible = visible.value,
enter = enter,
exit = exit
) {
Text(text = "天青色等煙雨,而我在等你,炊煙裊裊升起,隔江千萬(wàn)里")
}
}
}
}
可以看到上面具體代碼也不多,簡(jiǎn)單說(shuō)下吧,VisibleItem 可組合項(xiàng)接收兩個(gè)參數(shù),用來(lái)設(shè)置進(jìn)入和退出的動(dòng)畫,然后添加了一個(gè)狀態(tài)值(用于表示是否展示),之后添加了一個(gè) Card ,其實(shí)沒(méi)什么用,只是覺(jué)得圓角的好看??,Card 中包裹著一個(gè)橫向布局,橫向布局中一個(gè)圖片和一段文字,文字使用 AnimatedVisibility 包裹著來(lái)實(shí)現(xiàn)內(nèi)容的動(dòng)畫。
其實(shí)工作中經(jīng)常有這么一種情況,動(dòng)畫并不是點(diǎn)擊來(lái)觸發(fā)的,而是當(dāng)進(jìn)入頁(yè)面的時(shí)候就會(huì)觸發(fā),這個(gè)時(shí)候該怎么實(shí)現(xiàn)呢?Compose 也為我們想到了,只要將 AnimatedVisibility 添加到組合樹中,就可以立即觸發(fā)動(dòng)畫:
@Composable
private fun VisibleItem(
enter: EnterTransition = fadeIn() + expandHorizontally(),
exit: ExitTransition = fadeOut() + shrinkHorizontally(),
) {
val visible = remember { mutableStateOf(true) }
val state = remember {
MutableTransitionState(false).apply {
// Start the animation immediately.
targetState = true
}
}
Card(modifier = Modifier.padding(10.dp)) {
.....
AnimatedVisibility(
/// 設(shè)置visibleState
visibleState = state,
enter = enter,
exit = exit
) {
.....
}
像上面這樣寫就可以實(shí)現(xiàn)進(jìn)入頁(yè)面的時(shí)候就會(huì)觸發(fā)動(dòng)畫,來(lái)看看效果吧:

可以看到上圖顯示效果其實(shí)并不太好,沒(méi)有辦法,Gif圖壓縮完就這樣,其實(shí)日常開(kāi)發(fā)咱們也經(jīng)常遇到這種情況,動(dòng)畫時(shí)間太短,看不出是否是動(dòng)效工程師想要的效果,這樣吧,給動(dòng)畫多加點(diǎn)時(shí)間吧,然后咱們?cè)倏纯葱Ч桑?/p>
VisibleItem(
scaleIn(animationSpec = tween(durationMillis = 1500)),
scaleOut(animationSpec = tween(durationMillis = 1500))
)
VisibleItem(
expandIn(animationSpec = tween(durationMillis = 1500)),
shrinkOut(animationSpec = tween(durationMillis = 1500))
)
簡(jiǎn)單寫兩個(gè)大家能看明白是修改了動(dòng)畫持續(xù)時(shí)間就行,里面具體的各種參數(shù)和使用方法等等會(huì)在之后的文章中進(jìn)行細(xì)聊,一個(gè)專欄都是關(guān)于 Compose 動(dòng)畫的,今天只是帶大家瞅一瞅!沒(méi)事,肯定都能弄明白,再來(lái)看下效果吧!

這回看著就好多了,在實(shí)際工作中我也經(jīng)常使用這種方式來(lái)進(jìn)行調(diào)試動(dòng)畫效果,大家也可以試試,當(dāng)然 Compose 中有更方便的動(dòng)畫調(diào)試,這個(gè)咱們之后在細(xì)說(shuō)。
屬性動(dòng)畫——animate*AsState
說(shuō)屬性動(dòng)畫前像先問(wèn)大家一個(gè)問(wèn)題,什么是屬性動(dòng)畫呢?
屬性動(dòng)畫是通過(guò)不斷地修改值來(lái)實(shí)現(xiàn)的,而初始值和結(jié)束值之間的過(guò)渡動(dòng)畫就需要來(lái)計(jì)算了,之前我們?cè)谠沧恐袝?huì)使用到 ValueAnimator 、 ObjectAnimator 等 api 來(lái)實(shí)現(xiàn),但寫過(guò)或者了解過(guò) Compose 的都知道 Compose 是聲明式的,同安卓現(xiàn)有的構(gòu)建 UI 的模型完全不同,Compose 中的動(dòng)畫是重寫的一套,至于 Compose 為什么不直接移植屬性動(dòng)畫到 Compose 這個(gè)問(wèn)題,我記得之前扔物線專門出過(guò)一個(gè)視頻來(lái)說(shuō)這個(gè),感興趣的可以去搜一下,總結(jié)下來(lái)就是之前安卓中的屬性動(dòng)畫都是基于 View 的,而 Comppose 中我們拿不到可操作的 View ,所以移植屬性動(dòng)畫也就無(wú)從談起了。
在 Compose 中為我們提供了一整套 api 來(lái)實(shí)現(xiàn)屬性動(dòng)畫,就是本小節(jié)的標(biāo)題:animate*AsState ,具體有哪些呢?來(lái)看下吧!

官方為我們提供了上圖這十種方法,我們可以根據(jù)實(shí)際項(xiàng)目中的需求進(jìn)行挑選使用,這塊咱們隨便挑選一個(gè)使用看下吧。
@Composable
fun AnimateAsStateTest() {
var isSmall by remember { mutableStateOf(true) }
val size: Dp by animateDpAsState(
targetValue = if (isSmall) 40.dp else 100.dp,
animationSpec = tween(durationMillis = 1500))
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Button(onClick = { isSmall = !isSmall }) {
Text("修改Dp值")
}
Box(modifier = Modifier.size(size).background(Red))
}
}
上面代碼中使用了 animateDpAsState ,并又增加了動(dòng)畫時(shí)長(zhǎng)便于看的更清楚,然后通過(guò)按鈕控制 Box 的大小,來(lái)運(yùn)行看下動(dòng)畫效果吧!

Gif圖看著還是有點(diǎn)卡頓,實(shí)際運(yùn)行其實(shí)非常流暢,大家可以在本地編碼試一下,效果真的不錯(cuò)。
別的一些就不在這里一一進(jìn)行展示了,大家可以在本地試試。
內(nèi)容大小動(dòng)畫——animateContentSize
這個(gè)動(dòng)畫其實(shí)在實(shí)際工作中使用的挺頻繁,比如說(shuō)一些動(dòng)態(tài)內(nèi)容過(guò)多顯示不全的情況,就會(huì)有點(diǎn)擊“全文”顯示完整內(nèi)容,當(dāng)閱讀完畢后又會(huì)點(diǎn)擊“收起”來(lái)收起內(nèi)容。
之前如果想實(shí)現(xiàn)這個(gè)功能就需要花點(diǎn)心思了,但是現(xiàn)在使用 Compose 來(lái)實(shí)現(xiàn)的話就很簡(jiǎn)單了!先來(lái)看下實(shí)現(xiàn)代碼:
val expend = remember { mutableStateOf(false) }
Column(modifier = Modifier.padding(10.dp)) {
Text(
text = "朋友圈一般指的是騰訊微信上的一個(gè)社交功能,于微信4.0版本2012年4月19日更新時(shí)上線“+
"用戶可以通過(guò)朋友圈發(fā)表文字和圖片,同時(shí)可通過(guò)其他軟件將文章或者音樂(lè)分享到朋友圈。"+
"用戶可以對(duì)好友新發(fā)的照片進(jìn)行“評(píng)論”或“贊”,其他用戶只能看相同好友的評(píng)論或贊。",
modifier = Modifier.animateContentSize(), /// 這塊調(diào)用了內(nèi)容大小動(dòng)畫
maxLines = if (expend.value) Int.MAX_VALUE else 2 /// 這塊控制內(nèi)容顯示多少
)
Text(if (expend.value) "收起" else "全文", color = Color.Blue, modifier = Modifier.clickable {
expend.value = !expend.value
})
}
可以看到代碼并不多,對(duì)內(nèi)容添加了大小動(dòng)畫,然后用一個(gè) Text 的點(diǎn)擊事件來(lái)控制內(nèi)容的展示,再來(lái)看下實(shí)際效果!

效果還是可以的,哈哈哈
重復(fù)動(dòng)畫——rememberInfiniteTransition
重復(fù)動(dòng)畫在日常開(kāi)發(fā)中使用的頻率也非常高!比如加載動(dòng)畫,在未加載成功之前會(huì)一直重復(fù)播放,或者空頁(yè)面的動(dòng)畫等等,來(lái)看下簡(jiǎn)單實(shí)用吧!
@Composable
fun InfiniteTransition() {
val infiniteTransition = rememberInfiniteTransition()
val size by infiniteTransition.animateValue(
initialValue = 100.dp,
targetValue = 200.dp,
typeConverter = TwoWayConverter({ AnimationVector1D(it.value) }, { it.value.dp }),
animationSpec = infiniteRepeatable(
animation = tween(1000, easing = LinearEasing),
repeatMode = RepeatMode.Reverse
)
)
Box(
Modifier.size(size).padding(20.dp).background(Color.Red)
)
}
InfiniteTransition 可以像 Transition 一樣保存一個(gè)或多個(gè)子動(dòng)畫,但是,這些動(dòng)畫一進(jìn)入組合階段就開(kāi)始運(yùn)行,除非被移除,否則不會(huì)停止,可以使用 animateColor、animatedFloat 或 animatedValue 添加子動(dòng)畫。
上面代碼中使用了 animateValue ,來(lái)無(wú)限重復(fù)修改 Box 的大小,來(lái)看下實(shí)際效果吧!

大家可以觀察下運(yùn)行的效果,發(fā)現(xiàn)點(diǎn)什么問(wèn)題了嗎?Box 由小到大后執(zhí)行完一次動(dòng)畫后并沒(méi)雨直接變小再緩慢變大,而是緩慢變大然后緩慢變小再緩慢變大這樣重復(fù),這是因?yàn)樵O(shè)置了 repeatMode 為 Reverse , repeatMode 還可以設(shè)置為 Restart ,修改再看下運(yùn)行效果!

是不是效果完全不同,哈哈哈!
結(jié)尾
本篇文章簡(jiǎn)單帶大家瞅了一眼 Compose 中的開(kāi)箱即用的動(dòng)畫,通過(guò)簡(jiǎn)單的一瞅大家可以感受到在 Compsoe 中使用動(dòng)畫的簡(jiǎn)單便捷,這些都是 Compose 為我們封裝好的一些 api,當(dāng)然咱們也可以進(jìn)行自定義。
其實(shí)今天說(shuō)的只是 Compose 動(dòng)畫的冰山一角,動(dòng)畫還有非常多可玩的東西,在之后的文章中會(huì)詳細(xì)道來(lái)。
本文中所有代碼都在 Github 中,有需要可以去看:github.com/zhujiang521…
以上就是Compose開(kāi)發(fā)之動(dòng)畫藝術(shù)探索及實(shí)現(xiàn)示例的詳細(xì)內(nèi)容,更多關(guān)于Compose開(kāi)發(fā)動(dòng)畫藝術(shù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Android Compose 屬性動(dòng)畫使用探索詳解
- Android中分析Jetpack?Compose動(dòng)畫內(nèi)部的實(shí)現(xiàn)原理
- Jetpack Compose實(shí)現(xiàn)列表和動(dòng)畫效果詳解
- Jetpack Compose實(shí)現(xiàn)動(dòng)畫效果的方法詳解
- 通過(guò)Jetpack Compose實(shí)現(xiàn)雙擊點(diǎn)贊動(dòng)畫效果
- 利用Jetpack Compose繪制可愛(ài)的天氣動(dòng)畫
- Android之Compose頁(yè)面切換動(dòng)畫介紹
- Compose?動(dòng)畫藝術(shù)之屬性動(dòng)畫探索
相關(guān)文章
Android超實(shí)用的Toast提示框優(yōu)化分享
Toast是Android中用來(lái)顯示顯示信息的一種機(jī)制,和Dialog不一樣的是,Toast是沒(méi)有焦點(diǎn)的,而且Toast顯示的時(shí)間有限,過(guò)一定的時(shí)間就會(huì)自動(dòng)消失。那么這篇文章跟大家分享下Android中Toast的優(yōu)化,對(duì)大家日常開(kāi)發(fā)還是很實(shí)用,下面來(lái)一起看看吧。2016-09-09
Android Studio 4.0 新功能中的Live Layout Inspector詳解
這篇文章主要介紹了Android Studio 4.0 新功能中的Live Layout Inspector,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06
Flutter框架實(shí)現(xiàn)Android拖動(dòng)到垃圾桶刪除效果
這篇文章主要介紹了Flutter框架實(shí)現(xiàn)Android拖動(dòng)到垃圾桶刪除效果,Flutter框架中的Draggable部件,用于支持用戶通過(guò)手勢(shì)拖動(dòng),它是基于手勢(shì)的一種方式,可以使用戶可以在屏幕上拖動(dòng)指定的部件,下面我們來(lái)詳細(xì)了解一下2023-12-12
詳解Android如何自定義view實(shí)現(xiàn)圓形進(jìn)度條
Android中實(shí)現(xiàn)進(jìn)度條有很多種方式,自定義進(jìn)度條一般是繼承progressBar或繼承view來(lái)實(shí)現(xiàn)。本文將介紹通過(guò)自定義View實(shí)現(xiàn)的圓形進(jìn)度條,感興趣的可以動(dòng)手試一試2022-01-01
簡(jiǎn)單實(shí)現(xiàn)Android應(yīng)用的啟動(dòng)頁(yè)
這篇文章主要介紹了簡(jiǎn)單實(shí)現(xiàn)Android應(yīng)用的啟動(dòng)頁(yè),文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09
Android?TextView跑馬燈實(shí)現(xiàn)原理及方法實(shí)例
字的跑馬燈效果在移動(dòng)端開(kāi)發(fā)中是一個(gè)比較常見(jiàn)的需求場(chǎng)景,下面這篇文章主要給大家介紹了關(guān)于Android?TextView跑馬燈實(shí)現(xiàn)原理及方法的相關(guān)資料,需要的朋友可以參考下2022-05-05
Android使用OkHttp進(jìn)行網(wǎng)絡(luò)同步異步操作
這篇文章主要為大家詳細(xì)介紹了Android使用OkHttp進(jìn)行網(wǎng)絡(luò)同步異步操作,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07

