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 thành phần dùng chung, bạn có thể các tham số có thể được dùng để 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ố ảnh động dùng cho kích thước và chuyển động vị trí, bạn có thể chỉ định một tham số boundsTransform khác trên Modifier.sharedElement(). Thao tác này cung cấp vị trí Rect ban đầu và vị trí Rect mục tiêu.

Ví dụ: để khiến văn bản trong ví dụ trước di chuyển theo một vòng cung chuyển động, 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ể dùng AnimationSpec bất kỳ. Ví dụ này sử dụng 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 ảnh động giữa hai giới hạn dùng chung, bạn có thể đặt tham số resizeMode đến 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. Trước tiên, ScaleToBounds đo lường bố cục con bằng các quy tắc ràng buộc trước (hoặc mục tiêu). Sau đó, bố cục ổn định của thành phần con được điều chỉnh theo tỷ lệ để vừa với các giới hạn dùng chung. Bạn có thể coi ScaleToBounds là "tỷ lệ đồ hoạ" giữa các trạng thái.

Trong khi đó, RemeasureToBounds đo lường lại và bố cục lại bố cục con của sharedBounds có các điều kiện ràng buộc cố định dạng ảnh động dựa trên kích thước mục tiêu. Chiến lược phát hành đĩa đơn quá trình đo lường lại được kích hoạt bởi sự thay đổi về kích thước giới hạn, điều này có thể ở mọi khung hình.

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

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ụ bên dưới:

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ẽ tạo hiệu ứng độ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 ảnh động cho nội dung, chẳng hạn như văn bản.

Ví dụ sau minh hoạ cho nội dung mô tả "Lorem Ipsum" đang nhập màn hình theo hai cách khác nhau. Ví dụ đầu tiên, văn bản được chỉnh lại luồng khi đi vào khi vùng chứa tăng kích thước, ví dụ thứ hai văn bản không chỉnh lại khi hàm lớn dần. Việc thêm Modifier.skipToLookaheadSize() sẽ ngăn việc chảy lại khi kích thước tăng lên.

Không có Modifier.skipToLookahead() – hãy lưu ý văn bản "Lorem Ipsum" sẽ được kết xuất lại

Modifier.skipToLookahead() – lưu ý văn bản "Lorem Ipsum" giữ nguyên trạng thái cuối cùng ở đầu ảnh động

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

Một khái niệm quan trọng khi tạo các phần tử dùng chung trong Compose là để các phần tử này có thể chia sẻ giữa các thành phần kết hợp khác nhau, việc 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 khớp với đích đến. Hiệu ứng của việc này là lớp con sẽ thoát khỏi giới hạn của lớp mẹ và các phép biến đổi lớp (ví dụ: alpha và tỷ lệ).

Thành phần này sẽ hiển thị trên các thành phần giao diện người dùng không được chia sẻ khác, sau khi hiệu ứng chuyển đổi khi hoàn tất, phần tử này sẽ bị thả khỏi lớp phủ vào DrawScope của chính nó.

Để cắt một phần tử dùng chung thành một hình dạng, hãy sử 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ử dùng chung không bao giờ hiển thị bên ngoài vùng chứa mẹ, bạn có thể đặt clipInOverlayDuringTransition trên sharedElement(). Theo mặc định, đối với các giới hạn chung được lồng, clipInOverlayDuringTransition sẽ sử dụng đoạn mã đường dẫn của phần tử mẹ sharedBounds().

Để hỗ trợ việc giữ cho các thành phần trên giao diện người dùng cụ thể (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 sử dụng Modifier.renderInSharedTransitionScopeOverlay(). Theo mặc định, đối tượng 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 trên phần tử dùng chung cho đến khi màn hình không hiển thị. Thêm đối tượng sửa đổi vào thành phần kết hợp giúp nâng cao.

Không có Modifier.renderInSharedTransitionScopeOverlay()

Với Modifier.renderInSharedTransitionScopeOverlay()

Đôi khi, bạn có thể muốn thành phần kết hợp không được chia sẻ của mình tạo ảnh động cũng như vẫn nằm trên 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 sử 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 một số ít trường hợp, nếu muốn phần tử được chia sẻ không hiển thị trong lớp phủ, bạn có thể đặt renderInOverlayDuringTransition trên sharedElement() thành false.

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

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

Để truyền thay đổi về kích thước đến 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. Đang thực hiện vì vậy, mục này sẽ lớn dần hoặc co lại. Tất cả các mục khác trong bố cục sẽ phản hồi thay đổi này.

PlaceholderSize.contentSize (mặc định)

PlaceholderSize.animatedSize

(Hãy chú ý đến 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 lại tăng trưởng)