自訂共用元素轉換

如要自訂共用元素轉換動畫的執行方式,可以使用幾個參數變更共用元素的轉換方式。

動畫規格

如要變更大小和位置動作使用的動畫規格,您可以在 Modifier.sharedElement() 上指定不同的 boundsTransform 參數。這會提供初始 Rect 位置和目標 Rect 位置。

舉例來說,如要讓上述範例中的文字使用弧形動作移動,請指定 boundsTransform 參數以使用 keyframes 規格:

val textBoundsTransform = BoundsTransform { initialBounds, targetBounds ->
    keyframes {
        durationMillis = boundsAnimationDurationMillis
        initialBounds at 0 using ArcMode.ArcBelow using FastOutSlowInEasing
        targetBounds at boundsAnimationDurationMillis
    }
}
Text(
    "Cupcake", fontSize = 28.sp,
    modifier = Modifier.sharedBounds(
        rememberSharedContentState(key = "title"),
        animatedVisibilityScope = animatedVisibilityScope,
        boundsTransform = textBoundsTransform
    )
)

您可以使用任何 AnimationSpec。本範例使用 keyframes 規格。

圖 1.顯示不同 boundsTransform 參數的範例

調整大小模式

在兩個共用邊界之間建立動畫時,您可以將 resizeMode 參數設為 RemeasureToBoundsScaleToBounds。這個參數可決定共用元素在兩種狀態之間轉換的方式。ScaleToBounds 會先使用 Look 圖表 (或目標) 限制條件測量子版面配置。接著,系統會將子項的穩定版面配置調整為符合共用邊界。ScaleToBounds 可視為是兩個狀態之間的「圖形比例」。

RemeasureToBounds 會重新測量及重新配置 sharedBounds 的子版面配置,而會根據目標大小,使用固定的動畫限制動畫。重新測量作業是由邊界大小變更觸發,可能代表每個影格。

針對 Text 可組合項,建議使用 ScaleToBounds,因為這樣可避免將文字重新安排到不同的行。針對不同長寬比的邊界,且如果您希望兩個共用元素之間的一致性,建議使用 RemeasureToBounds

您可在以下範例中瞭解兩種調整大小模式的差異:

ScaleToBounds

RemeasureToBounds

跳至最終版面配置

根據預設,在兩個版面配置之間切換時,版面配置大小會在開始和最終狀態之間進行動畫。在為文字等內容建立動畫時,這可能會是不理想的行為。

以下範例說明「Lorem Ipsum」以兩種不同方式輸入畫面的說明文字。第一個範例會在容器放大時的文字自動重排,第二個範例在容器放大時不會重排。新增 Modifier.skipToLookaheadSize() 即可避免在增加時自動執行自動重排。

沒有 Modifier.skipToLookahead() - 請留意「Lorem Ipsum」文字重排

Modifier.skipToLookahead() - 請注意,「Lorem Ipsum」文字會在動畫開始時保留最終狀態

剪輯片段和疊加層

在 Compose 中建立共用元素時,一個重要概念,是為了讓這些元素在不同可組合項之間共用,當轉換開始讓其與目的地相符時,可組合項的算繪作業會提升至圖層疊加層。這種做法會逸出父項的邊界及其圖層轉換 (例如 Alpha 和縮放)。

轉場效果會在其他非共用的 UI 元素上算繪,因此當轉換完成後,元素會從疊加元素捨棄到自身的 DrawScope

如要將共用元素裁剪成形狀,請使用標準 Modifier.clip() 函式。將其放在 sharedElement() 後方:

Image(
    painter = painterResource(id = R.drawable.cupcake),
    contentDescription = "Cupcake",
    modifier = Modifier
        .size(100.dp)
        .sharedElement(
            rememberSharedContentState(key = "image"),
            animatedVisibilityScope = this@AnimatedContent
        )
        .clip(RoundedCornerShape(16.dp)),
    contentScale = ContentScale.Crop
)

如果您需要確保共用元素絕不會在父項容器外部算繪,可在 sharedElement() 上設定 clipInOverlayDuringTransition。根據預設,對於巢狀共用邊界,clipInOverlayDuringTransition 會使用父項 sharedBounds() 的裁剪路徑。

如要支援在共用元素轉換期間,讓特定 UI 元素 (例如底部列或懸浮動作按鈕) 保持開啟,請使用 Modifier.renderInSharedTransitionScopeOverlay()。根據預設,此修飾符會在共用轉換期間保留疊加中的內容。

舉例來說,在 Jetsnack 中,BottomAppBar 必須置於共用元素上方,直到畫面未顯示為止。為可組合項新增修飾符,即可提升其高度。

未提供Modifier.renderInSharedTransitionScopeOverlay()資訊

登入 Modifier.renderInSharedTransitionScopeOverlay()

有時候,您可能希望非共用的可組合項在轉換之前,以動畫方式消失,並且仍保留在其他可組合項上方。在這種情況下,請使用 renderInSharedTransitionScopeOverlay().animateEnterExit(),在共用元素轉換執行時,為可組合項建立動畫:

JetsnackBottomBar(
    modifier = Modifier
        .renderInSharedTransitionScopeOverlay(
            zIndexInOverlay = 1f,
        )
        .animateEnterExit(
            enter = fadeIn() + slideInVertically {
                it
            },
            exit = fadeOut() + slideOutVertically {
                it
            }
        )
)

圖 2:隨著動畫轉場而向內或向外滑動

在極少數情況下,如果您想不讓共用元素顯示在疊加層中,可以將 sharedElement()renderInOverlayDuringTransition 設為 false。

針對共用元素大小變更通知同層級版面配置

根據預設,當版面配置轉換時,sharedBounds()sharedElement() 不會通知父項容器的大小變更。

如要在轉換時將大小變更套用至父項容器,請將 placeHolderSize 參數變更為 PlaceHolderSize.animatedSize。這樣會導致項目成長或縮小。版面配置中的所有其他項目都會回應變更。

PlaceholderSize.contentSize (預設)

PlaceholderSize.animatedSize

(請注意,清單中其他項目如何隨著某個項目逐漸增加而下移)