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ể dùng một vài tham số để thay đổi cách chuyển đổi của các thành phần dùng chung.

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

Để thay đổi thông số ảnh động dùng cho việc di chuyển kích thước và 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ụ: để làm cho 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ể dùng AnimationSpec bất kỳ. Ví dụ này sử dụng thông số keyframes.

Hình 1. Ví dụ minh hoạ các tham số boundsTransform

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 thành RemeasureToBounds hoặc ScaleToBounds. Tham số này xác định cách phần tử dùng chung chuyển đổi giữa hai trạng thái. Trước tiên, ScaleToBounds đo lường bố cục con với các điều kiện ràng buộc về tầm nhìn (hoặc mục tiêu). Sau đó, bố cục ổn định của bố cục con sẽ được điều chỉnh theo tỷ lệ cho phù hợp với các giới hạn chung. ScaleToBounds có thể được coi là một "thang đồ 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 với 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. 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, có thể là mọi khung hình.

Đối với thành phần kết hợp Text, bạn nên dùng ScaleToBounds vì thuộc tính này sẽ tránh được bố cục lại và chỉnh lại luồng văn bản trên các dòng khác nhau. Đối với các giới hạn có tỷ lệ khung hình khác nhau và nếu muốn tính liên tục linh hoạt giữa 2 phần tử dùng chung, bạn nên dùng RemeasureToBounds.

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 2 bố cục, kích thước bố cục sẽ tạo ảnh độ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ạ văn bản mô tả "Lorem Ipsum" nhập vào màn hình theo hai cách khác nhau. Ví dụ đầu tiên về văn bản được chỉnh lại luồng khi vùng chứa tăng kích thước, còn ví dụ thứ hai thì văn bản không chỉnh lại luồng khi tăng kích thước. Việc thêm Modifier.skipToLookaheadSize() sẽ ngăn việc chỉnh lại luồng khi mã này phát triển.

Không cóModifier.skipToLookahead() – chú ý đến việc chỉnh lại luồng văn bản "Lorem Ipsum"

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

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

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

Sau khi quá trình chuyển đổi hoàn tất, phần tử này sẽ bị loại bỏ từ lớp phủ xuống 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ử dụng đường dẫn cắt từ sharedBounds() mẹ.

Để hỗ trợ việc giữ cho 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 thành phần được chia sẻ, hãy sử dụng Modifier.renderInSharedTransitionScopeOverlay(). Theo mặc định, đối tượng sửa đổi này giữ nội dung trong lớp phủ trong khoảng thời gian hiệu ứng chuyển đổi 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 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úp nâng cao thành phần này.

Không có Modifier.renderInSharedTransitionScopeOverlay()

Bằng Modifier.renderInSharedTransitionScopeOverlay()

Đôi khi, bạn có thể muốn thành phần kết hợp không được chia sẻ tạo ảnh động cũng như vẫn ở 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 thành phần 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 chuyển đổi ảnh động

Trong một số ít trường hợp mà bạn không muốn phần tử được chia sẻ xuất hiện 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 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.

Để áp dụng các thay đổi về kích thước đến vùng chứa mẹ khi chuyển đổi, hãy thay đổi tham số placeHolderSize thành PlaceHolderSize.animatedSize. Làm như vậy sẽ khiến mục này lớn lên hoặc thu nhỏ. Tất cả các mục khác trong bố cục đều phản hồi vớ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)