共有要素遷移アニメーションの実行方法をカスタマイズする場合、共有要素の遷移方法の変更に使用できるパラメータがいくつかあります。
アニメーションの仕様
サイズと位置の移動に使用されるアニメーション仕様を変更するには、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
仕様を使用しています。
boundsTransform
パラメータを示す例サイズ変更モード
2 つの共有境界の間でアニメーション化する場合は、resizeMode
パラメータを RemeasureToBounds
または ScaleToBounds
に設定できます。このパラメータにより、共有要素が 2 つの状態間でどのように遷移するかが決まります。ScaleToBounds
はまず、先読み(ターゲット)制約を使用して子レイアウトを測定します。その後、子の安定版レイアウトが、共有境界内に収まるようにスケーリングされます。ScaleToBounds
は、状態間の「グラフィカル スケール」と考えることができます。
一方、RemeasureToBounds
は、ターゲット サイズに基づいてアニメーション化された固定制約を使用して、sharedBounds
の子レイアウトを再測定して再レイアウトします。再測定は境界サイズ変更によってトリガーされます。この変化はフレームごとに行われる可能性があります。
Text
コンポーザブルの場合、テキストを別の行に再レイアウトしてリフローすることを回避するために、ScaleToBounds
をおすすめします。境界のアスペクト比が異なり、2 つの共有要素間で滑らかな連続性が必要な場合は、RemeasureToBounds
をおすすめします。
2 つのサイズ変更モードの違いを以下の例に示します。
|
|
---|---|
最終レイアウトにスキップ
デフォルトでは、2 つのレイアウト間を移行するときに、レイアウト サイズが開始状態と最終状態の間でアニメーション化されます。これは、テキストなどのコンテンツをアニメーション化する場合、望ましくない動作になる可能性があります。
次の例は、「Lorem Ipsum」という説明文が 2 種類の方法で画面に入る例を示しています。最初の例では、コンテナのサイズが大きくなると、テキストが入り込むとリフローします。2 番目の例では、テキストが大きくなってもリフローしません。Modifier.skipToLookaheadSize()
を追加すると、拡大に伴うリフローを防ぐことができます。
Modifier.skipToLookahead() なし - 「Lorem Ipsum」のテキストがリフローされている |
Modifier.skipToLookahead() - 「Lorem Ipsum」というテキストはアニメーション開始時の最終状態を保持しています。 |
---|---|
クリップとオーバーレイ
Compose で共有要素を作成する際の重要なコンセプトは、異なるコンポーザブル間で共有するために、デスティネーションで一致する遷移が開始されると、コンポーザブルのレンダリングをレイヤ オーバーレイに昇格させることです。これにより、親の境界とそのレイヤ変換(アルファやスケールなど)がエスケープされます。
共有されていない他の 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
を共有要素の上に配置する必要があります。コンポーザブルに修飾子を追加すると、高さが維持されます。
|
|
---|---|
遷移前に、共有でないコンポーザブルをアニメーション化し、他のコンポーザブルの上に重ねて表示したい場合があります。そのような場合は、renderInSharedTransitionScopeOverlay().animateEnterExit()
を使用して、共有要素の遷移の実行時にコンポーザブルをアニメーション化します。
JetsnackBottomBar( modifier = Modifier .renderInSharedTransitionScopeOverlay( zIndexInOverlay = 1f, ) .animateEnterExit( enter = fadeIn() + slideInVertically { it }, exit = fadeOut() + slideOutVertically { it } ) )
まれに、共有要素をオーバーレイでレンダリングしないようにする場合は、sharedElement()
の renderInOverlayDuringTransition
を false に設定します。
共有要素サイズの変更を兄弟レイアウトに通知する
デフォルトでは、sharedBounds()
と sharedElement()
はレイアウト遷移時に親コンテナにサイズ変更を通知しません。
遷移時に親コンテナにサイズ変更を反映するには、placeHolderSize
パラメータを PlaceHolderSize.animatedSize
に変更します。この操作を行うと、アイテムが拡大または縮小されます。レイアウト内の他のすべてのアイテムが変更に応答します。
|
(1 つのアイテムが増えるのに反応して、リスト内の他のアイテムがどのように下降していくかに注目してください) |
---|---|