공유 요소의 전환 맞춤설정

공유 요소 전환 애니메이션이 실행되는 방식을 맞춤설정하려면 공유 요소의 전환 방식을 변경하는 데 사용할 수 있는 매개변수입니다.

애니메이션 사양

크기 및 위치 이동에 사용되는 애니메이션 사양을 변경하려면 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 매개변수를 설정할 수 있습니다. RemeasureToBounds 또는 ScaleToBounds로 설정합니다. 이 매개변수는 공유 요소가 두 상태 간에 전환됩니다. ScaleToBounds는 먼저 룩아헤드(또는 타겟) 제약 조건으로 하위 레이아웃을 측정합니다. 그런 다음 하위 요소의 안정적인 레이아웃은 공유 경계에 맞게 조정됩니다. ScaleToBounds은 상태 간의 '그래픽 눈금'으로 생각할 수 있습니다.

반면 RemeasureToBounds는 대상 크기를 기반으로 애니메이션이 적용된 고정된 제약 조건으로 sharedBounds의 하위 레이아웃을 다시 측정하고 다시 레이아웃합니다. 재측정은 경계 크기 변경에 의해 트리거되며, 이는 프레임마다 발생할 수 있습니다.

Text 컴포저블의 경우 ScaleToBounds를 사용하는 것이 좋습니다. 그러면 텍스트가 다른 줄로 재레이아웃되고 다시 흐르지 않기 때문입니다. 가로세로 비율이 다른 경계에서 두 공유 요소 간에 원활한 연속성을 원한다면 RemeasureToBounds를 사용하는 것이 좋습니다.

두 크기 조절 모드의 차이점은 다음 예에서 확인할 수 있습니다.

ScaleToBounds

RemeasureToBounds

최종 레이아웃으로 건너뛰기

기본적으로 두 레이아웃 간에 전환할 때 레이아웃 크기는 시작 상태와 최종 상태 간에 애니메이션 처리됩니다. 이는 다음과 같은 경우에 바람직하지 않은 동작일 수 있습니다. 애니메이션을 적용할 수 있습니다

다음 예는 설명 텍스트 'Lorem Ipsum'을 보여줍니다. 진입 두 가지 방식으로 화면을 전환할 수 있습니다. 첫 번째 예에서는 컨테이너 크기가 커질 때 텍스트가 들어가면서 리플로우되고, 두 번째 예에서는 텍스트가 커질 때 리플로우되지 않습니다. 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는 공유 요소를 사용할 수 없습니다. 컴포저블에 수정자를 추가하면 컴포저블이 계속 올라갑니다.

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

(한 항목이 늘어남에 따라 목록의 다른 항목이 어떻게 아래로 이동하는지 확인)