Tuỳ chỉnh hiệu ứng chuyển đổi thành phần dùng chung

Để tuỳ chỉnh cách chạy ảnh động chuyển đổi phần tử dùng chung, bạn có thể dùng một số tham số để thay đổi cách chuyển đổi các phần tử dùng chung.

Thông số kỹ thuật của ảnh động

Để thay đổi thông số kỹ thuật về ảnh động dùng cho chuyển động kích thước và vị trí, bạn có thể chỉ định một tham số boundsTransform khác trên Modifier.sharedElement(). Điều này cung cấp vị trí Rect ban đầu và vị trí Rect mục tiêu.

Ví dụ: để văn bản trong ví dụ trước di chuyển theo chuyển động vòng cung, hãy chỉ định tham số boundsTransform để sử dụng thông số 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
    )
)

Bạn có thể sử dụng bất kỳ AnimationSpec nào. Ví dụ này sử dụng một thông số keyframes.

Hình 1. Ví dụ cho thấy các tham số boundsTransform khác nhau

Chế độ đổi kích thước

Khi tạo hiệu ứng chuyển động giữa hai ranh giới dùng chung, bạn có thể đặt tham số resizeMode thành RemeasureToBounds hoặc ScaleToBounds. Tham số này xác định cách phần tử được chia sẻ chuyển đổi giữa hai trạng thái. ScaleToBounds trước tiên đo bố cục con bằng các điều kiện ràng buộc dự đoán (hoặc mục tiêu). Sau đó, bố cục ổn định của thành phần con sẽ được điều chỉnh tỷ lệ cho vừa với các ranh giới được chia sẻ. ScaleToBounds có thể được coi là "thang đồ hoạ" giữa các trạng thái.

Ngược lại, RemeasureToBounds đo lường lại và bố trí lại bố cục con của sharedBounds với các điều kiện ràng buộc cố định được tạo hiệu ứng dựa trên kích thước mục tiêu. Việc đo lường lại được kích hoạt khi kích thước ranh giới thay đổi, có thể là mọi khung hình.

Đối với các thành phần kết hợp Text, bạn nên dùng ScaleToBounds vì thành phần này tránh được việc bố trí lại và sắp xếp lại văn bản trên các dòng khác nhau. RemeasureToBounds được đề xuất cho các ranh giới có tỷ lệ khung hình khác nhau và nếu bạn muốn có tính liên tục linh hoạt giữa hai phần tử dùng chung.

Bạn có thể thấy sự khác biệt giữa hai chế độ đổi kích thước trong các ví dụ sau:

ScaleToBounds

RemeasureToBounds

Chuyển đến bố cục cuối cùng

Theo mặc định, khi chuyển đổi giữa hai bố cục, kích thước bố cục sẽ chuyển động giữa trạng thái bắt đầu và trạng thái cuối cùng. Đây có thể là hành vi không mong muốn khi tạo hiệu ứng cho nội dung, chẳng hạn như văn bản.

Ví dụ sau đây minh hoạ văn bản mô tả "Lorem Ipsum" xuất hiện trên màn hình theo hai cách khác nhau. Trong ví dụ đầu tiên, văn bản sẽ được sắp xếp lại khi văn bản đó xuất hiện khi vùng chứa tăng kích thước. Trong ví dụ thứ hai, văn bản không được sắp xếp lại khi tăng kích thước. Việc thêm Modifier.skipToLookaheadSize() sẽ ngăn chặn việc sắp xếp lại khi Modifier.skipToLookaheadSize() tăng kích thước.

Không có Modifier.skipToLookahead() – hãy chú ý đến văn bản "Lorem Ipsum" được điều chỉnh lại

Modifier.skipToLookahead() – lưu ý rằng văn bản "Lorem Ipsum" vẫn giữ trạng thái cuối cùng khi bắt đầu ảnh động

Đoạn video và lớp phủ

Để các phần tử dùng chung có thể chia sẻ giữa các thành phần kết hợp khác nhau, quá trình kết xuất thành phần kết hợp sẽ được nâng lên thành một lớp phủ khi quá trình chuyển đổi bắt đầu đến thành phần kết hợp phù hợp trong đích đến. Hiệu ứng của việc này là nó sẽ thoát khỏi ranh giới của thành phần mẹ và các phép biến đổi lớp (ví dụ: alpha và tỷ lệ).

Nút này sẽ hiển thị ở trên cùng của các thành phần giao diện người dùng không dùng chung khác. Sau khi hiệu ứng chuyển đổi kết thúc, phần tử sẽ được thả từ lớp phủ xuống DrawScope của riêng phần tử đó.

Để cắt một phần tử dùng chung thành một hình dạng, hãy dùng hàm Modifier.clip() tiêu chuẩn. Đặt sau 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
)

Nếu cần đảm bảo rằng một phần tử được chia sẻ không bao giờ hiển thị bên ngoài một vùng chứa mẹ, bạn có thể đặt clipInOverlayDuringTransition trên sharedElement(). Theo mặc định, đối với các ranh giới được chia sẻ lồng nhau, clipInOverlayDuringTransition sẽ sử dụng đường dẫn cắt từ sharedBounds() mẹ.

Để hỗ trợ việc giữ các thành phần cụ thể trên giao diện người dùng (chẳng hạn như thanh dưới cùng hoặc nút hành động nổi) luôn ở trên cùng trong quá trình chuyển đổi phần tử dùng chung, hãy dùng Modifier.renderInSharedTransitionScopeOverlay(). Theo mặc định, công cụ sửa đổi này sẽ giữ nội dung trong lớp phủ trong thời gian chuyển đổi dùng chung đang hoạt động.

Ví dụ: trong Jetsnack, BottomAppBar cần được đặt lên trên phần tử dùng chung cho đến khi màn hình không còn hiển thị. Việc thêm đối tượng sửa đổi vào thành phần kết hợp sẽ giữ cho thành phần đó ở trạng thái nâng lên.

Không có Modifier.renderInSharedTransitionScopeOverlay()

Với Modifier.renderInSharedTransitionScopeOverlay()

Bạn có thể muốn thành phần kết hợp không dùng chung của mình chuyển động ra ngoài cũng như vẫn ở trên cùng của các thành phần kết hợp khác trước khi chuyển đổi. Trong những trường hợp như vậy, hãy dùng renderInSharedTransitionScopeOverlay().animateEnterExit() để tạo ảnh động cho thành phần kết hợp khi quá trình chuyển đổi phần tử dùng chung chạy:

JetsnackBottomBar(
    modifier = Modifier
        .renderInSharedTransitionScopeOverlay(
            zIndexInOverlay = 1f,
        )
        .animateEnterExit(
            enter = fadeIn() + slideInVertically {
                it
            },
            exit = fadeOut() + slideOutVertically {
                it
            }
        )
)

Hình 2. Thanh ứng dụng ở dưới cùng trượt vào và ra khi ảnh động chuyển đổi.

Trong trường hợp hiếm gặp mà bạn không muốn phần tử dùng chung hiển thị trong lớp phủ, bạn có thể đặt renderInOverlayDuringTransition trên sharedElement() thành false.

Thông báo cho các bố cục cùng cấp về những thay đổi đối với kích thước phần tử dùng chung

Theo mặc định, sharedBounds()sharedElement() sẽ không thông báo cho vùng chứa mẹ về bất kỳ thay đổi nào về kích thước khi bố cục chuyển đổi.

Để truyền các thay đổi về kích thước cho vùng chứa mẹ khi vùng chứa này chuyển đổi, hãy thay đổi tham số placeHolderSize thành PlaceHolderSize.animatedSize. Làm như vậy sẽ khiến mục đó tăng hoặc giảm kích thước. Tất cả các mục khác trong bố cục đều phản hồi thay đổi này.

PlaceholderSize.contentSize (mặc định)

PlaceholderSize.animatedSize

(Lưu ý cách các mục khác trong danh sách di chuyển xuống để phản hồi một mục đang tăng kích thước)