ไลบรารี Picture-in-Picture (PiP) ของ Jetpack เป็นโซลูชันที่มีประสิทธิภาพและ คล่องตัวสำหรับนักพัฒนาแอป Android ในการใช้ฟังก์ชันการทำงานของ PiP โดยเฉพาะอย่างยิ่งสำหรับแอปการเล่นสื่อ แอปการสื่อสารผ่านวิดีโอ และแอปการนำทาง ไลบรารีนี้ช่วยลดโค้ด Boilerplate ข้อบกพร่องทั่วไปในแอป และปรับปรุงคุณภาพโดยรวมของประสบการณ์ของผู้ใช้ PIP ด้วยการมอบ API ที่รวมเป็นหนึ่งเดียว
ไลบรารี PiP ของ Jetpack ช่วยให้การใช้ API ของ PiP ที่มีอยู่สะดวกขึ้นด้วยการแก้ไขปัญหาและความไม่สอดคล้องที่สำคัญหลายประการในระบบนิเวศของ Android ดังนี้
- การแยกส่วนของระบบปฏิบัติการ: ไลบรารีจะจัดการความแตกต่างในการเรียก API ของ PiP
ใน Android เวอร์ชันต่างๆ โดยอัตโนมัติ เช่น การใช้
enterPictureInPictureModeก่อน Android 12 และisAutoEnterEnabledหลังจากนั้น เพื่อให้นักพัฒนาแอปไม่ต้องจัดการความแตกต่างของเวอร์ชัน - พารามิเตอร์ PiP ไม่ถูกต้อง: ไลบรารีมีโซลูชันแบบรวมสำหรับการตั้งค่าพารามิเตอร์ PiP อย่างถูกต้อง
เช่น
setSourceRectHintเพื่อสร้างภาพเคลื่อนไหวที่ราบรื่น และมีคุณภาพสูงระหว่างการเล่นสื่อ - Callback สถานะ PiP แบบรวม: ไลบรารีรวม
onPictureInPictureModeChangedและonPictureInPictureUiStateChangedไว้ในอินเทอร์เฟซ Callback แบบรวมเดียว (`PictureInPictureDelegate.OnPictureInPictureEventListener`) เพื่อให้การจัดการสถานะและ UI ง่ายขึ้น - การลดโค้ดสำเร็จรูป: ไลบรารีช่วยลดปริมาณโค้ดสำเร็จรูปที่ต้องใช้ซ้ำๆ ด้วยการนำเสนอชุด
RemoteActionsที่กำหนดไว้ล่วงหน้าสำหรับกรณีการใช้งานทั่วไป เช่น การควบคุมการเล่นและการดำเนินการวิดีโอคอล - การเตรียมพร้อมสำหรับอนาคต: ฟีเจอร์ PiP เพิ่มเติมจะให้บริการผ่านไลบรารี Jetpack ซึ่งช่วยให้ผู้ใช้เข้าถึงฟังก์ชันการทำงานเพิ่มเติมได้โดยใช้ความพยายามน้อยที่สุดหรือแทบไม่ต้องใช้เลย
ขั้นตอนการย้ายข้อมูล
ระบุหมวดหมู่กรณีการใช้งานของแอปและตรรกะ PiP เดิม
หมวดหมู่: การเล่นวิดีโอ การนำทาง หรือวิดีโอคอล
ตรรกะ PiP เดิมที่ต้องระบุ:
onUserLeaveHintsetAutoEnterEnabledonPictureInPictureModeChangedonPictureInPictureUiStateChangedsetPictureInPictureParams
2. การกำหนดค่า AndroidManifest
ตรวจสอบว่ากิจกรรมที่เข้าสู่ PiP ประกาศการรองรับใน AndroidManifest.xml ด้วย configChanges ที่จำเป็นเพื่อป้องกันการรีสตาร์ทที่ไม่จำเป็น
<activity
android:name="VideoActivity" android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
</activity>
3. การตั้งค่าสภาพแวดล้อม
เพิ่มการขึ้นต่อกันที่จำเป็นลงใน 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") }
ใช้ไลบรารี AndroidX ล่าสุดสำหรับการขึ้นต่อกันและดูข้อมูลดังกล่าวในหน้า รุ่น
4. การเลือกและการเริ่มต้นเทมเพลต
เลือกเทมเพลตการใช้งานที่เหมาะกับกรณีการใช้งานของแอปมากที่สุด
- การนำทางและวิดีโอคอล:
BasicPictureInPictureโดยทั่วไปจะไม่รองรับการปรับขนาดอย่างราบรื่น และคุณไม่จำเป็นต้องใช้คำแนะนำเกี่ยวกับสี่เหลี่ยมผืนผ้าต้นทาง - การเล่นวิดีโอ:
VideoPlaybackPictureInPictureโดยจะติดตามขอบเขตมุมมองของเพลเยอร์สำหรับคำแนะนำเกี่ยวกับสี่เหลี่ยมผืนผ้าต้นทางโดยอัตโนมัติ และเปิดใช้การปรับขนาดอย่างราบรื่นโดยค่าเริ่มต้น
หากต้องการใช้ไลบรารี Jetpack ให้แทนที่การใช้งาน PiP แบบกำหนดเองที่มีอยู่ด้วย API ของไลบรารี Jetpack ความซับซ้อนและค่าใช้จ่ายในการนำไปใช้จะแตกต่างกันไปตามการใช้งานปัจจุบันของแอป
ส่วนต่อไปนี้อธิบายกรณีการใช้งานทั่วไปบางส่วนของ PiP และขั้นตอนการใช้งานที่จำเป็น
การนำทาง
แอปจะแจ้งให้ไลบรารีทราบสถานะการใช้งานหรือไม่ใช้งานของการนำทาง และตั้งค่าอัตราส่วนกว้างยาว จากนั้นไลบรารี Jetpack จะจัดการส่วนที่เหลือให้
ความแตกต่างที่สำคัญ
- ไม่จำเป็นต้องแยกความแตกต่างระหว่างการเข้าอัตโนมัติและการเข้าแบบเดิมในฝั่งแอป
- อินเทอร์เฟซ Callback แบบรวม
- ตัวสร้าง
PictureInPictureParamsใหม่เพื่อความเข้ากันได้แบบย้อนหลัง
วิดีโอคอล
แอปจะแจ้งให้ไลบรารีทราบสถานะการใช้งานหรือไม่ใช้งานของการโทร และตั้งค่าอัตราส่วนกว้างยาว
ความแตกต่างที่สำคัญ
- ไม่จำเป็นต้องแยกความแตกต่างระหว่างการเข้าอัตโนมัติและการเข้าแบบเดิมในฝั่งแอป
- อินเทอร์เฟซ Callback แบบรวม
- ตัวสร้าง
PictureInPictureParamsใหม่เพื่อความเข้ากันได้แบบย้อนหลัง - ไอคอนการดำเนินการที่ได้มาตรฐานสำหรับวิดีโอคอล
5. การย้ายข้อมูลโค้ด
- ตรรกะการเข้า: แทนที่ตรรกะเฉพาะ API เช่น
setAutoEnterEnabledสำหรับ Android 12 ขึ้นไป หรือonUserLeaveHintสำหรับ Android 11 ลงไป ด้วยsetEnabledทริกเกอร์ฟังก์ชันนี้เมื่อใดก็ตามที่สถานะสิทธิ์เข้าใช้ PiP เปลี่ยนไป - Callback: รวม
onPictureInPictureModeChanged(การสลับเลย์เอาต์) และonPictureInPictureUiStateChanged(ภาพเคลื่อนไหว/สถานะ) ไว้ใน Callback ที่อิงตามเหตุการณ์แบบรวมonPictureInPictureEvent - การดำเนินการและพารามิเตอร์: อัปเดตพารามิเตอร์โดยใช้
setActionsและsetAspectRatioในอินสแตนซ์เทมเพลตเมื่อใดก็ตามที่พารามิเตอร์เปลี่ยนแปลง
รูปแบบการใช้งานอ้างอิง
ตัวอย่างการใช้งาน
การนำทางและวิดีโอคอล
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 */ } } } }
การเล่นวิดีโอ
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() } } } }