Thư viện Jetpack Hình trong hình (PiP) mang đến một giải pháp tinh gọn và mạnh mẽ cho các nhà phát triển ứng dụng Android để triển khai chức năng PiP, đặc biệt là đối với các ứng dụng phát nội dung nghe nhìn, ứng dụng liên lạc qua video và ứng dụng điều hướng. Bằng cách cung cấp một API hợp nhất, thư viện này giúp loại bỏ mã nguyên mẫu, các lỗi thường gặp trong ứng dụng và cải thiện chất lượng tổng thể của trải nghiệm người dùng ở chế độ PiP.
Thư viện PiP Jetpack hỗ trợ các API PiP hiện có bằng cách giải quyết một số thách thức và điểm không nhất quán chính trong hệ sinh thái Android:
- Phân mảnh hệ điều hành: Thư viện này tự động xử lý những điểm khác biệt trong các lệnh gọi API PiP trên nhiều phiên bản Android, chẳng hạn như sử dụng
enterPictureInPictureModetrước Android 12 vàisAutoEnterEnabledsau Android 12, nên nhà phát triển không cần quản lý những điểm khác biệt về phiên bản. - Các tham số PiP không chính xác: Cung cấp một giải pháp hợp nhất để thiết lập chính xác các tham số PiP, chẳng hạn như
setSourceRectHint, nhằm tạo ra các ảnh động mượt mà và chất lượng cao trong quá trình phát nội dung nghe nhìn. - Các lệnh gọi lại trạng thái PiP hợp nhất: Lệnh gọi lại này hợp nhất
onPictureInPictureModeChangedvàonPictureInPictureUiStateChangedthành một giao diện lệnh gọi lại hợp nhất duy nhất (PictureInPictureDelegate.OnPictureInPictureEventListener) để đơn giản hoá việc quản lý trạng thái và giao diện người dùng. - Giảm mã nguyên mẫu: Thư viện này giảm số lượng mã nguyên mẫu lặp đi lặp lại bằng cách cung cấp các nhóm
RemoteActionsđược xác định trước cho các trường hợp sử dụng phổ biến, chẳng hạn như bộ điều khiển chế độ phát và các thao tác gọi video. - Đảm bảo khả năng tương thích với các phiên bản trong tương lai: Các tính năng PiP khác được cung cấp thông qua thư viện Jetpack, cho phép người dùng truy cập vào các chức năng bổ sung mà không tốn nhiều công sức.
Quy trình di chuyển
Xác định danh mục trường hợp sử dụng của ứng dụng và logic PiP cũ:
Danh mục: Phát video, Chỉ đường hoặc Gọi video.
Logic PiP cũ cần xác định:
onUserLeaveHintsetAutoEnterEnabledonPictureInPictureModeChangedonPictureInPictureUiStateChangedsetPictureInPictureParams.
2. Cấu hình AndroidManifest
Đảm bảo Hoạt động chuyển sang chế độ PiP khai báo hỗ trợ trong AndroidManifest.xml bằng configChanges cần thiết để tránh khởi động lại không cần thiết:
<activity
android:name="VideoActivity" android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
</activity>
3. Thiết lập môi trường
Thêm các phần phụ thuộc bắt buộc vào build.gradle:
dependencies {
implementation("androidx.core:core:1.18.0")
implementation("androidx.activity:activity:1.13.0")
implementation("androidx.core:core-pip:1.0.0-alpha02") }
Hãy dùng các thư viện AndroidX mới nhất cho các phần phụ thuộc và tham khảo trang các bản phát hành để biết thông tin đó.
4. Chọn và khởi tạo mẫu
Chọn mẫu triển khai phù hợp nhất với trường hợp sử dụng của ứng dụng:
- Điều hướng và gọi video:
BasicPictureInPicture; thường không hỗ trợ thay đổi kích thước liền mạch và bạn không cần gợi ý về hình chữ nhật nguồn. - Phát video:
VideoPlaybackPictureInPicture; tự động theo dõi ranh giới xem của trình phát cho gợi ý về hình chữ nhật nguồn và cho phép đổi kích thước liền mạch theo mặc định.
Để áp dụng Thư viện Jetpack, hãy thay thế chế độ PiP tuỳ chỉnh hiện có bằng các API Thư viện Jetpack. Độ phức tạp và chi phí áp dụng sẽ khác nhau tuỳ thuộc vào cách triển khai hiện tại của ứng dụng.
Các phần sau đây mô tả một số trường hợp sử dụng điển hình của chế độ PiP và các bước triển khai cần thiết:
Điều hướng
Ứng dụng thông báo cho thư viện về trạng thái hoạt động hoặc không hoạt động của thành phần điều hướng và đặt tỷ lệ khung hình. Thư viện Jetpack sẽ xử lý phần còn lại.
Những điểm khác biệt chính:
- Không cần phân biệt tính năng tự động nhập và tính năng nhập cũ ở phía ứng dụng.
- Các giao diện gọi lại được hợp nhất.
- Trình tạo
PictureInPictureParamsmới để tương thích ngược.
Gọi video
Ứng dụng thông báo cho thư viện về trạng thái đang hoạt động hoặc không hoạt động của cuộc gọi và đặt tỷ lệ khung hình.
Những điểm khác biệt chính:
- Không cần phân biệt tính năng tự động nhập và tính năng nhập cũ ở phía ứng dụng.
- Các giao diện gọi lại được hợp nhất.
- Trình tạo
PictureInPictureParamsmới để tương thích ngược. - Biểu tượng thao tác được chuẩn hoá cho cuộc gọi video.
5. Di chuyển mã
- Logic nhập: Thay thế logic dành riêng cho API, chẳng hạn như
setAutoEnterEnabledcho Android 12 trở lên hoặconUserLeaveHintcho Android 11 trở xuống bằngsetEnabled. Kích hoạt sự kiện này bất cứ khi nào trạng thái đủ điều kiện dùng chế độ PiP thay đổi. - Lệnh gọi lại: Hợp nhất
onPictureInPictureModeChanged(chuyển đổi bố cục) vàonPictureInPictureUiStateChanged(ảnh động/trạng thái) thành một lệnh gọi lại dựa trên sự kiện hợp nhấtonPictureInPictureEvent. - Thao tác và tham số: Cập nhật tham số bằng cách sử dụng
setActionsvàsetAspectRatiotrên thực thể mẫu bất cứ khi nào các tham số này thay đổi. - Xử lý đặc biệt cho video: Đối với các ứng dụng video, hãy sử dụng
setPlayerViewđể tự động hoá việc cập nhật gợi ý về hình chữ nhật nguồn và đảm bảo các hiệu ứng chuyển cảnh diễn ra mượt mà. ` ### 6. Dọn dẹp
Đối với VideoPlaybackPictureInPicture, hãy gọi close trong onDispose hoặc onDestroy để giải phóng các tài nguyên như trình theo dõi lượt xem.
Mẫu triển khai tham chiếu
Ví dụ về cách triển khai.
Điều hướng và cuộc gọi video
class NavOrVideoCallJpipActivity : ComponentActivity(), PictureInPictureDelegate.OnPictureInPictureEventListener { private lateinit var pictureInPictureImpl: BasicPictureInPicture override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) pictureInPictureImpl = BasicPictureInPicture(this) // BasicPictureInPicture is ideal for Navigation and Video call use cases. pictureInPictureImpl.addOnPictureInPictureEventListener( ContextCompat.getMainExecutor(this), this ) setContent { } } override fun onPictureInPictureEvent( event: PictureInPictureDelegate.Event, config: Configuration? ) { when (event) { PictureInPictureDelegate.Event.ENTERED -> { /* Toggle to PiP layout */ } PictureInPictureDelegate.Event.EXITED -> { /* Toggle to Full-screen layout */ } PictureInPictureDelegate.Event.STASHED -> { /* Optional: PiP is stashed */ } PictureInPictureDelegate.Event.UNSTASHED -> { /* Optional: PiP is unstashed */ } } } }
Phát lại video
class VideoPlaybackJpipActivity : ComponentActivity(), PictureInPictureDelegate.OnPictureInPictureEventListener { private lateinit var pictureInPictureImpl: VideoPlaybackPictureInPicture override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) pictureInPictureImpl = VideoPlaybackPictureInPicture(this) pictureInPictureImpl.addOnPictureInPictureEventListener( ContextCompat.getMainExecutor(this), this ) setContent { ContentScreen(pictureInPictureImpl) } } override fun onPictureInPictureEvent( event: PictureInPictureDelegate.Event, config: Configuration? ) { when (event) { PictureInPictureDelegate.Event.ENTER_ANIMATION_START -> { /* Hide overlays */ } PictureInPictureDelegate.Event.ENTER_ANIMATION_END -> { /* Animation finished */ } PictureInPictureDelegate.Event.ENTERED -> { /* Switch to PiP layout */ } PictureInPictureDelegate.Event.STASHED -> { /* PiP stashed */ } PictureInPictureDelegate.Event.UNSTASHED -> { /* PiP unstashed */ } PictureInPictureDelegate.Event.EXITED -> { /* Return to full-screen */ } } } @Composable fun ContentScreen(pipController: VideoPlaybackPictureInPicture) { DisposableEffect(pipController) { onDispose { pipController.close() } } } }