欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android Compose 屬性動(dòng)畫(huà)使用探索詳解

 更新時(shí)間:2022年09月27日 14:54:37   作者:loongwind  
這篇文章主要為大家介紹了Android Compose 屬性動(dòng)畫(huà)使用探索詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

Jetpack Compose(簡(jiǎn)稱(chēng) Compose )是 Google 官方推出的基于 Kotlin 語(yǔ)言的 Android 新一代 UI 開(kāi)發(fā)框架,其采用聲明式的 UI 編程特性使得 Android 應(yīng)用界面的編寫(xiě)和維護(hù)變得更加簡(jiǎn)單。

本專(zhuān)欄將詳細(xì)介紹在使用 Compose 進(jìn)行 UI 開(kāi)發(fā)中如何實(shí)現(xiàn)炫酷的動(dòng)畫(huà)效果。動(dòng)畫(huà)效果在 App 使用中至關(guān)重要,它使得 App 的交互更加自然流暢,用戶使用體驗(yàn)更加良好。

在傳統(tǒng)的 Android 開(kāi)發(fā)中有古老的 View 動(dòng)畫(huà)和目前流行的屬性動(dòng)畫(huà),如今 View 動(dòng)畫(huà)幾乎已被廣大開(kāi)發(fā)者所拋棄,屬性動(dòng)畫(huà)因其可以作用于任何對(duì)象的靈活和強(qiáng)大特性而被開(kāi)發(fā)者所擁抱。既然屬性動(dòng)畫(huà)這么強(qiáng)大,那么它是否能用在 Compose 開(kāi)發(fā)中呢?如果能那跟傳統(tǒng) UI 開(kāi)發(fā)中使用又有什么區(qū)別呢?本篇就帶領(lǐng)你來(lái)探索一下在 Compose 中屬性動(dòng)畫(huà)的使用。

使用探索

在傳統(tǒng) Android 開(kāi)發(fā)中,屬性動(dòng)畫(huà)使用得最多的是 ObjectAnimatorValueAnimator,接下來(lái)就探索一下在 Compose 中如何使用它們來(lái)實(shí)現(xiàn)動(dòng)畫(huà)效果。

ObjectAnimator 使用探索

首先看一下在傳統(tǒng) Android 開(kāi)發(fā)中如何使用屬性動(dòng)畫(huà),比如使用屬性動(dòng)畫(huà)實(shí)現(xiàn)豎直方向向下移動(dòng)的動(dòng)畫(huà):

val animator = ObjectAnimator.ofFloat(view, "translationY", 10f, 100f)
animator.start()

通過(guò) ObjectAnimator作用于 View 的 translationY屬性,不斷改變 translationY 的值從而實(shí)現(xiàn)動(dòng)畫(huà)效果,一個(gè)很簡(jiǎn)單的屬性動(dòng)畫(huà),這里就不貼運(yùn)行效果了。

那在 Compose 中能否使用 ObjectAnimator 呢?

下面使用 Compose 在界面上顯示一個(gè) 100dp*100dp 的藍(lán)色正方形方塊,代碼如下:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Box(Modifier.padding(start = 10.dp, top = 10.dp)
                .size(100.dp)
                .background(Color.Blue)
            )
        }
    }
}

運(yùn)行效果如下:

現(xiàn)在要同樣實(shí)現(xiàn)一個(gè)豎直方向移動(dòng)的動(dòng)畫(huà)效果,讓方塊從上往下移動(dòng)。在上面的屬性動(dòng)畫(huà)實(shí)現(xiàn)中 ObjectAnimator是作用于 View 組件上的,按照這個(gè)思路在這里 ObjectAnimator 就應(yīng)該作用于 Box 上,但實(shí)際上我們這里壓根拿不到 Box 的實(shí)例,因?yàn)檫@里的 Box 實(shí)際是一個(gè)函數(shù)且沒(méi)有返回值,看一下 Box 的源碼:

@Composable
fun Box(modifier: Modifier) {
    Layout({}, measurePolicy = EmptyBoxMeasurePolicy, modifier = modifier)
}

既然作用于 Box 上不行,那能不能作用于 State 上呢,Compose 是數(shù)據(jù)驅(qū)動(dòng) UI 刷新,通過(guò)數(shù)據(jù)狀態(tài)改變重組 UI 實(shí)現(xiàn)界面的刷新,把上面的 top 提取為一個(gè) State 再通過(guò) ObjectAnimator 去改變是否可行呢?改造代碼實(shí)驗(yàn)一下:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val topPadding:MutableState<Int> = mutableStateOf(10)
        
        val animator = ObjectAnimator.ofInt(topPadding, "value", 10, 100)
        animator.duration = 1000
        
        setContent {
            Box(Modifier.padding(start = 10.dp, top = topPadding.value.dp)
                .size(100.dp)
                .background(Color.Blue)
                // 添加點(diǎn)擊事件
                .clickable {
                    // 啟動(dòng)動(dòng)畫(huà)
                    animator.start()
                }
            )
        }
    }
}

改造如下:

  • 將之前 top 的固定值提取成了一個(gè) State 變量 topPadding,當(dāng) topPadding 的值發(fā)生改變時(shí)會(huì)重組界面從而讓界面刷新
  • 聲明了 ObjectAnimator 的 animator 變量,作用于 topPadding 的 value 屬性上,并設(shè)置動(dòng)畫(huà)值從 10 到 100,動(dòng)畫(huà)時(shí)長(zhǎng) 1000ms
  • 給 Box 添加點(diǎn)擊監(jiān)聽(tīng)事件啟動(dòng)動(dòng)畫(huà)

實(shí)際上寫(xiě)完這段代碼,編輯器就已經(jīng)有報(bào)錯(cuò)提示了,提示如下:

說(shuō)沒(méi)有找到帶 Int 參數(shù)的 setValue方法,那來(lái)看看 MutableState是否有 setValue 方法:

interface MutableState<T> : State<T> {
    override var value: T
    operator fun component1(): T
    operator fun component2(): (T) -> Unit
}

可以發(fā)現(xiàn) MutableState 中是有一個(gè) var 修飾的 value 變量的,說(shuō)明是有 setValue 方法的,但是錯(cuò)誤提示是找不到帶 Int 參數(shù)的 setValue 方法,實(shí)際上 MutableState 的 setValue 的定義應(yīng)該是這樣的:

fun setValue(value:T){
    this.value = value
}

這里參數(shù)類(lèi)型是泛型 T,而 ObjectAnimator 找的是明確的 Int 類(lèi)型參數(shù)的方法,所以找不到。那怎么辦呢?是不是就意味著在 Compose 中無(wú)法使用 ObjectAnimator 了呢?

直接使用確實(shí)是不行,那我們能不能對(duì)其進(jìn)行封裝,不是找不到對(duì)應(yīng)的 setValue 方法嘛,那我封裝一下提供一個(gè) setValue 方法不就行了。定義一個(gè) IntState類(lèi),再提供一個(gè) mutableIntStateOf方法:

class IntState(private val state: MutableState<Int>){
    var value : Int = state.value
        get() = state.value
        set(value) {
            field = value
            state.value = value
        }
}
fun mutableIntStateOf(value: Int, policy: SnapshotMutationPolicy<Int> = structuralEqualityPolicy()) : IntState{
    val state = mutableStateOf(value, policy)
    return IntState(state)
}

IntState構(gòu)造方法傳入一個(gè) MutableState 類(lèi)型的 state 參數(shù),然后提供一個(gè) value 變量,get 方法返回 state.value ,set 方法將傳入值設(shè)置給 state.value,這樣 IntState 就有了一個(gè)明確的 setValue(value:Int) 的方法。

為了便于使用,封裝一個(gè) mutableIntStateOf方法,實(shí)現(xiàn)里先采用 Compose 提供的 mutableStateOf 方法獲取一個(gè) MutableState ,然后用其構(gòu)建一個(gè) IntState 進(jìn)行返回。

再改造一下上面動(dòng)畫(huà)實(shí)現(xiàn)代碼將 mutableStateOf替換成 mutableIntStateOf

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 替換為 mutableIntStateOf
        val topPadding = mutableIntStateOf(10)
        // 創(chuàng)建 ObjectAnimator 目標(biāo)為 topPadding,作用屬性為 value,值從 10 變化到 100
        val animator = ObjectAnimator.ofInt(topPadding, "value", 10, 100)
        // 設(shè)置動(dòng)畫(huà)時(shí)長(zhǎng) 1s
        animator.duration = 1000
        
        setContent {
            Box(Modifier.padding(start = 10.dp, top = topPadding.value.dp)
                .size(100.dp)
                .background(Color.Blue)
                // 添加點(diǎn)擊事件
                .clickable {
                    // 啟動(dòng)動(dòng)畫(huà)
                    animator.start()
                }
            )
        }
    }
}

現(xiàn)在不報(bào)錯(cuò)了,運(yùn)行一下看看是否有動(dòng)畫(huà)效果:

效果符合預(yù)期,說(shuō)明這種辦法是可行,也說(shuō)明 ObjectAnimator 在 Compose 中也是可以使用的,只是不能像傳統(tǒng) Android 開(kāi)發(fā)那樣直接作用于 View 組件上,而是需要進(jìn)行二次封裝后使用。

ValueAnimator 使用探索

ObjectAnimator 使用探索完了,那么 ValueAnimator能否使用呢?Compose 以聲明式的方式通過(guò)數(shù)據(jù)驅(qū)動(dòng)界面刷新,而ValueAnimator主要用于數(shù)據(jù)的改變,好像很契合的樣子,使用 ValueAnimator 不斷改變 State 的值理論上就可以實(shí)現(xiàn)動(dòng)畫(huà)效果。還是上面的例子,改造成使用 ValueAnimator來(lái)實(shí)現(xiàn):

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 使用 mutableStateOf 創(chuàng)建 topPadding 的 State
        var topPadding by mutableStateOf(10)
        // 創(chuàng)建 ValueAnimator 從 10 變化到 100
        val animator = ValueAnimator.ofInt(10, 100)
        // 動(dòng)畫(huà)時(shí)長(zhǎng) 1s
        animator.duration = 1000
        // 設(shè)置監(jiān)聽(tīng),當(dāng)動(dòng)畫(huà)改變時(shí)動(dòng)態(tài)修改 topPadding 的值
        animator.addUpdateListener {
            topPadding = it.animatedValue as Int
        }
        setContent {
            Box(Modifier.padding(start = 10.dp, top = topPadding.dp)
                .size(100.dp)
                .background(Color.Blue)
                .clickable {
                    animator.start()
                }
            )
        }
    }
}

是否有效果呢?運(yùn)行一下看看效果:

跟上面使用 ObjectAnimator 實(shí)現(xiàn)的效果一致,說(shuō)明 ValueAnimator 在 Compose 中實(shí)現(xiàn)動(dòng)畫(huà)是可行的,只是需要手動(dòng)去監(jiān)聽(tīng) ValueAnimator 值的變化然后去動(dòng)態(tài)更新 State 的值,稍微麻煩了一點(diǎn),實(shí)際上我們也可以對(duì)其進(jìn)行封裝簡(jiǎn)化其使用。

通過(guò)上面的代碼發(fā)現(xiàn),如果要在 Compose 中使用 ValueAnimator 來(lái)實(shí)現(xiàn)動(dòng)畫(huà),對(duì)動(dòng)畫(huà)數(shù)值的改變進(jìn)行監(jiān)聽(tīng)并動(dòng)態(tài)更新 State 的值是必不可少的一步,那么我們就可以將其提取進(jìn)行封裝。

/**
 * @param state 動(dòng)畫(huà)作用的目標(biāo) State
 * @param values 動(dòng)畫(huà)的變化值,可變參數(shù)
 */
fun animatorOfInt(state:MutableState<Int>, vararg values: Int) : ValueAnimator{
    // 創(chuàng)建 ValueAnimator ,參數(shù)為傳入的 values
    val animator = ValueAnimator.ofInt(*values)
    // 添加監(jiān)聽(tīng)
    animator.addUpdateListener {
        // 更新 state 的 value 值
        state.value = it.animatedValue as Int
    }
    return animator
}

然后將上面的創(chuàng)建動(dòng)畫(huà)替換成使用 animatorOfInt 創(chuàng)建:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val topPadding = mutableStateOf(10)
        // 使用封裝的 animatorOfInt 方法創(chuàng)建動(dòng)畫(huà)
        val animator = animatorOfInt(topPadding, 10, 100)
        animator.duration = 1000
        setContent {
            Box(Modifier.padding(start = 10.dp, top = topPadding.value.dp)
                .size(100.dp)
                .background(Color.Blue)
                .clickable {
                    animator.start()
                }
            )
        }
    }
}

使用是不是要簡(jiǎn)單很多,不需要手動(dòng)去處理動(dòng)畫(huà)值變化的監(jiān)聽(tīng)了,有點(diǎn)使用 ObjectAnimator 的感覺(jué),只是不需要指定目標(biāo)屬性。運(yùn)行效果跟上面一致就不貼圖了。

Compose 函數(shù)中使用屬性動(dòng)畫(huà)

前面在 Compose 中使用的動(dòng)畫(huà)都是創(chuàng)建在 Compose 函數(shù)外面的,如果我們想把這個(gè)組件封裝成一個(gè)獨(dú)立的 Compose 組件就需要將動(dòng)畫(huà)的創(chuàng)建放到 Compose 函數(shù)里面,比如將上面的效果封裝成一個(gè) AnimationBox組件:

@Composable
fun AnimationBox(){
    val topPadding = mutableStateOf(10) 
    val animator = animatorOfInt(topPadding, 10, 100)
    animator.duration = 1000
    Box(modifier = Modifier.padding(start = 10.dp, top = topPadding.value.dp)
        .size(100.dp)
        .background(Color.Blue)
        .clickable {
            animator.start()
        })
}

首先 mutableStateOf 會(huì)報(bào)錯(cuò):

意思是在組合過(guò)程中創(chuàng)建 state 需要使用 remember,原因是當(dāng) state 里的值發(fā)生變化時(shí) Compose 會(huì)進(jìn)行重組導(dǎo)致函數(shù)重新執(zhí)行,如果 mutableStateOf 不加 remember則會(huì)每次重組都重新創(chuàng)建 state,導(dǎo)致 UI 上使用的值每次都是初始值而得不到刷新。

既然報(bào)錯(cuò)那就給他加上 remember:

@Composable
fun AnimationBox(){
    val topPadding = remember { mutableStateOf(10) }
    ...
}

然后在使用的地方直接使用 AnimationBox() 即可:

setContent {
    AnimationBox()
}

運(yùn)行后發(fā)現(xiàn)效果跟之前一樣,那是不是就可以了呢?

實(shí)際上上面的代碼是還存在問(wèn)題的,前面說(shuō)在 Compose 重組時(shí)會(huì)重新執(zhí)行 Compose 組件的代碼,也就是在界面刷新時(shí)會(huì)多次重復(fù)創(chuàng)建動(dòng)畫(huà)對(duì)象,我們?cè)?animatorOfInt 函數(shù)里添加一個(gè)日志再看看運(yùn)行時(shí)的日志輸出:

fun  animatorOfInt(state:MutableState<Int>, vararg values: Int) : ValueAnimator{
    println("-------call animatorOfInt--------")
  ...
}

輸出結(jié)果:

I/System.out: -------call animatorOfInt--------
I/System.out: -------call animatorOfInt--------
I/System.out: -------call animatorOfInt--------
I/System.out: -------call animatorOfInt--------
I/System.out: -------call animatorOfInt--------
I/System.out: -------call animatorOfInt--------
I/System.out: -------call animatorOfInt--------
I/System.out: -------call animatorOfInt--------
I/System.out: -------call animatorOfInt--------
I/System.out: -------call animatorOfInt--------
I/System.out: -------call animatorOfInt--------
I/System.out: -------call animatorOfInt--------
I/System.out: -------call animatorOfInt--------
I/System.out: -------call animatorOfInt--------
I/System.out: -------call animatorOfInt--------

日志確實(shí)輸出了多次,意味著動(dòng)畫(huà)確實(shí)創(chuàng)建了多次,那怎么解決呢?

前面說(shuō)了 remember可以解決重組時(shí)重復(fù)創(chuàng)建的問(wèn)題,所以只需在創(chuàng)建動(dòng)畫(huà)上套上 remember即可,如下:

val animator = remember { animatorOfInt(topPadding, 10, 100) }

修改后再看日志,發(fā)現(xiàn)就只在第一次進(jìn)行了創(chuàng)建,動(dòng)畫(huà)執(zhí)行過(guò)程中并沒(méi)有再次創(chuàng)建。

為了方便使用,可以再封裝一個(gè) rememberAnimatorOfInt方法:

@Composable
fun rememberAnimatorOfInt(state:MutableState<Int>, vararg values: Int) : ValueAnimator{
   return remember { animatorOfInt(state, *values) }
}

在 animatorOfInt 上套了一個(gè) remember,這樣使用時(shí)就可以直接使用 rememberAnimatorOfInt 方法:

val animator = rememberAnimatorOfInt(topPadding, 10, 100)

remember 是 Compose 提供的在 Compose 函數(shù)中緩存狀態(tài)的方法,解決在 Compose 重組時(shí)重復(fù)創(chuàng)建的問(wèn)題,關(guān)于 remember 更多使用大家可以自行查詢相關(guān)資料,本專(zhuān)欄主要講解動(dòng)畫(huà)的使用就不過(guò)多贅述。

實(shí)戰(zhàn)

前面介紹了屬性動(dòng)畫(huà)在 Compose 中的運(yùn)用,那在實(shí)際開(kāi)發(fā)中到底好不好用呢?接下來(lái)我們通過(guò)一個(gè)實(shí)例來(lái)看看。

先看一下最終實(shí)現(xiàn)的效果:

一個(gè)上傳按鈕的動(dòng)畫(huà)效果,動(dòng)畫(huà)主要分為三階段:

  • 上傳開(kāi)始
  • 按鈕從圓角矩形變成圓形
  • 按鈕顏色從藍(lán)色變成中間白色,邊框灰色
  • 文字逐漸消失
  • 上傳進(jìn)度
  • 邊框根據(jù)進(jìn)度變?yōu)樗{(lán)色
  • 上傳完成
  • 按鈕從圓形變成圓角矩形
  • 按鈕顏色變成紅色
  • 文字逐漸顯示,且文字變?yōu)?“Success”

上傳開(kāi)始動(dòng)畫(huà)

先把按鈕的初始狀態(tài)使用 Compose 實(shí)現(xiàn):

@Composable
fun UploadButton() {
    Box(
        modifier = Modifier
            .padding(start = 10.dp, top = 10.dp)
            .width(180.dp),
        contentAlignment = Alignment.Center
    ) {
        Box(
            modifier = Modifier
                .clip(RoundedCornerShape(24.dp))
                .background(Color.Blue)
                .size(180.dp, 48.dp),
            contentAlignment = Alignment.Center,
        ) {
            Text("Upload", color = Color.White)
        }
    }
}

運(yùn)行效果如下:

下面就為這按鈕添加動(dòng)畫(huà),前面講了動(dòng)畫(huà)主要作用于 State 上,所以需要先將使用到的數(shù)據(jù)提取成對(duì)應(yīng)的狀態(tài):

@Composable
fun UploadButton() {
    val originWidth = 180.dp
    val circleSize = 48.dp
    var text by remember { mutableStateOf("Upload") }
    val textAlpha = remember { mutableStateOf(1.0f) }
    val backgroundColor = remember { mutableStateOf(Color.Blue) }
    val boxWidth = remember { mutableStateOf(originWidth) }
    Box(
        modifier = Modifier
            .padding(start = 10.dp, top = 10.dp)
            .width(originWidth),
        contentAlignment = Alignment.Center
    ) {
        Box(
            modifier = Modifier
                .clip(RoundedCornerShape(height/2))
                .background(backgroundColor.value)
                .size(boxWidth.value, height),
            contentAlignment = Alignment.Center,
        ) {
            Text(text, color = Color.White, modifier = Modifier.alpha(textAlpha.value))
        }
    }
}

創(chuàng)建開(kāi)始上傳的動(dòng)畫(huà):

@Composable
fun UploadButton() {
  ...
    val uploadStartAnimator = remember {
        // 創(chuàng)建 AnimatorSet
        val animatorSet = AnimatorSet()
        // 按鈕寬度變化動(dòng)畫(huà)
        val widthAnimator = animatorOfDp(boxWidth, arrayOf(originWidth, circleSize))
        // 文字消失動(dòng)畫(huà)
        val textAnimator = animatorOfFloat(textAlpha, 1f, 0.0f)
        // 按鈕顏色動(dòng)畫(huà)
        val colorAnimator = animatorOfColor(backgroundColor, arrayOf(Color.Blue, Color.Gray))
        // 動(dòng)畫(huà)添加到 AnimatorSet
        animatorSet.playTogether(widthAnimator, textAnimator, colorAnimator)
        animatorSet
    }
    Box(...) {
        Box(
            modifier = Modifier
                ...
                .clickable {
                    // 點(diǎn)擊執(zhí)行動(dòng)畫(huà)
                    uploadStartAnimator.start()
                },
            ...
        )
    }
}

分別創(chuàng)建按鈕寬度、按鈕顏色和文字 alpha 值變化的動(dòng)畫(huà),因需同時(shí)執(zhí)行多個(gè)動(dòng)畫(huà),這里使用 AnimatorSet 進(jìn)行同時(shí)執(zhí)行,然后在按鈕上添加點(diǎn)擊事件進(jìn)行動(dòng)畫(huà)執(zhí)行。

上面的 animatorOfDp、animatorOfFloat、animatorOfColor都是自定義封裝的函數(shù),封裝方法與上面介紹的 animatorOfInt基本相同,源碼可通過(guò)文章最后附的源碼地址進(jìn)行查看。

運(yùn)行效果如下:

好像還差點(diǎn),中間應(yīng)該是白色的,在 Box 下再添加一個(gè)白色圓形的 Box,默認(rèn) alpha 是 0,上傳開(kāi)始時(shí) alpha 從 0 變成 1 :

@Composable
fun UploadButton() {
    ...
    val progressAlpha = remember { mutableStateOf(0.0f) }
    val uploadStartAnimator = remember {
        ...
        // 中間白色透明度變化動(dòng)畫(huà)
        val centerAlphaAnimator = animatorOfFloat(progressAlpha, 0.0f, 1f)
        animatorSet.playTogether(widthAnimator, textAnimator, colorAnimator, centerAlphaAnimator)
        animatorSet
    }
    Box(...) {
        Box(...) {
            // 白色圓形
            Box(
                modifier = Modifier.size(40.dp).clip(RoundedCornerShape(20.dp))
                    .alpha(progressAlpha.value).background(Color.White)
            )
            Text(text, color = Color.White, modifier = Modifier.alpha(textAlpha.value))
        }
    }
}

運(yùn)行效果如下:

上傳進(jìn)度動(dòng)畫(huà)

這里通過(guò)自定義 clip 的一個(gè)弧形的 shape 來(lái)實(shí)現(xiàn)進(jìn)度,自定義代碼如下:

class ArcShape(private val progress: Int) : Shape {
    override fun createOutline(
        size: Size,
        layoutDirection: LayoutDirection,
        density: Density
    ): Outline {
        val path = Path().apply {
            moveTo(size.width / 2f, size.height / 2f)
            arcTo(Rect(0f, 0f, size.width, size.height), -90f, progress / 100f * 360f, false)
            close()
        }
        return Outline.Generic(path)
    }
}

傳入一個(gè)進(jìn)度值(0-100),然后根據(jù)進(jìn)度值算出一個(gè)繪制的弧度,使用這個(gè)自定義的 ArcShape 代碼如下:

 Box(Modifier.size(48.dp).clip(ArcShape(30)).background(Color.Blue))

效果:

所以只需動(dòng)態(tài)改變 ArcShape 的 progress 參數(shù)的值就能實(shí)現(xiàn)上傳進(jìn)度效果,修改代碼如下:

@Composable
fun PreviewUploadButton() {
    ...
    val progress = remember { mutableStateOf(0) }
    //上傳進(jìn)度動(dòng)畫(huà)
    val progressAnimator = remember {
        val animator = animatorOfInt(progress, 0, 100)
        animator.duration = 1000
        animator
    }
    val uploadStartAnimator = remember {
        ...
        // 添加動(dòng)畫(huà)監(jiān)聽(tīng),完成后執(zhí)行進(jìn)度動(dòng)畫(huà)
        animatorSet.addListener(onEnd = {
            progressAnimator.start()
        })
        animatorSet
    }
    Box(...) {
        Box(...) {
            // 進(jìn)度 Box
            Box(
                modifier = Modifier.size(height).clip(ArcShape(progress.value))
                    .alpha(progressAlpha.value).background(Color.Blue)
            )
            ...
        }
    }
}

運(yùn)行效果:

上傳完成動(dòng)畫(huà)

最后是上傳完成動(dòng)畫(huà)就很簡(jiǎn)單了,基本就是開(kāi)始動(dòng)畫(huà)的反向,只是按鈕顏色從藍(lán)色變成了紅色,動(dòng)畫(huà)在上傳進(jìn)度動(dòng)畫(huà)完成時(shí)執(zhí)行:

@Composable
fun PreviewUploadButton() {
    ...
    
    val endAnimatorSet = remember {
        val animatorSet = AnimatorSet()
        val widthAnimator = animatorOfDp(boxWidth, arrayOf(circleSize, originWidth))
        val centerAnimator = animatorOfFloat(progressAlpha, 1f, 0f)
        val textAnimator = animatorOfFloat(textAlpha, 0f, 1f)
        val colorAnimator = animatorOfColor(backgroundColor, arrayOf(Color.Blue, Color.Red))
        animatorSet.playTogether(widthAnimator, centerAnimator, textAnimator, colorAnimator)
        animatorSet.addListener(onStart = {
            text = "Success"
        })
        animatorSet
    }
    val progressAnimator = remember {
        val animator = animatorOfInt(progress, 0, 100)
        animator.duration = 1000
        animator.addListener(onEnd = {
            endAnimatorSet.start()
        })
        animator
    }
    ...
}

最終效果:

最后

通過(guò)本篇文章的探索可以發(fā)現(xiàn)屬性動(dòng)畫(huà)在 Compose 中確實(shí)是可以使用的,雖然跟傳統(tǒng) UI 開(kāi)發(fā)中使用屬性動(dòng)畫(huà)有所區(qū)別,但確實(shí)能用,而且通過(guò)一個(gè)簡(jiǎn)單的實(shí)戰(zhàn)示例發(fā)現(xiàn)好像還挺好用的。好了,我已經(jīng)學(xué)會(huì) Compose 的動(dòng)畫(huà)開(kāi)發(fā)了,什么?Compose 還單獨(dú)提供了一套動(dòng)畫(huà) API ?

屬性動(dòng)畫(huà)這不是挺好使的么,這不是多此一舉么,難道 Compose 的動(dòng)畫(huà) API 比屬性動(dòng)畫(huà)還好用、還強(qiáng)大?如果感興趣請(qǐng)關(guān)注本專(zhuān)欄,從下一篇開(kāi)始帶你真正走進(jìn) Compose 的動(dòng)畫(huà)世界。

源碼地址:ComposeAnimationDemo

以上就是Android Compose 屬性動(dòng)畫(huà)使用探索詳解的詳細(xì)內(nèi)容,更多關(guān)于Android Compose 屬性動(dòng)畫(huà)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論