공유 요소의 전환 맞춤설정

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

애니메이션 사양

크기 및 위치 이동에 사용되는 애니메이션 사양을 변경하려면 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

(한 항목이 커지면서 목록의 다른 항목이 아래로 이동하는 것을 볼 수 있습니다.)