自訂共用元素轉換

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

動畫規格

如要變更用於大小和位置移動的動畫規格,您可以在 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 會先使用預測 (或目標) 限制來評估子項版面配置。接著, 子項的穩定版面配置會經過縮放,以符合共用邊界。 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

(請留意清單中其他項目如何因應持續增長的項目)