공유 요소의 전환 맞춤설정

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

애니메이션 사양

크기 및 위치 이동에 사용되는 애니메이션 사양을 변경하려면 Modifier.sharedElement()에서 다른 boundsTransform 매개변수를 지정하면 됩니다. 이렇게 하면 초기 Rect 위치와 타겟 Rect 위치가 제공됩니다.

예를 들어 앞의 예시에서 텍스트가 호 모양으로 움직이도록 하려면 keyframes 사양을 사용하도록 boundsTransform 매개변수를 지정합니다.

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은 먼저 lookahead (또는 타겟) 제약 조건으로 하위 레이아웃을 측정합니다. 그런 다음 하위 요소의 안정적인 레이아웃이 공유 경계에 맞게 조정됩니다. ScaleToBounds는 상태 간의 '그래픽 스케일'로 생각할 수 있습니다.

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

Text 컴포저블의 경우 ScaleToBounds을 사용하면 텍스트가 다른 줄로 재배치되고 다시 흐르는 것을 방지할 수 있으므로 ScaleToBounds이 권장됩니다. RemeasureToBounds는 가로세로 비율이 다른 경계에 권장되며, 두 공유 요소 간에 부드러운 연속성을 원하는 경우에도 권장됩니다.

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

ScaleToBounds

RemeasureToBounds

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

기본적으로 두 레이아웃 간에 전환할 때 레이아웃 크기는 시작 상태와 최종 상태 사이에서 애니메이션 처리됩니다. 텍스트와 같은 콘텐츠를 애니메이션으로 처리할 때는 이 동작이 바람직하지 않을 수 있습니다.

다음 예에서는 'Lorem Ipsum' 설명 텍스트가 두 가지 다른 방식으로 화면에 표시됩니다. 첫 번째 예에서는 컨테이너의 크기가 커지면서 텍스트가 리플로우됩니다. 두 번째 예에서는 텍스트가 커져도 리플로우되지 않습니다. Modifier.skipToLookaheadSize()를 추가하면 증가할 때 리플로우가 방지됩니다.

Modifier.skipToLookahead() 없음 - 'Lorem Ipsum' 텍스트가 리플로우됨

Modifier.skipToLookahead() - 애니메이션 시작 시 'Lorem Ipsum' 텍스트가 최종 상태를 유지합니다.

클립 및 오버레이

공유 요소가 서로 다른 컴포저블 간에 공유되려면 전환이 대상의 일치 항목으로 시작될 때 컴포저블의 렌더링이 레이어 오버레이로 상승됩니다. 이렇게 하면 상위 요소의 경계와 레이어 변환 (예: 알파 및 크기 조절)이 이스케이프됩니다.

공유되지 않은 다른 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

(한 항목이 커지면 목록의 다른 항목이 아래로 이동하는 것을 확인하세요.)