Android Compose 框架隱式動畫之過渡動畫深入剖析(二十六)

Android Compose 框架隱式動畫之過渡動畫深入剖析

一、引言

在移動應用開發領域,用戶體驗始終是至關重要的。動畫效果作為提升用戶體驗的關鍵元素,能夠為應用增添生動性和交互性。Android Compose 作為現代 Android UI 工具包,為開發者提供了豐富且強大的動畫支持,其中隱式動畫里的過渡動畫(Transition、Crossfade)尤為引人注目。

過渡動畫能夠在界面元素的狀態發生變化時,自動創建平滑的動畫過渡效果,讓界面的變化更加自然和流暢。Transition?可以讓開發者精確控制多個屬性在不同狀態之間的過渡,而?Crossfade?則專注于實現內容之間的淡入淡出過渡。深入理解這兩種過渡動畫的原理和使用方法,有助于開發者在 Android Compose 應用中創建出更加出色的用戶界面。

本文將從源碼級別深入分析?Transition?和?Crossfade,詳細介紹它們的工作原理、核心方法和使用場景,并通過豐富的示例代碼和詳細的注釋,幫助開發者更好地掌握這兩種過渡動畫的使用。

二、Android Compose 隱式動畫概述

2.1 隱式動畫的概念

隱式動畫是 Android Compose 中一種非常便捷的動畫實現方式。它允許開發者在定義界面元素的屬性時,只需指定屬性的目標值,Compose 框架會自動根據屬性的變化創建動畫過渡效果。與顯式動畫不同,顯式動畫需要開發者手動控制動畫的啟動、停止和每一個關鍵幀,而隱式動畫則由框架自動處理這些細節,大大簡化了動畫的實現過程。

例如,當一個按鈕的顏色從紅色變為藍色時,使用隱式動畫,開發者只需要更新按鈕顏色的屬性值,Compose 框架會自動創建一個顏色漸變的動畫過渡,讓按鈕顏色的變化更加平滑。

2.2 過渡動畫在隱式動畫中的地位

過渡動畫是隱式動畫的重要組成部分,它主要用于處理界面元素在不同狀態之間的過渡效果。在 Android Compose 中,狀態的變化是非常常見的,比如按鈕的點擊、列表項的展開和收縮等。過渡動畫可以讓這些狀態變化更加自然和流暢,增強用戶體驗。

Transition?和?Crossfade?是過渡動畫中兩個非常重要的組件。Transition?可以用于管理多個屬性在不同狀態之間的過渡,開發者可以為每個屬性定義不同的動畫效果和過渡時間。而?Crossfade?則專注于實現內容之間的淡入淡出過渡,非常適合用于在不同內容片段之間進行切換的場景。

2.3 隱式動畫與傳統動畫的對比

與傳統的 Android 動畫實現方式相比,Android Compose 的隱式動畫具有以下幾個顯著的優點:

  • 聲明式編程風格:傳統的 Android 動畫通常使用命令式編程方式,需要開發者手動控制動畫的各個環節,如動畫的開始、停止、持續時間等。而 Android Compose 的隱式動畫采用聲明式編程風格,開發者只需要描述屬性的目標值和動畫的一些基本參數,框架會自動處理動畫的執行過程,代碼更加簡潔和易于維護。
  • 與 Compose UI 系統深度集成:隱式動畫與 Compose 的 UI 系統緊密結合,能夠充分利用 Compose 的狀態管理和布局系統。當界面元素的狀態發生變化時,隱式動畫可以自動觸發,并且能夠根據界面的布局和狀態變化進行自適應調整。
  • 性能優化:Compose 框架對隱式動畫進行了優化,能夠高效地處理動畫的計算和渲染。它采用智能的重組機制,只有當與動畫相關的狀態發生變化時,才會重新計算和執行動畫,避免了不必要的性能開銷。

三、Transition 源碼分析

3.1 Transition 的基本定義與結構

在 Android Compose 中,Transition?是一個用于管理狀態過渡動畫的核心類。下面是?Transition?類的基本定義和結構:

kotlin

// 引入必要的包
import androidx.compose.animation.core.*
import androidx.compose.runtime.*// 定義 Transition 類,T 為狀態的類型
@Stable
class Transition<T>(// 可變的過渡狀態對象,用于存儲當前狀態和目標狀態private val transitionState: MutableTransitionState<T>,// 過渡的標簽,用于調試和標識label: String = "Transition"
) {// 獲取當前狀態internal val currentState: Tget() = transitionState.currentState// 獲取目標狀態internal val targetState: Tget() = transitionState.targetState// 用于存儲過渡動畫的映射,鍵為動畫的標識,值為過渡定義private val transitions = mutableMapOf<Any, TransitionDefinition<T, *>>()// 用于存儲動畫完成回調的映射,鍵為動畫的標識,值為回調函數private val onEndCallbacks = mutableMapOf<Any, () -> Unit>()// 用于生成動畫規范的工廠類private val animationSpecFactory: AnimationSpecFactory = AnimationSpecFactory()// 用于控制過渡動畫的暫停狀態private var isPaused = false// 過渡的標簽,用于調試目的val label: String = labelinternal set// 內部的 State 對象,用于跟蹤過渡狀態的變化internal val transitionStateState: State<MutableTransitionState<T>> = remember {derivedStateOf { transitionState }}// 用于存儲當前運行的動畫狀態private var currentAnimation: AnimationState<T>? = null// 用于存儲上一次運行的動畫狀態private var previousAnimation: AnimationState<T>? = null// 用于調度動畫幀的回調函數private var frameCallback: (() -> Unit)? = null// 用于管理動畫的時鐘,提供時間信息private val clock = AnimationClockAmbient.current// 判斷是否正在進行過渡動畫internal val isInTransition: Booleanget() = currentAnimation != null// 獲取指定鍵對應的過渡動畫的值@Suppress("UNCHECKED_CAST")internal fun <V> getTransitionValue(key: Any): V? {return transitions[key]?.let {it.getValue(transitionState.currentState, transitionState.targetState)} as? V}// 設置過渡動畫的定義internal fun <V> setTransition(key: Any,transition: TransitionDefinition<T, V>,onEnd: (() -> Unit)? = null) {transitions[key] = transitionif (onEnd != null) {onEndCallbacks[key] = onEnd}}// 啟動過渡動畫internal fun startTransition() {if (isPaused) {return}if (currentAnimation != null) {// 如果已經有正在運行的動畫,停止它currentAnimation?.stop()previousAnimation = currentAnimation}val newAnimation = createAnimation()if (newAnimation != null) {currentAnimation = newAnimationnewAnimation.start()}}// 創建動畫狀態對象private fun createAnimation(): AnimationState<T>? {val transitionDefinitions = transitions.values.toList()if (transitionDefinitions.isEmpty()) {return null}val animationSpecs = transitionDefinitions.map { it.animationSpec }val initialValues = transitionDefinitions.map { it.getValue(transitionState.currentState, transitionState.targetState) }val targetValues = transitionDefinitions.map { it.getValue(transitionState.targetState, transitionState.currentState) }val animationSpec = animationSpecFactory.merge(animationSpecs)return AnimationState(initialValue = initialValues,targetValue = targetValues,animationSpec = animationSpec,onEnd = {currentAnimation = nullonEndCallbacks.forEach { (_, callback) -> callback() }onEndCallbacks.clear()},clock = clock)}// 暫停過渡動畫internal fun pauseTransition() {isPaused = truecurrentAnimation?.pause()}// 恢復過渡動畫internal fun resumeTransition() {isPaused = falsecurrentAnimation?.resume()}// 停止過渡動畫internal fun stopTransition() {isPaused = truecurrentAnimation?.stop()currentAnimation = nullpreviousAnimation = nullonEndCallbacks.clear()}// 跳轉到目標狀態internal fun jumpTo(target: T) {stopTransition()transitionState.currentState = targettransitions.forEach { (_, transition) ->transition.jumpTo(target)}}// 更新目標狀態internal fun updateTargetState(target: T) {if (transitionState.targetState == target) {return}transitionState.targetState = targetif (isInTransition) {stopTransition()}startTransition()}
}

從上述代碼可以看出,Transition?類主要負責管理狀態的過渡動畫。它通過?MutableTransitionState?來存儲當前狀態和目標狀態,并提供了一系列方法來控制動畫的啟動、暫停、恢復和停止。transitions?用于存儲不同動畫的定義,onEndCallbacks?用于存儲動畫完成后的回調函數。createAnimation?方法根據存儲的動畫定義創建實際的動畫對象并啟動。

3.2 Transition 的核心方法解析

3.2.1?setTransition?方法

kotlin

// 設置過渡動畫的定義
internal fun <V> setTransition(key: Any, // 動畫的標識,用于唯一區分不同的動畫transition: TransitionDefinition<T, V>, // 過渡動畫的定義對象onEnd: (() -> Unit)? = null // 動畫結束時的回調函數,可選
) {transitions[key] = transition // 將過渡動畫定義存儲到 transitions 映射中if (onEnd != null) {onEndCallbacks[key] = onEnd // 如果提供了回調函數,將其存儲到 onEndCallbacks 映射中}
}

setTransition?方法用于添加一個過渡動畫定義。通過這個方法,開發者可以將不同的動畫定義添加到?Transition?對象中,以便在狀態變化時執行相應的動畫。key?用于唯一標識這個動畫定義,transition?是具體的動畫定義對象,onEnd?是動畫結束時的回調函數(可選)。

3.2.2?startTransition?方法

kotlin

// 啟動過渡動畫
internal fun startTransition() {if (isPaused) { // 如果過渡動畫處于暫停狀態,直接返回return}if (currentAnimation != null) { // 如果已經有正在運行的動畫// 停止當前正在運行的動畫currentAnimation?.stop()// 將當前動畫狀態保存到 previousAnimation 中previousAnimation = currentAnimation}val newAnimation = createAnimation() // 創建新的動畫狀態對象if (newAnimation != null) { // 如果新的動畫狀態對象創建成功currentAnimation = newAnimation // 將新的動畫狀態對象設置為當前動畫newAnimation.start() // 啟動新的動畫}
}

startTransition?方法用于啟動過渡動畫。首先檢查當前是否處于暫停狀態,如果是則直接返回。然后,如果有正在運行的動畫,先停止它并保存到?previousAnimation?中。接著調用?createAnimation?方法創建新的動畫對象,如果創建成功,則將其設置為當前運行的動畫并啟動。

3.2.3?createAnimation?方法

kotlin

// 創建動畫狀態對象
private fun createAnimation(): AnimationState<T>? {val transitionDefinitions = transitions.values.toList() // 獲取所有的過渡動畫定義if (transitionDefinitions.isEmpty()) { // 如果沒有過渡動畫定義,返回 nullreturn null}val animationSpecs = transitionDefinitions.map { it.animationSpec } // 獲取所有過渡動畫的動畫規范val initialValues = transitionDefinitions.map { it.getValue(transitionState.currentState, transitionState.targetState) } // 獲取所有過渡動畫的初始值val targetValues = transitionDefinitions.map { it.getValue(transitionState.targetState, transitionState.currentState) } // 獲取所有過渡動畫的目標值val animationSpec = animationSpecFactory.merge(animationSpecs) // 合并所有的動畫規范return AnimationState(initialValue = initialValues, // 設置動畫的初始值targetValue = targetValues, // 設置動畫的目標值animationSpec = animationSpec, // 設置動畫的規范onEnd = {currentAnimation = null // 動畫結束后,將當前動畫狀態設置為 nullonEndCallbacks.forEach { (_, callback) -> callback() } // 執行所有的動畫結束回調函數onEndCallbacks.clear() // 清空動畫結束回調函數映射},clock = clock // 設置動畫的時鐘)
}

createAnimation?方法負責創建實際的動畫對象。它首先獲取所有已添加的動畫定義列表?transitionDefinitions,如果列表為空則返回?null。然后分別從動畫定義中提取動畫規范?animationSpecs、初始值?initialValues?和目標值?targetValues。通過?animationSpecFactory.merge?方法合并動畫規范,最后創建并返回一個?AnimationState?對象。AnimationState?對象包含了動畫的初始值、目標值、動畫規范以及動畫結束時的回調函數等信息。

3.2.4?updateTargetState?方法

kotlin

// 更新目標狀態
internal fun updateTargetState(target: T) {if (transitionState.targetState == target) { // 如果新的目標狀態與當前目標狀態相同,直接返回return}transitionState.targetState = target // 更新過渡狀態的目標狀態if (isInTransition) { // 如果當前正在進行過渡動畫stopTransition() // 停止當前的過渡動畫}startTransition() // 啟動新的過渡動畫
}

updateTargetState?方法用于更新目標狀態。首先檢查新的目標狀態是否與當前目標狀態相同,如果相同則直接返回。否則更新?transitionState?的目標狀態。如果當前正在進行過渡動畫,則先停止動畫,然后啟動新的過渡動畫,以確保根據新的目標狀態執行正確的動畫過渡。

3.3 Transition 的使用示例與代碼解析

下面是一個使用?Transition?實現按鈕顏色過渡動畫的示例:

kotlin

import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.tween
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*@OptIn(ExperimentalAnimationApi::class)
@Composable
fun TransitionExample() {// 定義一個可變狀態,用于控制按鈕的顏色var isButtonClicked by remember { mutableStateOf(false) }// 創建一個 Transition 對象,用于管理按鈕顏色的過渡val transition = updateTransition(targetState = isButtonClicked, label = "ButtonColorTransition")// 定義按鈕顏色的過渡動畫val buttonColor by transition.animateColor(transitionSpec = { tween(durationMillis = 300) }, // 動畫規范,持續時間為 300 毫秒label = "ButtonColor" // 動畫的標簽) { if (it) androidx.compose.ui.graphics.Color.Green else androidx.compose.ui.graphics.Color.Red } // 根據狀態返回不同的顏色// 創建按鈕Button(onClick = { isButtonClicked = !isButtonClicked }, // 點擊按鈕時切換狀態modifier = Modifier,colors = ButtonDefaults.buttonColors(backgroundColor = buttonColor) // 設置按鈕的背景顏色) {Text(text = if (isButtonClicked) "Clicked" else "Not Clicked") // 根據狀態顯示不同的文本}
}

代碼解析:

  • 首先,使用?remember?創建一個可變狀態?isButtonClicked,用于控制按鈕的點擊狀態。
  • 然后,通過?updateTransition?創建一個?Transition?對象?transition,其目標狀態為?isButtonClicked,并設置了過渡標簽?ButtonColorTransition
  • 接著,使用?transition.animateColor?定義按鈕顏色的過渡動畫。transitionSpec?指定了動畫的規范,這里使用了一個持續時間為 300 毫秒的線性過渡動畫。label?用于標識這個動畫,{ if (it) androidx.compose.ui.graphics.Color.Green else androidx.compose.ui.graphics.Color.Red }?根據?isButtonClicked?的值決定按鈕的起始和結束顏色。
  • 最后,創建一個按鈕,其背景顏色根據?buttonColor?動態變化,點擊按鈕時會切換?isButtonClicked?的狀態,從而觸發?Transition?對象執行顏色過渡動畫。

3.4 Transition 中的狀態管理機制

在?Transition?中,狀態管理是通過?MutableTransitionState?來實現的。MutableTransitionState?是一個可變的狀態對象,它存儲了當前狀態和目標狀態。當目標狀態發生變化時,Transition?會根據狀態的變化啟動相應的過渡動畫。

kotlin

// 定義 MutableTransitionState 類,T 為狀態的類型
class MutableTransitionState<T>(initialState: T // 初始狀態
) {// 當前狀態var currentState: T = initialStateinternal set// 目標狀態var targetState: T = initialStateinternal set// 判斷是否正在過渡中val isInTransition: Booleanget() = currentState != targetState// 跳轉到目標狀態fun jumpTo(target: T) {currentState = targettargetState = target}// 更新目標狀態fun updateState(target: T) {targetState = target}
}

MutableTransitionState?提供了?currentState?和?targetState?兩個屬性來存儲當前狀態和目標狀態。isInTransition?屬性用于判斷是否正在進行過渡。jumpTo?方法用于直接跳轉到目標狀態,而?updateState?方法用于更新目標狀態。

在?Transition?中,當調用?updateTargetState?方法更新目標狀態時,會觸發?MutableTransitionState?的?targetState?屬性的更新,然后?Transition?會根據狀態的變化啟動相應的過渡動畫。

3.5 Transition 與動畫規范的結合

在?Transition?中,動畫規范(AnimationSpec)起著重要的作用,它定義了動畫的行為,如動畫的持續時間、插值器等。Transition?通過?AnimationSpec?來控制過渡動畫的具體效果。

kotlin

// 定義一個簡單的動畫規范,使用線性插值器,持續時間為 300 毫秒
val linearAnimationSpec = tween<Float>(durationMillis = 300)@OptIn(ExperimentalAnimationApi::class)
@Composable
fun TransitionWithAnimationSpecExample() {var isExpanded by remember { mutableStateOf(false) }val transition = updateTransition(targetState = isExpanded, label = "SizeTransition")val size by transition.animateDp(transitionSpec = { linearAnimationSpec }, // 使用定義好的動畫規范label = "Size") { if (it) 200.dp else 100.dp }Box(modifier = Modifier.size(size).background(Color.Blue).clickable { isExpanded = !isExpanded })
}

在這個示例中,我們定義了一個簡單的線性動畫規范?linearAnimationSpec,并將其應用到?Transition?的?animateDp?方法中。當?isExpanded?狀態發生變化時,Box?的大小會根據?linearAnimationSpec?定義的動畫規范進行過渡。

除了?tween?動畫規范,Android Compose 還提供了其他類型的動畫規范,如?springkeyframes?等,開發者可以根據需要選擇合適的動畫規范來實現不同的動畫效果。

3.6 Transition 中的回調機制

Transition?提供了回調機制,允許開發者在動畫結束時執行特定的操作。通過?setTransition?方法的?onEnd?參數,可以傳入一個回調函數,當動畫結束時,該回調函數會被調用。

kotlin

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun TransitionWithCallbackExample() {var isAnimated by remember { mutableStateOf(false) }val transition = updateTransition(targetState = isAnimated, label = "CallbackTransition")val alpha by transition.animateFloat(transitionSpec = { tween(durationMillis = 500) },label = "Alpha") { if (it) 1f else 0f }transition.setTransition(key = "AlphaTransition",transition = FloatTransitionDefinition(animationSpec = tween(durationMillis = 500)) { if (it) 1f else 0f },onEnd = {// 動畫結束時的回調函數println("Animation ended!")})Box(modifier = Modifier.size(100.dp).background(Color.Red).alpha(alpha).clickable { isAnimated = !isAnimated })
}

在這個示例中,我們通過?setTransition?方法的?onEnd?參數傳入了一個回調函數,當?alpha?動畫結束時,會打印出 “Animation ended!”。

3.7 Transition 的性能優化考慮

在使用?Transition?時,為了提高性能,需要考慮以下幾點:

  • 減少不必要的動畫:避免在界面中添加過多不必要的過渡動畫,過多的動畫會增加系統的計算負擔,導致性能下降。只在必要的地方添加動畫,例如在用戶操作的關鍵節點,如按鈕點擊、頁面切換等,以提供及時的反饋和良好的用戶體驗。
  • 合理設置動畫持續時間:動畫持續時間過長會導致用戶等待時間過長,影響用戶體驗;而過短則可能使動畫效果不明顯。因此,需要根據具體的應用場景和用戶交互需求,合理設置動畫的持續時間。
  • 使用合適的動畫規范:選擇合適的動畫規范可以提高動畫的性能。例如,spring?動畫規范在一些情況下可以提供更自然的動畫效果,并且性能相對較好。
  • 避免頻繁的狀態變化:頻繁的狀態變化會導致動畫頻繁觸發,增加系統的開銷。盡量減少不必要的狀態變化,例如在用戶連續點擊按鈕時,可以設置一個時間間隔,避免在短時間內多次觸發動畫。

四、Crossfade 源碼分析

4.1 Crossfade 的基本定義與結構

Crossfade?是 Android Compose 中用于實現內容之間淡入淡出過渡的 Composable 函數。下面是?Crossfade?的基本定義和結構:

kotlin

import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.layoutId@OptIn(ExperimentalAnimationApi::class)
@Composable
fun Crossfade(targetState: Any, // 目標狀態modifier: Modifier = Modifier, // 修飾符animationSpec: AnimationSpec<Float> = crossfadeSpec(), // 動畫規范content: @Composable (targetState: Any) -> Unit // 內容函數
) {// 創建一個 Transition 對象,用于管理目標狀態的過渡val transition = updateTransition(targetState = targetState, label = "Crossfade")// 定義透明度的過渡動畫val alpha by transition.animateFloat(transitionSpec = { animationSpec }, // 使用傳入的動畫規范label = "CrossfadeAlpha" // 動畫的標簽) { 1f } // 透明度的目標值為 1f// 獲取上一個狀態val previousContent = transition.previousState// 獲取當前狀態val currentContent = transition.currentState// 使用 Layout 組件進行布局Layout(modifier = modifier,content = {if (previousContent != null) {// 繪制上一個狀態的內容Box(modifier = Modifier.layoutId("previousContent").alpha(1f - alpha) // 設置上一個狀態內容的透明度) {content(previousContent)}}// 繪制當前狀態的內容Box(modifier = Modifier.layoutId("currentContent").alpha(alpha) // 設置當前狀態內容的透明度) {content(currentContent)}}) { measurables, constraints ->// 測量所有的子組件val placeables = measurables.map { it.measure(constraints) }// 獲取最大的寬度和高度val maxWidth = placeables.maxOfOrNull { it.width } ?: 0val maxHeight = placeables.maxOfOrNull { it.height } ?: 0// 創建布局結果layout(maxWidth, maxHeight) {placeables.forEach { placeable ->// 將子組件放置在布局中placeable.placeRelative(0, 0)}}}
}

從上述代碼可以看出,Crossfade?首先通過?updateTransition?創建一個?Transition?對象,用于管理目標狀態的變化。然后,使用?transition.animateFloat?為內容的透明度創建一個動畫過渡。接著,通過?transition.previousState?和?transition.currentState?獲取上一個狀態和當前狀態。在?Layout?組件中,分別繪制上一個狀態和當前狀態的內容,并根據透明度的動畫過渡設置它們的透明度,從而實現淡入淡出的效果。

4.2 Crossfade 的核心方法與邏輯解析

4.2.1?updateTransition?的作用

kotlin

val transition = updateTransition(targetState = targetState, label = "Crossfade")

updateTransition?是一個非常重要的函數,它用于創建一個?Transition?對象,該對象會跟蹤?targetState?的變化。當?targetState?發生改變時,Transition?對象會根據定義的動畫規則來執行過渡動畫。在?Crossfade?中,updateTransition?為淡入淡出過渡提供了狀態管理和動畫觸發的基礎。

4.2.2?animateFloat?的實現

kotlin

val alpha by transition.animateFloat(transitionSpec = { animationSpec },label = "CrossfadeAlpha"
) { 1f }

animateFloat?是?Transition?對象的一個擴展函數,用于創建一個?Float?類型的動畫過渡。transitionSpec?指定了動畫的規范,這里使用了傳入的?animationSpec,它決定了動畫的持續時間、插值器等屬性。label?用于標識這個動畫,方便調試和管理。{ 1f }?是一個狀態映射函數,它指定了每個狀態對應的?Float?值,這里初始值和目標值都為?1f,但在過渡過程中?alpha?會根據動畫規范進行變化。

4.2.3?Layout?組件的使用

kotlin

Layout(modifier = modifier,content = {if (previousContent != null) {Box(modifier = Modifier.layoutId("previousContent").alpha(1f - alpha)) {content(previousContent)}}Box(modifier = Modifier.layoutId("currentContent").alpha(alpha)) {content(currentContent)}}
) { measurables, constraints ->val placeables = measurables.map { it.measure(constraints) }val maxWidth = placeables.maxOfOrNull { it.width } ?: 0val maxHeight = placeables.maxOfOrNull { it.height } ?: 0layout(maxWidth, maxHeight) {placeables.forEach { placeable ->placeable.placeRelative(0, 0)}}
}

Layout?組件用于自定義布局。在?Crossfade?中,Layout?組件用于將上一個狀態和當前狀態的內容進行布局。通過?layoutId?為每個內容分配一個唯一的標識,方便管理。根據透明度的動畫過渡,分別設置上一個狀態和當前狀態內容的透明度,實現淡入淡出的效果。

4.3 Crossfade 的使用示例與代碼解析

下面是一個使用?Crossfade?實現文本切換淡入淡出效果的示例:

kotlin

import androidx.compose.animation.Crossfade
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*@OptIn(ExperimentalAnimationApi::class)
@Composable
fun CrossfadeTextExample() {// 定義一個可變狀態,用于控制顯示的文本var showFirstText by remember { mutableStateOf(true) }// 創建一個按鈕,點擊時切換顯示的文本Button(onClick = { showFirstText = !showFirstText }) {Text(text = "Toggle Text")}// 使用 Crossfade 組件,根據 showFirstText 的值切換顯示不同的文本Crossfade(targetState = showFirstText,animationSpec = androidx.compose.animation.core.tween(durationMillis = 500) // 動畫持續時間為 500 毫秒) { isFirst ->if (isFirst) {Text(text = "This is the first text.")} else {Text(text = "This is the second text.")}}
}

代碼解析:

  • 首先,使用?remember?創建一個可變狀態?showFirstText,用于控制顯示的文本。
  • 然后,創建一個按鈕,點擊按鈕時會切換?showFirstText?的狀態。
  • 接著,使用?Crossfade?組件,targetState?為?showFirstTextanimationSpec?指定了動畫的持續時間為 500 毫秒。在?content?函數中,根據?isFirst?的值顯示不同的文本。當?showFirstText?狀態發生變化時,Crossfade?會自動執行淡入淡出過渡動畫,使文本切換更加平滑。

4.4 Crossfade 中的動畫規范定制

在?Crossfade?中,可以通過傳入不同的動畫規范來定制淡入淡出的效果。除了使用?tween?動畫規范,還可以使用?springkeyframes?等其他類型的動畫規范。

kotlin

import androidx.compose.animation.Crossfade
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.*
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*@OptIn(ExperimentalAnimationApi::class)
@Composable
fun CrossfadeWithCustomSpecExample() {var showFirstImage by remember { mutableStateOf(true) }// 自定義彈簧動畫規范val springSpec = spring<Float>(dampingRatio = Spring.DampingRatioLowBouncy,stiffness = Spring.StiffnessLow)Button(onClick = { showFirstImage = !showFirstImage }) {Text(text = "Toggle Image")}Crossfade(targetState = showFirstImage,animationSpec = springSpec // 使用自定義的彈簧動畫規范) { isFirst ->if (isFirst) {// 顯示第一張圖片Image(painter = painterResource(id = R.drawable.image1),contentDescription = null)} else {// 顯示第二張圖片Image(painter = painterResource(id = R.drawable.image2),contentDescription = null)}}
}

在這個示例中,我們自定義了一個彈簧動畫規范?springSpec,并將其應用到?Crossfade?組件中。當點擊按鈕切換圖片時,圖片會以彈簧動畫的效果進行淡入淡出過渡。

4.5 Crossfade 的性能優化技巧

為了提高?Crossfade?的性能,可以考慮以下幾點:

  • 減少不必要的內容重繪Crossfade?在過渡過程中會同時繪制上一個狀態和當前狀態的內容,因此要盡量減少不必要的內容重繪。可以通過合理管理狀態和內容,避免在過渡過程中頻繁更新內容。
  • 優化動畫規范:選擇合適的動畫規范可以提高動畫的性能。例如,使用簡單的?tween?動畫規范通常比復雜的?keyframes?動畫規范性能更好。
  • 避免在過渡過程中進行復雜計算:在?Crossfade?的過渡過程中,盡量避免進行復雜的計算,以免影響動畫的流暢度。可以將復雜的計算提前進行,或者在過渡結束后再進行。

五、Transition 與 Crossfade 的對比與應用場景分析

5.1 功能對比

5.1.1 動畫控制粒度
  • Transition:提供了更細粒度的動畫控制。它允許開發者為不同的屬性(如顏色、大小、位置等)定義獨立的動畫過渡。例如,在一個按鈕狀態切換的場景中,可以同時為按鈕的背景顏色、文字顏色和大小定義不同的過渡動畫,并且可以精確控制每個動畫的起始和結束狀態、持續時間、插值器等。

kotlin

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun TransitionFineGrainedControlExample() {var isButtonActive by remember { mutableStateOf(false) }val transition = updateTransition(targetState = isButtonActive, label = "ButtonTransition")val backgroundColor by transition.animateColor(transitionSpec = { tween(durationMillis = 300) },label = "BackgroundColor") { if (it) Color.Green else Color.Red }val textColor by transition.animateColor(transitionSpec = { tween(durationMillis = 300) },label = "TextColor") { if (it) Color.White else Color.Black }val buttonSize by transition.animateDp(transitionSpec = { tween(durationMillis = 300) },label = "ButtonSize") { if (it) 200.dp else 100.dp }Button(onClick = { isButtonActive = !isButtonActive },modifier = Modifier.size(buttonSize),colors = ButtonDefaults.buttonColors(backgroundColor = backgroundColor)) {Text(text = "Button", color = textColor)}
}
  • Crossfade:主要專注于內容之間的淡入淡出過渡,動畫控制相對單一。它通過改變內容的透明度來實現過渡效果,適用于簡單的內容切換場景,如文本、圖像的切換。

kotlin

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun CrossfadeSimpleTransitionExample() {var showFirstText by remember { mutableStateOf(true)
5.1.2 狀態管理
  • Transition:通過?MutableTransitionState?來管理狀態的變化,能夠跟蹤當前狀態和目標狀態,并根據狀態的變化觸發相應的動畫。可以在狀態變化時動態調整動畫的參數,實現復雜的狀態過渡邏輯。例如,在一個多級菜單展開和收縮的場景中,可以根據不同的層級狀態設置不同的動畫效果和持續時間。

kotlin

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun TransitionComplexStateManagementExample() {// 定義多級菜單的狀態,這里簡化為一個 Int 類型表示層級var menuLevel by remember { mutableStateOf(1) }val transition = updateTransition(targetState = menuLevel, label = "MenuTransition")// 不同層級的動畫持續時間不同val duration = when (menuLevel) {1 -> 2002 -> 300else -> 400}val menuHeight by transition.animateDp(transitionSpec = { tween(durationMillis = duration) },label = "MenuHeight") {when (it) {1 -> 50.dp2 -> 150.dpelse -> 250.dp}}Box(modifier = Modifier.height(menuHeight).background(Color.Gray).clickable {menuLevel = if (menuLevel < 3) menuLevel + 1 else 1}) {Text(text = "Menu Level: $menuLevel")}
}
  • Crossfade:依賴于?updateTransition?創建的?Transition?對象來管理狀態,但它的狀態管理主要用于控制內容的顯示和隱藏,以及觸發淡入淡出動畫。狀態變化相對簡單,主要圍繞內容的切換。

kotlin

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun CrossfadeSimpleStateManagementExample() {var showContent by remember { mutableStateOf(true) }Crossfade(targetState = showContent,animationSpec = tween(durationMillis = 300)) { isVisible ->if (isVisible) {Text(text = "Visible Content")} else {Text(text = "Hidden Content")}}Button(onClick = { showContent = !showContent }) {Text(text = "Toggle Content")}
}
5.1.3 動畫類型支持
  • Transition:支持多種類型的動畫過渡,包括顏色、大小、位置、透明度等。可以使用不同的動畫規范(如?tweenspringkeyframes?等)來實現豐富的動畫效果。例如,在一個卡片翻轉的動畫中,可以同時對卡片的旋轉角度和透明度進行過渡動畫。

kotlin

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun TransitionMultipleAnimationTypesExample() {var isCardFlipped by remember { mutableStateOf(false) }val transition = updateTransition(targetState = isCardFlipped, label = "CardFlipTransition")val rotation by transition.animateFloat(transitionSpec = { tween(durationMillis = 500) },label = "Rotation") { if (it) 180f else 0f }val alpha by transition.animateFloat(transitionSpec = { tween(durationMillis = 500) },label = "Alpha") { if (it) 0.5f else 1f }Box(modifier = Modifier.size(200.dp).background(Color.Blue).rotate(rotation).alpha(alpha).clickable { isCardFlipped = !isCardFlipped }) {Text(text = if (isCardFlipped) "Back" else "Front")}
}
  • Crossfade:主要專注于透明度的過渡動畫,通過淡入淡出的效果實現內容的切換。雖然也可以結合其他動畫效果,但相對來說動畫類型較為單一。

5.2 性能對比

5.2.1 資源消耗
  • Transition:由于可以同時管理多個屬性的動畫過渡,可能會消耗更多的系統資源。特別是在動畫復雜度較高、涉及多個動畫同時執行時,需要更多的計算資源來處理動畫的計算和渲染。例如,在一個包含多個元素同時進行動畫的場景中,每個元素的多個屬性都有動畫過渡,會增加 CPU 和 GPU 的負擔。

kotlin

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun TransitionHighResourceConsumptionExample() {var isAnimated by remember { mutableStateOf(false) }val transition = updateTransition(targetState = isAnimated, label = "MultipleElementsTransition")// 多個元素的動畫repeat(10) { index ->val size by transition.animateDp(transitionSpec = { tween(durationMillis = 500) },label = "Size$index") { if (isAnimated) 100.dp + index * 10.dp else 50.dp }val color by transition.animateColor(transitionSpec = { tween(durationMillis = 500) },label = "Color$index") { if (isAnimated) Color.Green else Color.Red }Box(modifier = Modifier.size(size).background(color).offset(x = index * 20.dp, y = 0.dp))}Button(onClick = { isAnimated = !isAnimated }) {Text(text = "Animate")}
}
  • Crossfade:主要通過改變內容的透明度來實現過渡,動畫邏輯相對簡單,資源消耗較少。在處理簡單的內容切換時,能夠提供較好的性能表現。

kotlin

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun CrossfadeLowResourceConsumptionExample() {var showFirstImage by remember { mutableStateOf(true) }Crossfade(targetState = showFirstImage,animationSpec = tween(durationMillis = 300)) { isFirst ->if (isFirst) {Image(painter = painterResource(id = R.drawable.image1),contentDescription = null)} else {Image(painter = painterResource(id = R.drawable.image2),contentDescription = null)}}Button(onClick = { showFirstImage = !showFirstImage }) {Text(text = "Toggle Image")}
}
5.2.2 動畫流暢度
  • Transition:如果動畫參數設置合理,并且系統資源充足,Transition?可以實現非常流暢的動畫效果。但在復雜動畫場景下,可能會因為資源競爭等問題導致動畫出現卡頓。例如,當同時有多個復雜的動畫在運行時,可能會出現幀率下降的情況。
  • Crossfade:由于其動畫邏輯簡單,在大多數情況下能夠提供較為流暢的淡入淡出過渡效果,尤其是在性能較低的設備上也能保持較好的流暢度。

5.3 應用場景分析

5.3.1 Transition 的應用場景
  • 復雜狀態切換:當界面元素需要在多個狀態之間進行復雜的過渡時,如開關的打開和關閉、卡片的展開和收縮等,Transition?可以為每個狀態變化定義詳細的動畫過渡,使界面交互更加生動和自然。

kotlin

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun TransitionComplexStateSwitchExample() {var isSwitchOn by remember { mutableStateOf(false) }val transition = updateTransition(targetState = isSwitchOn, label = "SwitchTransition")val switchWidth by transition.animateDp(transitionSpec = { tween(durationMillis = 300) },label = "SwitchWidth") { if (isSwitchOn) 150.dp else 50.dp }val switchColor by transition.animateColor(transitionSpec = { tween(durationMillis = 300) },label = "SwitchColor") { if (isSwitchOn) Color.Green else Color.Red }Box(modifier = Modifier.width(switchWidth).height(50.dp).background(switchColor).clickable { isSwitchOn = !isSwitchOn }) {Text(text = if (isSwitchOn) "On" else "Off")}
}
  • 多屬性動畫:需要同時對多個屬性進行動畫過渡的場景,如按鈕的顏色、大小和位置同時發生變化,Transition?可以方便地實現這些屬性的協同動畫。

kotlin

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun TransitionMultiplePropertyAnimationExample() {var isButtonAnimated by remember { mutableStateOf(false) }val transition = updateTransition(targetState = isButtonAnimated, label = "ButtonMultiplePropertyTransition")val buttonSize by transition.animateDp(transitionSpec = { tween(durationMillis = 300) },label = "ButtonSize") { if (isButtonAnimated) 200.dp else 100.dp }val buttonColor by transition.animateColor(transitionSpec = { tween(durationMillis = 300) },label = "ButtonColor") { if (isButtonAnimated) Color.Green else Color.Red }val buttonOffset by transition.animateDp(transitionSpec = { tween(durationMillis = 300) },label = "ButtonOffset") { if (isButtonAnimated) 50.dp else 0.dp }Button(onClick = { isButtonAnimated = !isButtonAnimated },modifier = Modifier.size(buttonSize).offset(x = buttonOffset, y = 0.dp),colors = ButtonDefaults.buttonColors(backgroundColor = buttonColor)) {Text(text = "Animate Button")}
}
5.3.2 Crossfade 的應用場景
  • 內容切換:在不同內容片段之間進行切換的場景,如文本、圖像、頁面的切換,Crossfade?可以提供平滑的淡入淡出過渡效果,增強用戶體驗。

kotlin

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun CrossfadeContentSwitchExample() {var showPage by remember { mutableStateOf(1) }Crossfade(targetState = showPage,animationSpec = tween(durationMillis = 300)) { page ->when (page) {1 -> Text(text = "Page 1 Content")2 -> Text(text = "Page 2 Content")3 -> Text(text = "Page 3 Content")}}Row {Button(onClick = { showPage = 1 }) {Text(text = "Page 1")}Button(onClick = { showPage = 2 }) {Text(text = "Page 2")}Button(onClick = { showPage = 3 }) {Text(text = "Page 3")}}
}
  • 簡單過渡效果:對于只需要簡單的淡入淡出效果的場景,Crossfade?是一個簡潔而高效的選擇,能夠快速實現過渡動畫,減少開發成本。

kotlin

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun CrossfadeSimpleTransitionEffectExample() {var showText by remember { mutableStateOf(true) }Crossfade(targetState = showText,animationSpec = tween(durationMillis = 300)) { isVisible ->if (isVisible) {Text(text = "Hello, World!")}}Button(onClick = { showText = !showText }) {Text(text = "Toggle Text")}
}

六、高級用法與實戰案例

6.1 Transition 的高級用法

6.1.1 組合多個過渡動畫

可以將多個?Transition?組合在一起,實現更復雜的動畫效果。例如,在一個卡片展開的場景中,可以同時為卡片的高度、寬度和透明度定義不同的?Transition,并在卡片展開時同時觸發這些動畫。

kotlin

import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Card
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp@OptIn(ExperimentalAnimationApi::class)
@Composable
fun CombinedTransitionExample() {// 定義一個可變狀態,用于控制卡片的展開和收縮var isExpanded by remember { mutableStateOf(false) }// 創建一個用于控制卡片高度的 Transition 對象val heightTransition = updateTransition(targetState = isExpanded, label = "HeightTransition")val cardHeight by heightTransition.animateDp(transitionSpec = { tween(durationMillis = 300) },label = "CardHeight") { if (it) 200.dp else 100.dp }// 創建一個用于控制卡片寬度的 Transition 對象val widthTransition = updateTransition(targetState = isExpanded, label = "WidthTransition")val cardWidth by widthTransition.animateDp(transitionSpec = { tween(durationMillis = 300) },label = "CardWidth") { if (it) 300.dp else 200.dp }// 創建一個用于控制卡片透明度的 Transition 對象val alphaTransition = updateTransition(targetState = isExpanded, label = "AlphaTransition")val cardAlpha by alphaTransition.animateFloat(transitionSpec = { tween(durationMillis = 300) },label = "CardAlpha") { if (it) 1f else 0.5f }// 創建卡片Card(modifier = Modifier.width(cardWidth).height(cardHeight).alpha(cardAlpha).padding(16.dp).clickable { isExpanded = !isExpanded },elevation = 8.dp) {Column(modifier = Modifier.fillMaxSize().background(Color.LightGray).padding(16.dp),verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) {Text(text = if (isExpanded) "Expanded" else "Collapsed")}}
}

在這個示例中,分別為卡片的高度、寬度和透明度創建了不同的?Transition?對象,并在卡片點擊時同時觸發這些動畫,實現了卡片展開和收縮的復雜動畫效果。

6.1.2 自定義過渡動畫規范

可以通過自定義?AnimationSpec?來實現獨特的過渡動畫效果。例如,使用?SpringSpec?可以創建彈性動畫效果。

kotlin

import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp@OptIn(ExperimentalAnimationApi::class)
@Composable
fun CustomTransitionSpecExample() {// 定義一個可變狀態,用于控制按鈕的大小var isBig by remember { mutableStateOf(false) }// 自定義一個 SpringSpec 動畫規范val springSpec = SpringSpec(dampingRatio = Spring.DampingRatioLowBouncy,stiffness = Spring.StiffnessLow)// 創建一個 Transition 對象,用于控制按鈕的大小過渡val transition = updateTransition(targetState = isBig, label = "ButtonSizeTransition")val buttonSize by transition.animateDp(transitionSpec = { springSpec },label = "ButtonSize") { if (it) 200.dp else 100.dp }// 創建按鈕Button(onClick = { isBig = !isBig },modifier = Modifier.width(buttonSize).height(buttonSize).padding(16.dp)) {Text(text = if (isBig) "Big" else "Small")}
}

在這個示例中,使用?SpringSpec?自定義了一個彈性動畫規范,并將其應用到按鈕大小的過渡動畫中,使按鈕在大小變化時具有彈性效果。

6.1.3 動態更新過渡動畫

在某些情況下,可能需要根據不同的條件動態更新過渡動畫的參數。例如,根據用戶的操作次數來改變動畫的持續時間。

kotlin

import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp@OptIn(ExperimentalAnimationApi::class)
@Composable
fun DynamicTransitionUpdateExample() {// 定義一個可變狀態,用于控制方塊的大小var isExpanded by remember { mutableStateOf(false) }// 定義一個可變狀態,用于記錄點擊次數var clickCount by remember { mutableStateOf(0) }// 根據點擊次數動態計算動畫持續時間val duration = clickCount * 100 + 200// 創建一個 Transition 對象,用于控制方塊的大小過渡val transition = updateTransition(targetState = isExpanded, label = "BoxSizeTransition")val boxSize by transition.animateDp(transitionSpec = { tween(durationMillis = duration) },label = "BoxSize") { if (it) 200.dp else 100.dp }// 創建方塊Box(modifier = Modifier.size(boxSize).background(Color.Blue).clickable {isExpanded = !isExpandedclickCount++}) {Text(text = "Click Count: $clickCount",modifier = Modifier.align(Alignment.Center))}
}

在這個示例中,每次點擊方塊時,clickCount?會增加,動畫的持續時間會根據?clickCount?動態更新,從而實現動態更新過渡動畫的效果。

6.2 Crossfade 的高級用法

6.2.1 嵌套 Crossfade

可以在?Crossfade?中嵌套另一個?Crossfade,實現更復雜的內容過渡效果。例如,在一個頁面中,先進行大內容塊的切換,然后在每個大內容塊中再進行小內容的切換。

kotlin

import androidx.compose.animation.Crossfade
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*@OptIn(ExperimentalAnimationApi::class)
@Composable
fun NestedCrossfadeExample() {// 定義一個可變狀態,用于控制大內容塊的切換var showFirstContent by remember { mutableStateOf(true) }// 定義一個可變狀態,用于控制小內容塊的切換var showFirstSubContent by remember { mutableStateOf(true) }// 創建一個按鈕,點擊時切換大內容塊Button(onClick = { showFirstContent = !showFirstContent }) {Text(text = "Toggle Main Content")}// 使用 Crossfade 組件,根據 showFirstContent 的值切換大內容塊Crossfade(targetState = showFirstContent,animationSpec = androidx.compose.animation.core.tween(durationMillis = 500)) { isFirst ->if (isFirst) {Column(modifier = Modifier.fillMaxSize().padding(16.dp),verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) {Text(text = "This is the first main content.")// 創建一個按鈕,點擊時切換小內容塊Button(onClick = { showFirstSubContent = !showFirstSubContent }) {Text(text = "Toggle Sub Content")}// 在大內容塊中嵌套另一個 Crossfade,根據 showFirstSubContent 的值切換小內容塊Crossfade(targetState = showFirstSubContent,animationSpec = androidx.compose.animation.core.tween(durationMillis = 300)) { isFirstSub ->if (isFirstSub) {Text(text = "This is the first sub content.")} else {Image(painter = painterResource(id = R.drawable.sample_image),contentDescription = null,modifier = Modifier.width(200.dp).height(200.dp),contentScale = ContentScale.Crop)}}}} else {Text(text = "This is the second main content.")}}
}

在這個示例中,外層的?Crossfade?用于切換大內容塊,內層的?Crossfade?用于在大內容塊中切換小內容塊,實現了多層次的內容過渡效果。

6.2.2 自定義淡入淡出效果

可以通過自定義?AnimationSpec?來改變?Crossfade?的淡入淡出效果。例如,使用?KeyframesSpec?可以創建具有不同階段的淡入淡出動畫。

kotlin

import androidx.compose.animation.Crossfade
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.*
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*@OptIn(ExperimentalAnimationApi::class)
@Composable
fun CustomCrossfadeEffectExample() {// 定義一個可變狀態,用于控制顯示的內容var showFirstContent by remember { mutableStateOf(true) }// 自定義一個 KeyframesSpec 動畫規范val keyframesSpec = keyframes {durationMillis = 10000f at 0 with LinearEasing0.5f at 500 with FastOutSlowInEasing1f at 1000 with LinearEasing}// 創建一個按鈕,點擊時切換顯示的內容Button(onClick = { showFirstContent = !showFirstContent }) {Text(text = "Toggle Content")}// 使用 Crossfade 組件,根據 showFirstContent 的值切換顯示不同的內容,并應用自定義的動畫規范Crossfade(targetState = showFirstContent,animationSpec = keyframesSpec) { isFirst ->if (isFirst) {Text(text = "This is the first content.")} else {Image(painter = painterResource(id = R.drawable.sample_image),contentDescription = null,modifier = Modifier.width(200.dp).height(200.dp),contentScale = ContentScale.Crop)}}
}

在這個示例中,使用?KeyframesSpec?自定義了一個淡入淡出動畫規范,使內容在淡入淡出過程中具有不同的階段和插值效果。

6.2.3 與其他動畫效果結合

可以將?Crossfade?與其他動畫效果結合使用,創造出更豐富的動畫體驗。例如,結合?AnimatedVisibility?實現內容的淡入淡出和顯示隱藏的組合效果。

kotlin

import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp@OptIn(ExperimentalAnimationApi::class)
@Composable
fun CrossfadeWithOtherAnimationExample() {// 定義一個可變狀態,用于控制內容的顯示和隱藏var isVisible by remember { mutableStateOf(false) }// 定義一個可變狀態,用于控制內容的切換var showFirstText by remember { mutableStateOf(true) }// 創建一個按鈕,點擊時切換內容的顯示和隱藏狀態Button(onClick = { isVisible = !isVisible }) {Text(text = "Toggle Visibility")}// 創建一個按鈕,點擊時切換顯示的文本Button(onClick = { showFirstText = !showFirstText }) {Text(text = "Toggle Text")}// 使用 AnimatedVisibility 組件,根據 isVisible 的值顯示或隱藏內容AnimatedVisibility(visible = isVisible,enter = fadeIn() + expandIn(),exit = fadeOut() + shrinkOut()) {// 使用 Crossfade 組件,根據 showFirstText 的值切換顯示不同的文本Crossfade(targetState = showFirstText,animationSpec = tween(durationMillis = 300)) { isFirst ->Box(modifier = Modifier.size(200.dp).background(Color.LightGray).padding(16.dp),contentAlignment = Alignment.Center) {Text(text = if (isFirst) "First Text" else "Second Text")}}}
}

在這個示例中,AnimatedVisibility?控制內容的顯示和隱藏,并帶有淡入淡出和縮放動畫,Crossfade?用于在內容顯示時進行文本的切換,實現了兩種動畫效果的結合。

6.3 實戰案例:打造一個動畫卡片列表

以下是一個使用?Transition?和?Crossfade?打造動畫卡片列表的實戰案例:

kotlin

import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.*
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AnimatedCardList() {// 定義一個列表,存儲卡片的數據val cardList = remember {mutableListOf(CardData("Card 1", R.drawable.sample_image_1),CardData("Card 2", R.drawable.sample_image_2),CardData("Card 3", R.drawable.sample_image_3))}// 定義一個可變狀態,用于控制當前選中的卡片var selectedCardIndex by remember { mutableStateOf(-1) }// 創建一個垂直滾動的列表Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {// 遍歷卡片列表cardList.forEachIndexed { index, cardData ->// 創建一個 Transition 對象,用于控制卡片的展開和收縮val transition = updateTransition(targetState = index == selectedCardIndex, label = "CardTransition")val cardHeight by transition.animateDp(transitionSpec = { tween(durationMillis = 300) },label = "CardHeight") { if (it) 300.dp else 150.dp }val cardAlpha by transition.animateFloat(transitionSpec = { tween(durationMillis = 300) },label = "CardAlpha") { if (it) 1f else 0.8f }// 創建卡片Card(modifier = Modifier.fillMaxWidth().height(cardHeight).alpha(cardAlpha).padding(16.dp).clickable {if (selectedCardIndex == index) {selectedCardIndex = -1} else {selectedCardIndex = index}},elevation = 8.dp) {Column(modifier = Modifier.fillMaxSize().background(Color.LightGray)) {// 顯示卡片的標題Text(text = cardData.title,modifier = Modifier.fillMaxWidth().padding(16.dp),fontSize = 20.sp,textAlign = TextAlign.Center)// 使用 Crossfade 組件,根據卡片是否展開顯示不同的內容Crossfade(targetState = index == selectedCardIndex,animationSpec = tween(durationMillis = 300)) { isExpanded ->if (isExpanded) {// 展開時顯示圖片Image(painter = painterResource(id = cardData.imageResId),contentDescription = null,modifier = Modifier.fillMaxSize().clip(shape = MaterialTheme.shapes.medium),contentScale = ContentScale.Crop)} else {// 收縮時顯示空白Spacer(modifier = Modifier.fillMaxSize())}}}}}}
}// 定義卡片數據類
data class CardData(val title: String, val imageResId: Int)

代碼解析:

  • 首先,定義了一個?CardData?數據類,用于存儲卡片的標題和圖片資源 ID。

  • 然后,創建了一個?cardList?列表,存儲多個卡片的數據。

  • 接著,使用?selectedCardIndex?來控制當前選中的卡片。

  • 在遍歷卡片列表時,為每個卡片創建一個?Transition?對象,用于控制卡片的高度和透明度的過渡動畫。

  • 對于每個卡片,使用?Crossfade?組件根據卡片是否展開顯示不同的內容。當卡片展開時,顯示圖片;當卡片收縮時,顯示空白。

通過這種方式,實現了一個具有動畫效果的卡片列表,用戶點擊卡片時,卡片會展開并顯示圖片,再次點擊則收縮。

七、性能優化與注意事項

7.1 性能優化策略

7.1.1 合理設置動畫持續時間

動畫持續時間過長會導致用戶等待時間過長,影響用戶體驗;而過短則可能使動畫效果不明顯。因此,需要根據具體的應用場景和用戶交互需求,合理設置動畫的持續時間。例如,對于簡單的按鈕點擊反饋動畫,可以將持續時間設置為 200 - 300 毫秒;對于復雜的頁面切換動畫,可以設置為 500 - 1000 毫秒。

kotlin

// 簡單按鈕點擊動畫,持續時間 200 毫秒
val simpleClickAnimation = tween<Float>(durationMillis = 200)// 復雜頁面切換動畫,持續時間 800 毫秒
val complexPageTransitionAnimation = tween<Float>(durationMillis = 800)
7.1.2 減少不必要的動畫

避免在界面中添加過多不必要的動畫,過多的動畫會增加系統的計算負擔,導致性能下降。只在必要的地方添加動畫,例如在用戶操作的關鍵節點,如按鈕點擊、頁面切換等,以提供及時的反饋和良好的用戶體驗。

kotlin

// 避免在不需要動畫的地方添加動畫
// 例如,靜態文本不需要動畫效果
Text(text = "This is a static text", modifier = Modifier.padding(16.dp))// 只在需要的地方添加動畫,如按鈕點擊
var isButtonClicked by remember { mutableStateOf(false) }
val transition = updateTransition(targetState = isButtonClicked, label = "ButtonClickTransition")
val buttonColor by transition.animateColor(transitionSpec = { tween(durationMillis = 200) },label = "ButtonColor"
) { if (it) Color.Green else Color.Red }
Button(onClick = { isButtonClicked = !isButtonClicked },colors = ButtonDefaults.buttonColors(backgroundColor = buttonColor)
) {Text(text = "Click me")
}
7.1.3 使用合適的插值器

插值器決定了動畫的變化速率,不同的插值器可以產生不同的動畫效果。選擇合適的插值器可以使動畫更加自然和流暢。例如,FastOutSlowInEasing?可以使動畫開始時快速變化,結束時緩慢變化,符合人眼的視覺習慣。

kotlin

// 使用 FastOutSlowInEasing 插值器
val fastOutSlowInAnimation = tween<Float>(durationMillis = 300,easing = FastOutSlowInEasing
)@OptIn(ExperimentalAnimationApi::class)
@Composable
fun InterpolatorExample() {var isAnimated by remember { mutableStateOf(false) }val transition = updateTransition(targetState = isAnimated, label = "InterpolatorTransition")val offset by transition.animateDp(transitionSpec = { fastOutSlowInAnimation },label = "Offset") { if (isAnimated) 100.dp else 0.dp }Box(modifier = Modifier.size(50.dp).background(Color.Blue).offset(x = offset).clickable { isAnimated = !isAnimated })
}
7.1.4 避免頻繁的狀態變化

頻繁的狀態變化會導致動畫頻繁觸發,增加系統的開銷。盡量減少不必要的狀態變化,例如在用戶連續點擊按鈕時,可以設置一個時間間隔,避免在短時間內多次觸發動畫。

kotlin

import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.*
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import kotlinx.coroutines.delay@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AvoidFrequentStateChangesExample() {var isButtonClicked by remember { mutableStateOf(false) }// 用于記錄上次點擊的時間var lastClickTime by remember { mutableStateOf(0L) }// 定義點擊間隔時間,單位為毫秒val clickInterval = 500Lval transition = updateTransition(targetState = isButtonClicked, label = "ButtonClickTransition")val buttonColor by transition.animateColor(transitionSpec = { tween(durationMillis = 300) },label = "ButtonColor") { if (it) androidx.compose.ui.graphics.Color.Green else androidx.compose.ui.graphics.Color.Red }Button(onClick = {val currentTime = System.currentTimeMillis()if (currentTime - lastClickTime > clickInterval) {isButtonClicked = !isButtonClickedlastClickTime = currentTime}},modifier = Modifier,colors = androidx.compose.material.ButtonDefaults.buttonColors(backgroundColor = buttonColor)) {Text(text = if (isButtonClicked) "Clicked" else "Not Clicked")}
}

在這個示例中,通過記錄上次點擊的時間?lastClickTime,并與當前時間進行比較,只有當時間間隔超過?clickInterval?時,才會更新按鈕的狀態并觸發動畫,從而避免了頻繁的狀態變化。

7.1.5 優化布局結構

復雜的布局結構會增加動畫的計算和渲染時間。盡量簡化布局結構,避免嵌套過深的布局。例如,使用?Box?或?RowColumn?等簡單布局組件來替代復雜的布局嵌套。

kotlin

import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp@OptIn(ExperimentalAnimationApi::class)
@Composable
fun OptimizeLayoutStructureExample() {var isExpanded by remember { mutableStateOf(false) }val transition = updateTransition(targetState = isExpanded, label = "BoxSizeTransition")val boxHeight by transition.animateDp(transitionSpec = { tween(durationMillis = 300) },label = "BoxHeight") { if (isExpanded) 200.dp else 100.dp }// 簡化布局結構,使用 Box 作為根布局Box(modifier = Modifier.fillMaxWidth().height(boxHeight).background(Color.LightGray).clickable { isExpanded = !isExpanded },contentAlignment = Alignment.Center) {Text(text = if (isExpanded) "Expanded" else "Collapsed")}
}

在這個示例中,使用?Box?作為根布局,避免了復雜的布局嵌套,從而提高了動畫的性能。

7.2 常見問題及解決方法

7.2.1 動畫卡頓問題
  • 原因:動畫卡頓通常是由于系統資源不足,如 CPU 或 GPU 負載過高,或者動畫計算過于復雜導致的。

  • 解決方法

    • 優化動畫的復雜度,減少不必要的動畫效果。例如,避免同時進行多個復雜的動畫過渡。
    • 合理設置動畫的幀率,避免過高的幀率導致系統負擔過重。在 Android Compose 中,動畫的幀率默認是根據系統的刷新率進行自適應調整的,但可以通過自定義?AnimationSpec?來限制幀率。
    • 檢查布局結構,避免嵌套過深的布局,減少布局計算的開銷。
7.2.2 動畫閃爍問題
  • 原因:動畫閃爍可能是由于動畫的起始和結束狀態設置不當,或者動畫的更新頻率過高導致的。

  • 解決方法

    • 確保動畫的起始和結束狀態正確設置,避免出現狀態跳躍的情況。例如,在設置透明度動畫時,確保起始透明度和結束透明度的值合理。
    • 減少動畫的更新頻率,避免在短時間內多次更新動畫狀態。可以通過設置合適的動畫持續時間和插值器來實現。
7.2.3 動畫不執行問題
  • 原因:動畫不執行可能是由于狀態管理出現問題,或者動畫的依賴項沒有正確更新導致的。

  • 解決方法

    • 檢查狀態管理邏輯,確保狀態的更新能夠正確觸發動畫。例如,在使用?Transition?時,確保?updateTargetState?方法被正確調用。
    • 檢查動畫的依賴項,確保依賴項的變化能夠正確觸發動畫的更新。例如,在使用?animateDp?等方法時,確保狀態的變化能夠正確影響到動畫的目標值。

7.3 兼容性與版本注意事項

7.3.1 Android 版本兼容性

Android Compose 的動畫功能在不同的 Android 版本上可能會有一些差異。在使用?Transition?和?Crossfade?時,需要確保應用的最低支持版本能夠兼容 Android Compose 的要求。目前,Android Compose 要求 Android 5.0(API 級別 21)及以上版本。

groovy

// 在 build.gradle 文件中設置最低 SDK 版本
android {compileSdkVersion 33defaultConfig {applicationId "com.example.myapp"minSdkVersion 21targetSdkVersion 33versionCode 1versionName "1.0"}
}
7.3.2 Compose 版本更新

隨著 Android Compose 的不斷發展,其動畫 API 可能會有一些更新和改進。在使用?Transition?和?Crossfade?時,建議使用最新的 Compose 版本,以獲得更好的性能和功能。同時,需要注意 Compose 版本更新可能會帶來的 API 變化,及時更新代碼以確保兼容性。

groovy

// 在 build.gradle 文件中設置 Compose 版本
dependencies {implementation "androidx.compose.ui:ui:$compose_version"implementation "androidx.compose.material:material:$compose_version"implementation "androidx.compose.animation:animation:$compose_version"
}
7.3.3 第三方庫兼容性

如果在項目中使用了第三方庫,需要確保這些庫與 Android Compose 的動畫功能兼容。一些第三方庫可能會對布局和繪制過程進行修改,從而影響動畫的效果。在集成第三方庫時,需要進行充分的測試,確保動畫功能正常工作。

八、總結與展望

8.1 總結

在 Android 應用開發中,動畫效果是提升用戶體驗的關鍵因素之一。Android Compose 提供的?Transition?和?Crossfade?這兩種隱式過渡動畫機制,為開發者帶來了極大的便利和豐富的創意空間。

Transition?是一個強大的狀態過渡管理工具,它能夠精確控制多個屬性在不同狀態之間的過渡。通過?MutableTransitionState?管理狀態變化,結合各種動畫規范(如?tweenspring?等),可以實現復雜而精細的動畫效果。例如,在按鈕狀態切換、卡片展開收縮等場景中,Transition?可以同時對顏色、大小、位置等多個屬性進行動畫過渡,使界面交互更加生動自然。同時,Transition?還提供了回調機制,方便開發者在動畫結束時執行特定的操作。

Crossfade?則專注于內容之間的淡入淡出過渡,通過改變內容的透明度實現平滑的切換效果。它的實現邏輯相對簡單,資源消耗較少,在性能較低的設備上也能保持較好的流暢度。適用于文本、圖像、頁面等不同內容片段的切換場景,能夠為用戶提供清晰、舒適的視覺體驗。

通過對?Transition?和?Crossfade?的源碼分析,我們深入了解了它們的工作原理和核心機制。在實際應用中,開發者可以根據具體的需求和場景,靈活選擇和使用這兩種動畫機制,還可以將它們組合使用,創造出更加豐富多樣的動畫效果。同時,在使用過程中,需要注意性能優化和兼容性問題,合理設置動畫參數、簡化布局結構,確保應用在不同設備和 Android 版本上都能穩定運行。

8.2 展望

隨著 Android 技術的不斷發展和用戶對應用體驗要求的日益提高,Android Compose 的動畫功能也將不斷完善和擴展。

8.2.1 更豐富的動畫效果

未來,Transition?和?Crossfade?可能會支持更多種類的動畫效果和過渡方式。例如,引入更多復雜的插值器,如彈性插值器、貝塞爾曲線插值器等,讓動畫的變化更加自然和富有創意。同時,可能會增加對 3D 動畫效果的支持,為用戶帶來更加沉浸式的體驗。

8.2.2 智能化的動畫管理

隨著人工智能和機器學習技術的發展,Android Compose 的動畫管理可能會變得更加智能化。例如,根據用戶的操作習慣和行為模式,自動調整動畫的速度、持續時間和效果,提供更加個性化的動畫體驗。同時,智能算法可以對動畫性能進行實時監測和優化,自動識別和解決動畫卡頓、閃爍等問題。

8.2.3 與其他技術的融合

Android Compose 的動畫功能可能會與其他技術進行更深入的融合。例如,與增強現實(AR)和虛擬現實(VR)技術結合,為用戶創造更加逼真的動畫場景。與手勢識別技術結合,實現更加自然和直觀的交互動畫。此外,還可能與后端數據和網絡服務結合,根據實時數據的變化動態生成動畫效果。

8.2.4 簡化開發流程

為了降低開發者的學習成本和開發難度,未來的 Android Compose 動畫 API 可能會進一步簡化和優化。提供更多的預設動畫模板和便捷的工具,讓開發者可以快速創建出高質量的動畫效果。同時,文檔和示例代碼也會更加完善,幫助開發者更好地理解和使用動畫功能。

總之,Transition?和?Crossfade?作為 Android Compose 中重要的過渡動畫機制,在當前的應用開發中已經發揮了重要作用。隨著技術的不斷進步,它們將在未來的 Android 應用開發中展現出更大的潛力,為用戶帶來更加出色的動畫體驗。開發者可以持續關注 Android Compose 的發展動態,不斷探索和應用新的動畫技術,提升自己的開發能力和應用的競爭力。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/899025.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/899025.shtml
英文地址,請注明出處:http://en.pswp.cn/news/899025.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

sql server如何提高索引命中率

#新星杯14天創作挑戰營第9期# 前言 近期發現以前開發的系統運行緩慢&#xff0c;經排查&#xff0c;發現有很大的優化空間。數據庫版本使用的是sql server&#xff0c;主要有以下一些問題點&#xff1a;數據表無索引、一些不規范的寫法&#xff08;例如in、大表關聯&#xff0…

Qt進程間通信:QSharedMemory 使用詳解

1. 什么是 QSharedMemory&#xff1f; QSharedMemory 是 Qt 中用于進程間共享內存的類。它允許多個進程共享一塊內存區域&#xff0c;從而避免數據傳輸時的 IO 操作&#xff0c;提高通信速度。通過共享內存&#xff0c;多個進程可以直接讀寫這塊內存&#xff0c;而無需經過文件…

2024年3月全國計算機等級考試真題(二級C語言)

&#x1f600; 第1題 下列敘述中正確的是 A. 矩陣是非線性結構 B. 數組是長度固定的線性表 C. 對線性表只能作插入與刪除運算 D. 線性表中各元素的數據類型可以不同 題目解析&#xff1a; A. 矩陣是非線性結構 錯誤。矩陣通常是二維數組&#xff0c;屬…

基于Ebay拍賣網站成交價格的影響因素分析

摘要:近些年來網上拍賣的不斷地發展&#xff0c;網上購物慢慢變成了大家普遍接受的購物方式。因此關于網上拍賣的研究日益成為很多人研究的重點。 影響拍賣網站價格的因素很多&#xff0c;但很少有人分得清楚哪些因素才是比較重要的因素&#xff0c;因此對價格因素分析&#x…

Langchain中的表格解析:RAG 和表格的愛恨情仇

實現 RAG(Retrieval-Augmented Generation)是一個挑戰,尤其是在有效解析和理解非結構化文檔中的表格時。這在處理掃描文檔或圖像格式的文檔時尤為困難。這些挑戰至少包括以下三個方面: 1.表格的“叛逆期”:不準確的解析可能會破壞表格結構: 表格在文檔里就像個叛逆的青少…

Solr-搜索引擎-入門到精通

以下是對 Apache Solr 的簡介及其常用語法的快速入門指南&#xff1a; 一、Solr 是什么&#xff1f; ? 核心定位&#xff1a;Apache Solr 是一個基于 Lucene 的高性能、開源的搜索平臺&#xff0c;支持全文檢索、分詞、高亮、聚合統計等功能。 ? 核心功能&#xff1a; ? 全…

原生后臺GPS位置限制,降低功耗

1.后臺 GPS 位置限制的背景 為了控制功耗&#xff0c;Android 平臺對后臺應用獲取位置信息的頻率進行了限制。后臺的定義與后臺執行限制一致&#xff0c;即應用不在前臺運行時被視為后臺應用。 2.更新間隔限制&#xff1a; 后臺應用獲取位置信息的更新間隔不能超過一定閾值。…

Docker 搭建 PlantUML 服務:高效生成 UML 圖的最佳實踐

PlantUML 是一款開源的 UML 圖形生成工具&#xff0c;它支持生成類圖、時序圖、用例圖、活動圖等多種類型的 UML 圖&#xff0c;廣泛應用于軟件設計、文檔編寫以及團隊溝通中。通過 Docker 安裝 PlantUML&#xff0c;開發者可以快速搭建環境&#xff0c;輕松生成 UML 圖&#x…

簡鹿辦公匯總快速打開 Windows 資源管理器的幾種方式

無論你是剛開始使用電腦的新手&#xff0c;還是已經有一定經驗的用戶&#xff0c;了解如何在 Windows 系統中打開資源管理器都是一個非常基本且重要的技能。Windows 資源管理器&#xff08;現在稱為“文件資源管理器”&#xff09;是幫助你瀏覽、查找和管理計算機上所有文件的主…

嵌入式 python 安裝

下載嵌入式Python后解壓 https://www.python.org/ftp/python/3.12.9/python-3.12.9-embed-amd64.zip cd python-3.12.9-embed-amd64 安裝 pip .\python.exe .\get-pip.py 放開限制 嵌入式 Python 為了減少體積&#xff0c;默認會有一些限制。你需要編輯 python312._pth 文…

DeepSeek底層揭秘——GEMM

目錄 1. 什么是 GEMM&#xff1f; (1) 定義 (2) 核心目標 2. 核心功能 3. 對比傳統通用矩陣乘法庫 4. 技術要素 (1) 硬件感知優化 (2) 自動調優 (3) 高效內存管理 (4) 混合精度計算 5. 難點挑戰&#xff08;含解決方案&#xff09; 6. 技術路徑 7. 具體技術實現 8…

DNA-PAINT

參考: 【科研教程】NUPACK網頁版使用教程 https://www.bilibili.com/video/BV1G94y1W7mN/NUPACK新版網頁版教程-模擬部分 https://zhuanlan.zhihu.com/p/678730568NUPACK 4.0 User Guide https://docs.nupack.org/NUPACK網頁版使用指南 https://zhuanlan.zhihu.com/p/55024017…

小程序內表格合并功能實現—行合并

功能介紹&#xff1a;支付寶小程序手寫表格實現行內合并&#xff0c;依據動態數據自動計算每次需求合并的值&#xff0c;本次記錄行內合并&#xff0c;如果列內合并&#xff0c;同理即可實現 前端技術&#xff1a;grid布局 display&#xff1a;grid 先看實現效果: axml&…

計算機視覺cv2入門之角點檢測

角點是指圖像中兩條邊緣線的交點&#xff0c;這些交點通常代表了圖像中的重要特征。在計算機視覺中&#xff0c;角點檢測是一種關鍵的技術&#xff0c;它可以幫助我們從圖像中提取出這些重要的特征點&#xff0c;進而用于圖像匹配、物體識別、圖像拼接等多種應用。 角點檢測示例…

抽獎的誘惑系統注冊與登錄功能測試報告

目錄 一&#xff1a;項目背景 二&#xff1a;項目功能 &#xff08;1&#xff09; 注冊功能 &#xff08;2&#xff09; 登錄功能 電話 密碼登錄 電話 短信登錄 三&#xff1a;設計測試用例 四&#xff1a;功能測試 五&#xff1a;自動化測試代碼 七&#xff1a;遺留風…

【多媒體交互】Unity Kinect實現UI控件的點擊

在Unity中&#xff0c;通過Kinect實現UI控件的點擊功能&#xff0c;主要涉及手部追蹤、坐標映射和手勢檢測三個核心環節。 實現步驟 初始化Kinect與關節追蹤 使用KinectManager獲取用戶ID和手部關節點&#xff08;如JointType.HandLeft&#xff09;的坐標。 long userId _…

【藍橋杯】每日練習 Day12 貢獻法

前言 今天給大家帶來兩道貢獻法的問題&#xff0c;先來講一下什么是貢獻法。 貢獻法&#xff0c;與其說是一種算法&#xff0c;不如說是一種數學方法&#xff0c;是一種思維方式。 先來給大家舉個例子&#xff0c;假設現在有個問題&#xff0c;需要你在一個只有小寫字母的字…

go test相關命令

在 Go 項目中&#xff0c;go test 可以用于運行整個工程中的測試文件。以下是幾種方式&#xff1a; 1. 運行當前模塊或整個工程的測試 go test ./..../... 表示遞歸測試所有子目錄中的測試文件&#xff08;*_test.go&#xff09;。適用于 Go Modules 或 GOPATH 結構的項目。 …

RocketMQ 詳細知識點總結

RocketMQ 詳細知識點總結 1. 核心概念 1.1 基礎組件 Producer(生產者) 消息的發送者支持同步、異步和單向發送方式提供事務消息功能Consumer(消費者) 消息的接收者支持Push和Pull兩種消費模式支持集群消費和廣播消費NameServer(命名服務) 路由注冊中心無狀態節點,可集…

文字也能生成視頻?【藍耘實踐】:通義萬相2.1文生視頻

文字也能生成視頻&#xff1f;【藍耘實踐】&#xff1a;通義萬相2.1文生視頻 上次我們已經介紹了關于在藍耘云平臺實踐通義萬相的基本玩法&#xff0c;這次將介紹進階玩法&#xff0c;也就是使用文字來生成視頻。 首先我們還是先注冊或者登錄藍耘云平臺。 通過藍耘平臺進入流…