การฝังกิจกรรมจะเพิ่มประสิทธิภาพแอปในอุปกรณ์หน้าจอขนาดใหญ่โดยการแยกหน้าต่างงานของแอปพลิเคชันระหว่างกิจกรรม 2 รายการหรือ 2 อินสแตนซ์ของกิจกรรมเดียวกัน
หากแอปของคุณประกอบด้วยกิจกรรมหลายรายการ การฝังกิจกรรมจะช่วยให้คุณมอบประสบการณ์การใช้งานที่ดีขึ้นแก่ผู้ใช้ในแท็บเล็ต อุปกรณ์แบบพับ และอุปกรณ์ ChromeOS
การฝังกิจกรรมไม่จําเป็นต้องเปลี่ยนรูปแบบโค้ด คุณกำหนดวิธีที่แอปแสดงกิจกรรมได้ ไม่ว่าจะเป็นแสดงควบคู่กันหรือซ้อนกัน โดยการสร้างไฟล์การกำหนดค่า XML หรือการเรียกใช้ Jetpack WindowManager API
ระบบจะรองรับหน้าจอขนาดเล็กโดยอัตโนมัติ เมื่อแอปอยู่ในอุปกรณ์ที่มีหน้าจอขนาดเล็ก ระบบจะวางกิจกรรมซ้อนกัน ในหน้าจอขนาดใหญ่ กิจกรรมจะแสดงคู่กัน ระบบจะกำหนดการแสดงผลตามการกำหนดค่าที่คุณสร้างขึ้น โดยไม่จำเป็นต้องใช้ตรรกะการแยกย่อย
การฝังกิจกรรมรองรับการเปลี่ยนแปลงการวางแนวของอุปกรณ์และทำงานได้อย่างราบรื่นในอุปกรณ์แบบพับได้ โดยจะซ้อนและเลิกซ้อนกิจกรรมเมื่ออุปกรณ์พับหรือกางออก
การฝังกิจกรรมใช้ได้ในอุปกรณ์หน้าจอขนาดใหญ่ส่วนใหญ่ที่ใช้ Android 12L (API ระดับ 32) ขึ้นไป
แยกหน้าต่างงาน
การฝังกิจกรรมจะแบ่งหน้าต่างงานของแอปออกเป็น 2 คอนเทนเนอร์ ได้แก่ คอนเทนเนอร์หลักและคอนเทนเนอร์รอง คอนเทนเนอร์จะเก็บกิจกรรมที่เปิดจากกิจกรรมหลักหรือจากกิจกรรมอื่นๆ ที่อยู่ในคอนเทนเนอร์อยู่แล้ว
ระบบจะซ้อนกิจกรรมในคอนเทนเนอร์รองเมื่อเปิดใช้งาน และซ้อนคอนเทนเนอร์รองไว้บนคอนเทนเนอร์หลักบนหน้าจอขนาดเล็ก เพื่อให้การซ้อนกิจกรรมและการไปยังส่วนหลังสอดคล้องกับลําดับของกิจกรรมที่สร้างไว้ในแอปอยู่แล้ว
การฝังกิจกรรมช่วยให้คุณแสดงกิจกรรมได้หลายวิธี แอปสามารถแยกหน้าต่างงานโดยเปิดกิจกรรม 2 รายการพร้อมกันเคียงข้างกันได้ ดังนี้
หรือกิจกรรมที่ครอบครองหน้าต่างงานทั้งหน้าต่างอาจแยกออกเป็น 2 หน้าต่างได้ด้วยการเปิดกิจกรรมใหม่ควบคู่กัน ดังนี้
กิจกรรมที่แยกอยู่และแชร์หน้าต่างงานอยู่แล้วจะเปิดกิจกรรมอื่นๆ ได้ดังนี้
ที่ด้านข้างบนกิจกรรมอื่น
ไปด้านข้าง แล้วเลื่อนการแยกไปด้านข้างเพื่อซ่อนกิจกรรมหลักก่อนหน้า
เปิดกิจกรรมที่ด้านบน ซึ่งก็คือในสแต็กกิจกรรมเดียวกัน
วิธีเปิดกิจกรรมแบบเต็มหน้าจอในงานเดียวกัน
การนำทางกลับ
แอปพลิเคชันประเภทต่างๆ อาจมีกฎการนำทางกลับที่แตกต่างกันในสถานะหน้าต่างงานแบบแยก ขึ้นอยู่กับความสัมพันธ์ระหว่างกิจกรรมหรือวิธีที่ผู้ใช้เรียกเหตุการณ์กลับ เช่น
- แสดงร่วมกัน: หากกิจกรรมมีความเกี่ยวข้องกันและไม่ควรแสดงกิจกรรมใดกิจกรรมหนึ่งโดยไม่มีอีกกิจกรรมหนึ่ง คุณจะกำหนดค่าการไปยังส่วนหลังเพื่อให้แสดงทั้ง 2 กิจกรรมจนเสร็จสิ้นได้
- ทำงานคนเดียว: หากกิจกรรมทำงานอย่างอิสระทั้งหมด การนำทางกลับในกิจกรรมหนึ่งจะไม่ส่งผลต่อสถานะของกิจกรรมอื่นในหน้าต่างงาน
ระบบจะส่งเหตุการณ์ "กลับ" ไปยังกิจกรรมที่โฟกัสล่าสุดเมื่อใช้การไปยังส่วนต่างๆ ด้วยปุ่ม
สําหรับการไปยังส่วนต่างๆ ด้วยท่าทางสัมผัส ให้ทำดังนี้
Android 14 (API ระดับ 34) และต่ำกว่า — ระบบจะส่งเหตุการณ์ "กลับ" ไปยังกิจกรรมที่เกิดท่าทางสัมผัส เมื่อผู้ใช้ปัดจากด้านซ้ายของหน้าจอ ระบบจะส่งเหตุการณ์ "กลับ" ไปยังกิจกรรมในแผงด้านซ้ายของหน้าต่างที่แยก เมื่อผู้ใช้ปัดจากด้านขวาของหน้าจอ ระบบจะส่งเหตุการณ์ "กลับ" ไปยังกิจกรรมในแผงด้านขวา
Android 15 (API ระดับ 35) ขึ้นไป
เมื่อจัดการกับกิจกรรมหลายรายการจากแอปเดียวกัน ท่าทางสัมผัสจะดำเนินการกิจกรรมด้านบนให้เสร็จสมบูรณ์ไม่ว่าจะปัดไปในทิศทางใดก็ตาม เพื่อให้คุณได้รับประสบการณ์การใช้งานที่รวมเป็นหนึ่งเดียวมากขึ้น
ในสภาวะการณ์ที่เกี่ยวข้องกับกิจกรรม 2 รายการจากแอปที่แตกต่างกัน (การวางซ้อน) เหตุการณ์ "กลับ" จะนําไปยังกิจกรรมล่าสุดที่มีโฟกัส ซึ่งสอดคล้องกับลักษณะการนําทางด้วยปุ่ม
เลย์เอาต์แบบหลายแผง
Jetpack WindowManager ช่วยให้คุณสร้างกิจกรรมที่ฝังเลย์เอาต์หลายแผงบนอุปกรณ์หน้าจอขนาดใหญ่ที่ใช้ Android 12L (API ระดับ 32) ขึ้นไป และในอุปกรณ์บางรุ่นที่ใช้แพลตฟอร์มเวอร์ชันเก่า แอปที่มีอยู่ซึ่งอิงตามกิจกรรมหลายรายการแทนที่จะเป็นแฟรกเมนต์หรือเลย์เอาต์ตามมุมมอง เช่น SlidingPaneLayout
มอบประสบการณ์การใช้งานบนหน้าจอขนาดใหญ่ที่ดีขึ้นได้โดยไม่ต้องรีแฟกทอริงซอร์สโค้ด
ตัวอย่างที่พบบ่อยอย่างหนึ่งคือการแยกรายการเป็นรายละเอียด ระบบจะเริ่มกิจกรรมรายการ จากนั้นแอปพลิเคชันจะเริ่มกิจกรรมแบบละเอียดทันทีเพื่อให้การนำเสนอมีคุณภาพสูง ระบบการเปลี่ยนภาพจะรอจนกว่ากิจกรรมทั้ง 2 รายการจะวาดเสร็จ แล้วจึงแสดงกิจกรรมเหล่านั้นร่วมกัน ผู้ใช้จะเห็นกิจกรรมทั้ง 2 รายการเปิดขึ้นพร้อมกัน
แอตทริบิวต์แยก
คุณสามารถระบุสัดส่วนหน้าต่างงานระหว่างคอนเทนเนอร์ที่แยก และการวางคอนเทนเนอร์เทียบกับคอนเทนเนอร์อื่น
สำหรับกฎที่กําหนดไว้ในไฟล์การกําหนดค่า XML ให้ตั้งค่าแอตทริบิวต์ต่อไปนี้
splitRatio
: กำหนดสัดส่วนคอนเทนเนอร์ ค่าคือตัวเลขทศนิยมในช่วงเปิด (0.0, 1.0)splitLayoutDirection
: ระบุวิธีจัดวางคอนเทนเนอร์ที่แยกกันเมื่อเทียบกับคอนเทนเนอร์อื่นๆ ค่าต่างๆ ได้แก่ltr
: ซ้ายไปขวาrtl
: ขวาไปซ้ายlocale
:ltr
หรือrtl
จะกำหนดจากการตั้งค่าภาษา
ดูตัวอย่างได้ที่ส่วนการกําหนดค่า XML
สําหรับกฎที่สร้างโดยใช้ WindowManager API ให้สร้างออบเจ็กต์ SplitAttributes
ด้วย SplitAttributes.Builder
แล้วเรียกใช้เมธอดผู้สร้างต่อไปนี้
setSplitType()
: กำหนดสัดส่วนของคอนเทนเนอร์ที่แยก ดูอาร์กิวเมนต์ที่ถูกต้อง รวมถึงเมธอดSplitAttributes.SplitType.ratio()
ในSplitAttributes.SplitType
setLayoutDirection()
: ตั้งค่าเลย์เอาต์ของคอนเทนเนอร์ โปรดดูค่าที่เป็นไปได้ในSplitAttributes.LayoutDirection
ดูตัวอย่างได้ที่ส่วน WindowManager API
ตัวยึดตําแหน่ง
กิจกรรมตัวยึดตําแหน่งคือกิจกรรมรองที่ว่างเปล่าซึ่งครอบครองพื้นที่ของการแยกกิจกรรม ท้ายที่สุดแล้ว กิจกรรมเหล่านี้มีไว้เพื่อแทนที่ด้วยกิจกรรมอื่นซึ่งมีเนื้อหา ตัวอย่างเช่น กิจกรรมตัวยึดตำแหน่งอาจอยู่ด้านรองของกิจกรรมที่แยกในเลย์เอาต์รายการแบบละเอียดจนกว่าจะมีการเลือกรายการจากรายการ เมื่อถึงจุดนั้น กิจกรรมที่มีข้อมูลรายละเอียดสำหรับรายการที่เลือกจะแทนที่ตัวยึดตำแหน่ง
โดยค่าเริ่มต้น ระบบจะแสดงตัวยึดตําแหน่งเฉพาะเมื่อมีพื้นที่เพียงพอสําหรับการแยกกิจกรรม ตัวยึดตําแหน่งจะสิ้นสุดโดยอัตโนมัติเมื่อขนาดการแสดงผลเปลี่ยนไปเป็นความกว้างหรือความสูงที่เล็กเกินกว่าที่จะแสดงการแยก เมื่อมีพื้นที่เพียงพอ ระบบจะเปิดใช้งานตัวยึดตําแหน่งอีกครั้งโดยสถานะที่รีนิไทซ์ใหม่
อย่างไรก็ตาม แอตทริบิวต์ stickyPlaceholder
ของเมธอด SplitPlaceholderRule
หรือ setSticky()
ของ SplitPlaceholder.Builder
สามารถลบล้างลักษณะการทำงานเริ่มต้นได้ เมื่อแอตทริบิวต์หรือเมธอดระบุค่าเป็น true
ระบบจะแสดงตัวยึดตําแหน่งเป็นกิจกรรมบนสุดในหน้าต่างงานเมื่อปรับขนาดการแสดงผลเป็นหน้าจอเดียวจากหน้าจอแบบ 2 แผง (ดูตัวอย่างที่การกําหนดค่าแบบแยก)
การเปลี่ยนแปลงขนาดหน้าต่าง
เมื่อการเปลี่ยนแปลงการกำหนดค่าอุปกรณ์ทำให้หน้าต่างงานมีความกว้างไม่เพียงพอสำหรับเลย์เอาต์หลายแผง (เช่น เมื่ออุปกรณ์แบบพับหน้าจอขนาดใหญ่พับจากขนาดแท็บเล็ตเป็นขนาดโทรศัพท์ หรือหน้าต่างแอปมีการปรับขนาดในโหมดหลายหน้าต่าง) กิจกรรมที่ไม่ใช่ตัวยึดตำแหน่งในแผงรองของหน้าต่างงานจะซ้อนกันอยู่ด้านบนของกิจกรรมในแผงหลัก
กิจกรรมตัวยึดตําแหน่งจะแสดงเฉพาะเมื่อมีการแสดงผลกว้างพอสําหรับการแยก ในหน้าจอขนาดเล็ก ตัวยึดตำแหน่งจะปิดโดยอัตโนมัติ เมื่อพื้นที่แสดงผลมีขนาดใหญ่พออีกครั้ง ระบบจะสร้างตัวยึดตำแหน่งขึ้นมาใหม่ (ดูส่วนตัวยึดตําแหน่ง)
การวางซ้อนกิจกรรมเป็นไปได้เนื่องจาก WindowManager จัดลําดับกิจกรรมในลําดับ Z ในแผงรองเหนือกิจกรรมในแผงหลัก
กิจกรรมหลายรายการในแผงรอง
กิจกรรม ข เริ่มกิจกรรม ค แทนโดยไม่มี Flag Intent เพิ่มเติม
ส่งผลให้กิจกรรมในลำดับ z ของงานเดียวกันมีดังนี้
ดังนั้นในหน้าต่างงานขนาดเล็ก แอปพลิเคชันจะย่อเป็นกิจกรรมเดียวที่มี C ที่ด้านบนของกองดังนี้
การย้อนกลับในหน้าต่างขนาดเล็กจะเป็นการไปยังกิจกรรมที่ซ้อนกันอยู่
หากคืนค่าการกำหนดค่าหน้าต่างงานเป็นขนาดที่ใหญ่ขึ้นซึ่งรองรับหลายแผง กิจกรรมจะแสดงคู่กันอีกครั้ง
การแยกส่วนแบบซ้อน
กิจกรรม ข เริ่มกิจกรรม ค ทางด้านข้างและเลื่อนการแยกไปด้านข้าง
ผลลัพธ์ที่ได้คือลําดับ z ของกิจกรรมในงานเดียวกันดังต่อไปนี้
ในหน้าต่างงานขนาดเล็ก แอปพลิเคชันจะย่อขนาดเป็นกิจกรรมเดียวที่มี C อยู่ด้านบน
การวางแนวตั้งแบบคงที่
การตั้งค่าไฟล์ Manifest android:screenOrientation ช่วยให้แอปจำกัดกิจกรรมให้เป็นแนวตั้งหรือแนวนอนได้ ผู้ผลิตอุปกรณ์ (OEM) สามารถละเว้นคำขอการวางแนวหน้าจอและแสดงแอปในแนวตั้งในจอแสดงผลแนวนอนหรือแนวนอนในจอแสดงผลแนวตั้งได้ เพื่อปรับปรุงประสบการณ์ของผู้ใช้บนอุปกรณ์หน้าจอขนาดใหญ่ เช่น แท็บเล็ตและอุปกรณ์แบบพับได้
ในทำนองเดียวกัน เมื่อเปิดใช้การฝังกิจกรรม OEM จะปรับแต่งอุปกรณ์ให้แสดงกิจกรรมในแนวตั้งแบบ Letterbox แบบคงที่ในแนวนอนบนหน้าจอขนาดใหญ่ได้ (ความกว้าง ≥ 600dp) เมื่อกิจกรรมแนวตั้งแบบคงที่เปิดกิจกรรมที่ 2 อุปกรณ์จะแสดงกิจกรรมทั้ง 2 รายการควบคู่กันในการแสดงผลแบบ 2 แผง
เพิ่มพร็อพเพอร์ตี้ android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
ลงในไฟล์ Manifest ของแอปเสมอเพื่อแจ้งให้อุปกรณ์ทราบว่าแอปของคุณรองรับการฝังกิจกรรม (ดูส่วนการกำหนดค่าแบบแยก) จากนั้นอุปกรณ์ที่ปรับแต่งโดย OEM จะกำหนดได้ว่าจะใส่แถบดำด้านบนและด้านล่างเพื่อปรับขนาดกิจกรรมแบบตั้งตรงแบบคงที่หรือไม่
การกำหนดค่าการแยก
กฎการแยกจะกำหนดค่าการแยกกิจกรรม คุณกำหนดกฎการแยกในไฟล์การกำหนดค่า XML หรือโดยการเรียกใช้ WindowManager API ของ Jetpack
ไม่ว่าในกรณีใด แอปของคุณต้องเข้าถึงไลบรารี WindowManager และต้องแจ้งให้ระบบทราบว่าแอปได้ติดตั้งใช้งานการฝังกิจกรรมแล้ว
ทําดังนี้
เพิ่มทรัพยากร Dependency ของไลบรารี WindowManager เวอร์ชันล่าสุดลงในไฟล์
build.gradle
ระดับโมดูลของแอป เช่นimplementation 'androidx.window:window:1.1.0-beta02'
ไลบรารี WindowManager มีคอมโพเนนต์ทั้งหมดที่จําเป็นสําหรับการฝังกิจกรรม
แจ้งให้ระบบทราบว่าแอปของคุณใช้การฝังกิจกรรม
เพิ่มพร็อพเพอร์ตี้
android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
ลงในองค์ประกอบ <application> ของไฟล์ Manifest ของแอป และตั้งค่าเป็น "จริง" ตัวอย่างเช่น<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <property android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED" android:value="true" /> </application> </manifest>
ใน WindowManager เวอร์ชัน 1.1.0-alpha06 ขึ้นไป ระบบจะปิดใช้การแยกการฝังกิจกรรม เว้นแต่จะมีการเพิ่มพร็อพเพอร์ตี้ลงในไฟล์ Manifest และตั้งค่าเป็น true
นอกจากนี้ ผู้ผลิตอุปกรณ์ยังใช้การตั้งค่านี้เพื่อเปิดใช้ความสามารถที่กำหนดเองสำหรับแอปที่รองรับการฝังกิจกรรม เช่น อุปกรณ์อาจแสดงกิจกรรมในแนวตั้งเท่านั้นในจอแสดงผลแนวนอนเพื่อปรับแนวของกิจกรรมสำหรับการเปลี่ยนไปใช้เลย์เอาต์แบบ 2 แผงเมื่อกิจกรรมที่ 2 เริ่มขึ้น (ดูการวางแนวแนวตั้งแบบคงที่)
การกําหนดค่า XML
หากต้องการสร้างการใช้งานการฝังกิจกรรมที่อิงตาม XML ให้ทําตามขั้นตอนต่อไปนี้
สร้างไฟล์ทรัพยากร XML ที่จะทําสิ่งต่อไปนี้
- กําหนดกิจกรรมที่มีการแบ่ง
- กําหนดค่าตัวเลือกการแยก
- สร้างตัวยึดตำแหน่งสำหรับคอนเทนเนอร์รองของข้อมูลที่แยกเมื่อไม่มีเนื้อหา
- ระบุกิจกรรมที่ไม่ควรเป็นส่วนหนึ่งของการแยก
เช่น
<!-- main_split_config.xml --> <resources xmlns:window="http://schemas.android.com/apk/res-auto"> <!-- Define a split for the named activities. --> <SplitPairRule window:splitRatio="0.33" window:splitLayoutDirection="locale" window:splitMinWidthDp="840" window:splitMaxAspectRatioInPortrait="alwaysAllow" window:finishPrimaryWithSecondary="never" window:finishSecondaryWithPrimary="always" window:clearTop="false"> <SplitPairFilter window:primaryActivityName=".ListActivity" window:secondaryActivityName=".DetailActivity"/> </SplitPairRule> <!-- Specify a placeholder for the secondary container when content is not available. --> <SplitPlaceholderRule window:placeholderActivityName=".PlaceholderActivity" window:splitRatio="0.33" window:splitLayoutDirection="locale" window:splitMinWidthDp="840" window:splitMaxAspectRatioInPortrait="alwaysAllow" window:stickyPlaceholder="false"> <ActivityFilter window:activityName=".ListActivity"/> </SplitPlaceholderRule> <!-- Define activities that should never be part of a split. Note: Takes precedence over other split rules for the activity named in the rule. --> <ActivityRule window:alwaysExpand="true"> <ActivityFilter window:activityName=".ExpandedActivity"/> </ActivityRule> </resources>
สร้างตัวเริ่มต้น
คอมโพเนนต์ WindowManager
RuleController
จะแยกวิเคราะห์ไฟล์การกําหนดค่า XML และทําให้ระบบใช้กฎได้ ไลบรารี Startup ของ JetpackInitializer
จะทำให้ไฟล์ XML พร้อมใช้งานสำหรับRuleController
เมื่อแอปเริ่มต้นขึ้น เพื่อให้กฎมีผลเมื่อกิจกรรมใดๆ เริ่มต้นขึ้นวิธีสร้างตัวเริ่มต้นมีดังนี้
เพิ่มทรัพยากร Dependency ของไลบรารี Jetpack Startup เวอร์ชันล่าสุดลงในไฟล์
build.gradle
ระดับโมดูล เช่นimplementation 'androidx.startup:startup-runtime:1.1.1'
สร้างคลาสที่ใช้อินเทอร์เฟซ
Initializer
ตัวเริ่มต้นทำให้
RuleController
ใช้กฎการแยกได้ด้วยการส่งรหัสของไฟล์การกําหนดค่า XML (main_split_config.xml
) ไปยังเมธอดRuleController.parseRules()
Kotlin
class SplitInitializer : Initializer<RuleController> { override fun create(context: Context): RuleController { return RuleController.getInstance(context).apply { setRules(RuleController.parseRules(context, R.xml.main_split_config)) } } override fun dependencies(): List<Class<out Initializer<*>>> { return emptyList() } }
Java
public class SplitInitializer implements Initializer<RuleController> { @NonNull @Override public RuleController create(@NonNull Context context) { RuleController ruleController = RuleController.getInstance(context); ruleController.setRules( RuleController.parseRules(context, R.xml.main_split_config) ); return ruleController; } @NonNull @Override public List<Class<? extends Initializer<?>>> dependencies() { return Collections.emptyList(); } }
สร้างผู้ให้บริการเนื้อหาสําหรับคําจํากัดความของกฎ
เพิ่ม
androidx.startup.InitializationProvider
ลงในไฟล์ Manifest ของแอปเป็น<provider>
ใส่การอ้างอิงถึงการใช้งานRuleController
initializerSplitInitializer
ดังนี้<!-- AndroidManifest.xml --> <provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> <!-- Make SplitInitializer discoverable by InitializationProvider. --> <meta-data android:name="${applicationId}.SplitInitializer" android:value="androidx.startup" /> </provider>
InitializationProvider
จะค้นหาและเริ่มต้นSplitInitializer
ก่อนเรียกใช้เมธอดonCreate()
ของแอป กฎการแยกจึงมีผลเมื่อกิจกรรมหลักของแอปเริ่มต้น
WindowManager API
คุณใช้การฝังกิจกรรมแบบเป็นโปรแกรมได้ด้วยการเรียก API เพียงไม่กี่ครั้ง เรียกใช้เมธอด onCreate()
ของคลาสย่อยของ Application
เพื่อให้แน่ใจว่ากฎมีผลบังคับใช้ก่อนกิจกรรมใดๆ จะเริ่มต้น
หากต้องการสร้างการแยกกิจกรรมแบบเป็นโปรแกรม ให้ทําดังนี้
สร้างกฎการแยก
สร้าง
SplitPairFilter
ที่ระบุกิจกรรมที่แชร์การแยกKotlin
val splitPairFilter = SplitPairFilter( ComponentName(this, ListActivity::class.java), ComponentName(this, DetailActivity::class.java), null )
Java
SplitPairFilter splitPairFilter = new SplitPairFilter( new ComponentName(this, ListActivity.class), new ComponentName(this, DetailActivity.class), null );
เพิ่มตัวกรองลงในชุดตัวกรอง
Kotlin
val filterSet = setOf(splitPairFilter)
Java
Set<SplitPairFilter> filterSet = new HashSet<>(); filterSet.add(splitPairFilter);
สร้างแอตทริบิวต์เลย์เอาต์สำหรับการแยก
Kotlin
val splitAttributes: SplitAttributes = SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build()
Java
final SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build();
SplitAttributes.Builder
สร้างออบเจ็กต์ที่มีแอตทริบิวต์เลย์เอาต์ ดังนี้setSplitType()
: กําหนดวิธีจัดสรรพื้นที่แสดงผลที่มีอยู่ให้กับคอนเทนเนอร์กิจกรรมแต่ละรายการ ประเภทการแยกตามสัดส่วนจะระบุสัดส่วนพื้นที่โฆษณาที่มีอยู่ซึ่งจัดสรรให้กับคอนเทนเนอร์หลัก ส่วนคอนเทนเนอร์รองจะใช้พื้นที่โฆษณาที่เหลือsetLayoutDirection()
: ระบุวิธีจัดวางคอนเทนเนอร์กิจกรรมโดยสัมพันธ์กัน โดยให้คอนเทนเนอร์หลักแสดงก่อน
สร้าง
SplitPairRule
Kotlin
val splitPairRule = SplitPairRule.Builder(filterSet) .setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER) .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS) .setClearTop(false) .build()
Java
SplitPairRule splitPairRule = new SplitPairRule.Builder(filterSet) .setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER) .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS) .setClearTop(false) .build();
SplitPairRule.Builder
สร้างและกําหนดค่ากฎfilterSet
: มีตัวกรองคู่การแยกที่กําหนดเวลาที่จะใช้กฎโดยระบุกิจกรรมที่แชร์การแยกsetDefaultSplitAttributes()
: ใช้แอตทริบิวต์เลย์เอาต์กับกฎsetMinWidthDp()
: ตั้งค่าความกว้างในการแสดงผลขั้นต่ำ (เป็นพิกเซลแบบไม่ขึ้นอยู่กับความหนาแน่น dp) ที่เปิดใช้การแยกsetMinSmallestWidthDp()
: ตั้งค่าค่าต่ำสุด (เป็น dp) ที่ขนาดการแสดงผลที่เล็กกว่าของ 2 ขนาดต้องมีค่าเพื่อให้สามารถแยกหน้าจอได้ ไม่ว่าจะปรับแนวของอุปกรณ์เป็นแนวตั้งหรือแนวนอนsetMaxAspectRatioInPortrait()
: ตั้งค่าสัดส่วนการแสดงผลสูงสุด (ความสูง:ความกว้าง) ในแนวตั้งที่จะแสดงการแยกกิจกรรม หากสัดส่วนภาพของการแสดงผลแนวตั้งเกินสัดส่วนภาพสูงสุด ระบบจะปิดใช้การแยกโดยไม่คำนึงถึงความกว้างของจอแสดงผล หมายเหตุ: ค่าเริ่มต้นคือ 1.4 ซึ่งจะทำให้กิจกรรมครอบครองหน้าต่างงานทั้งหน้าต่างในแนวตั้งในแท็บเล็ตส่วนใหญ่ โปรดดูSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
และsetMaxAspectRatioInLandscape()
ด้วย ค่าเริ่มต้นสำหรับภาพแนวนอนคือALWAYS_ALLOW
setFinishPrimaryWithSecondary()
: ตั้งค่าว่าการทำกิจกรรมทั้งหมดในคอนเทนเนอร์รองส่งผลต่อกิจกรรมในคอนเทนเนอร์หลักอย่างไรNEVER
บ่งบอกว่าระบบไม่ควรสิ้นสุดกิจกรรมหลักเมื่อกิจกรรมทั้งหมดในคอนเทนเนอร์รองสิ้นสุด (ดูสิ้นสุดกิจกรรม)setFinishSecondaryWithPrimary()
: ตั้งค่าว่าการทำกิจกรรมทั้งหมดในคอนเทนเนอร์หลักส่งผลต่อกิจกรรมในคอนเทนเนอร์รองอย่างไรALWAYS
บ่งบอกว่าระบบควรทำกิจกรรมในคอนเทนเนอร์รองให้เสร็จสิ้นเสมอเมื่อกิจกรรมทั้งหมดในคอนเทนเนอร์หลักเสร็จสิ้น (ดูทำกิจกรรมให้เสร็จสิ้น)setClearTop()
: ระบุว่ากิจกรรมทั้งหมดในคอนเทนเนอร์รองจะสิ้นสุดลงหรือไม่เมื่อมีการเริ่มกิจกรรมใหม่ในคอนเทนเนอร์ ค่าfalse
ระบุว่าระบบจะวางกิจกรรมใหม่ซ้อนทับกิจกรรมที่อยู่ในคอนเทนเนอร์รองอยู่แล้ว
รับอินสแตนซ์แบบ Singleton ของ WindowManager
RuleController
และเพิ่มกฎKotlin
val ruleController = RuleController.getInstance(this) ruleController.addRule(splitPairRule)
Java
RuleController ruleController = RuleController.getInstance(this); ruleController.addRule(splitPairRule);
สร้างตัวยึดตำแหน่งสำหรับคอนเทนเนอร์รองเมื่อไม่มีเนื้อหา
สร้าง
ActivityFilter
ที่ระบุกิจกรรมที่ตัวยึดตำแหน่งใช้ร่วมกันในหน้าต่างงานKotlin
val placeholderActivityFilter = ActivityFilter( ComponentName(this, ListActivity::class.java), null )
Java
ActivityFilter placeholderActivityFilter = new ActivityFilter( new ComponentName(this, ListActivity.class), null );
เพิ่มตัวกรองลงในชุดตัวกรอง
Kotlin
val placeholderActivityFilterSet = setOf(placeholderActivityFilter)
Java
Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>(); placeholderActivityFilterSet.add(placeholderActivityFilter);
สร้าง
SplitPlaceholderRule
Kotlin
val splitPlaceholderRule = SplitPlaceholderRule.Builder( placeholderActivityFilterSet, Intent(context, PlaceholderActivity::class.java) ).setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS) .setSticky(false) .build()
Java
SplitPlaceholderRule splitPlaceholderRule = new SplitPlaceholderRule.Builder( placeholderActivityFilterSet, new Intent(context, PlaceholderActivity.class) ).setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS) .setSticky(false) .build();
SplitPlaceholderRule.Builder
สร้างและกําหนดค่ากฎplaceholderActivityFilterSet
: มีตัวกรองกิจกรรมที่กําหนดเวลาที่จะใช้กฎโดยระบุกิจกรรมที่เชื่อมโยงกับกิจกรรมตัวยึดตําแหน่งIntent
: ระบุการเปิดตัวกิจกรรมตัวยึดตำแหน่งsetDefaultSplitAttributes()
: ใช้แอตทริบิวต์เลย์เอาต์กับกฎsetMinWidthDp()
: ตั้งค่าความกว้างของการแสดงผลขั้นต่ำ (เป็นพิกเซลแบบไม่อิงตามความหนาแน่น dp) ที่อนุญาตให้แยกsetMinSmallestWidthDp()
: ตั้งค่าต่ำสุด (เป็น dp) ที่ขนาดการแสดงผลที่เล็กกว่าของ 2 ขนาดต้องมีขนาดเท่าใดจึงจะแยกหน้าจอได้ ไม่ว่าจะปรับแนวของอุปกรณ์เป็นแนวตั้งหรือแนวนอนsetMaxAspectRatioInPortrait()
: ตั้งค่าสัดส่วนการแสดงผลสูงสุด (ความสูง:ความกว้าง) ในแนวตั้งสำหรับการแสดงการแยกกิจกรรม หมายเหตุ: ค่าเริ่มต้นคือ 1.4 ซึ่งจะทำให้กิจกรรมเต็มหน้าต่างงานในแนวตั้งบนแท็บเล็ตส่วนใหญ่ โปรดดูSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
และsetMaxAspectRatioInLandscape()
ด้วย ค่าเริ่มต้นสำหรับแนวนอนคือALWAYS_ALLOW
setFinishPrimaryWithPlaceholder()
: ตั้งค่าว่าการทำกิจกรรมตัวยึดตำแหน่งเสร็จสิ้นส่งผลต่อกิจกรรมในคอนเทนเนอร์หลักอย่างไร ALWAYS บ่งบอกว่าระบบควรทำกิจกรรมในคอนเทนเนอร์หลักให้เสร็จสิ้นเสมอเมื่อตัวยึดตำแหน่งเสร็จสิ้น (ดูทำกิจกรรมให้เสร็จสิ้น)setSticky()
: กำหนดว่ากิจกรรมตัวยึดตำแหน่งจะปรากฏที่ด้านบนของกองกิจกรรมบนจอแสดงผลขนาดเล็กหรือไม่เมื่อตัวยึดตำแหน่งปรากฏขึ้นครั้งแรกในการแยกที่มีความกว้างขั้นต่ำเพียงพอ
เพิ่มกฎลงใน WindowManager
RuleController
Kotlin
ruleController.addRule(splitPlaceholderRule)
Java
ruleController.addRule(splitPlaceholderRule);
ระบุกิจกรรมที่ไม่ควรเป็นส่วนหนึ่งของการแยก
สร้าง
ActivityFilter
ที่ระบุกิจกรรมที่ควรใช้พื้นที่แสดงงานทั้งหมดเสมอ ดังนี้Kotlin
val expandedActivityFilter = ActivityFilter( ComponentName(this, ExpandedActivity::class.java), null )
Java
ActivityFilter expandedActivityFilter = new ActivityFilter( new ComponentName(this, ExpandedActivity.class), null );
เพิ่มตัวกรองลงในชุดตัวกรอง
Kotlin
val expandedActivityFilterSet = setOf(expandedActivityFilter)
Java
Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>(); expandedActivityFilterSet.add(expandedActivityFilter);
วิธีสร้าง
ActivityRule
Kotlin
val activityRule = ActivityRule.Builder(expandedActivityFilterSet) .setAlwaysExpand(true) .build()
Java
ActivityRule activityRule = new ActivityRule.Builder( expandedActivityFilterSet ).setAlwaysExpand(true) .build();
ActivityRule.Builder
สร้างและกําหนดค่ากฎexpandedActivityFilterSet
: มีตัวกรองกิจกรรมที่กําหนดเวลาที่จะใช้กฎโดยระบุกิจกรรมที่คุณต้องการยกเว้นจากการแยกsetAlwaysExpand()
: ระบุว่ากิจกรรมควรแสดงเต็มหน้าต่างงานหรือไม่
เพิ่มกฎลงใน WindowManager
RuleController
Kotlin
ruleController.addRule(activityRule)
Java
ruleController.addRule(activityRule);
การฝังข้ามแอปพลิเคชัน
ใน Android 13 (API ระดับ 33) ขึ้นไป แอปจะฝังกิจกรรมจากแอปอื่นๆ ได้ การฝังกิจกรรมข้ามแอปหรือข้าม UID ช่วยในการผสานรวมกิจกรรมจากแอปพลิเคชัน Android หลายรายการในลักษณะภาพ ระบบจะแสดงกิจกรรมของแอปโฮสต์และกิจกรรมที่ฝังจากแอปอื่นบนหน้าจอควบคู่กันหรือด้านบนและด้านล่าง เช่นเดียวกับการฝังกิจกรรมในแอปเดียว
ตัวอย่างเช่น แอปการตั้งค่าอาจฝังกิจกรรมตัวเลือกวอลเปเปอร์จากแอป WallpaperPicker ดังนี้
รูปแบบความน่าเชื่อถือ
กระบวนการโฮสต์ที่ฝังกิจกรรมจากแอปอื่นๆ สามารถกำหนดการแสดงกิจกรรมที่ฝังใหม่ได้ ซึ่งรวมถึงขนาด ตำแหน่ง การครอบตัด และความโปร่งใส โฮสต์ที่เป็นอันตรายสามารถใช้ความสามารถนี้เพื่อทำให้ผู้ใช้เข้าใจผิดและสร้างการคลิกจากระยะไกลหรือโจมตีอื่นๆ ที่เปลี่ยนรูปแบบ UI
Android กําหนดให้แอปเลือกใช้เพื่ออนุญาตการฝังกิจกรรมเพื่อป้องกันการใช้การฝังกิจกรรมข้ามแอปในทางที่ผิด แอปสามารถกำหนดโฮสต์ว่าเชื่อถือได้หรือไม่เชื่อถือได้
โฮสต์ที่เชื่อถือได้
หากต้องการอนุญาตให้แอปพลิเคชันอื่นๆ ฝังและควบคุมการแสดงกิจกรรมจากแอปของคุณอย่างเต็มรูปแบบ ให้ระบุใบรับรอง SHA-256 ของแอปพลิเคชันโฮสต์ในแอตทริบิวต์ android:knownActivityEmbeddingCerts
ขององค์ประกอบ <activity>
หรือ <application>
ในไฟล์ Manifest ของแอป
ตั้งค่า android:knownActivityEmbeddingCerts
เป็นสตริง ดังนี้
<activity
android:name=".MyEmbeddableActivity"
android:knownActivityEmbeddingCerts="@string/known_host_certificate_digest"
... />
หรือหากต้องการระบุใบรับรองหลายรายการ ให้ใช้อาร์เรย์สตริง ดังนี้
<activity
android:name=".MyEmbeddableActivity"
android:knownActivityEmbeddingCerts="@array/known_host_certificate_digests"
... />
ซึ่งอ้างอิงทรัพยากรดังต่อไปนี้
<resources>
<string-array name="known_host_certificate_digests">
<item>cert1</item>
<item>cert2</item>
...
</string-array>
</resources>
เจ้าของแอปสามารถรับข้อมูลสรุปใบรับรอง SHA ได้โดยเรียกใช้งาน Gradle
signingReport
ข้อมูลสรุปของใบรับรองคือลายนิ้วมือ SHA-256 ที่ไม่มีโคลอนคั่น โปรดดูข้อมูลเพิ่มเติมที่หัวข้อเรียกใช้รายงานการรับรองและการตรวจสอบสิทธิ์ไคลเอ็นต์
โฮสต์ที่ไม่น่าเชื่อถือ
หากต้องการอนุญาตให้แอปใดก็ตามฝังกิจกรรมของแอปและควบคุมการแสดงผล ให้ระบุแอตทริบิวต์ android:allowUntrustedActivityEmbedding
ในองค์ประกอบ <activity>
หรือ <application>
ในไฟล์ Manifest ของแอป เช่น
<activity
android:name=".MyEmbeddableActivity"
android:allowUntrustedActivityEmbedding="true"
... />
ค่าเริ่มต้นของแอตทริบิวต์คือเท็จ ซึ่งจะป้องกันไม่ให้ฝังกิจกรรมข้ามแอป
การตรวจสอบสิทธิ์ที่กำหนดเอง
หากต้องการลดความเสี่ยงของการฝังกิจกรรมที่ไม่น่าเชื่อถือ ให้สร้างกลไกการตรวจสอบสิทธิ์ที่กำหนดเองซึ่งจะยืนยันตัวตนของโฮสต์ หากทราบใบรับรองของโฮสต์ ให้ใช้ไลบรารี androidx.security.app.authenticator
เพื่อตรวจสอบสิทธิ์ หากผู้จัดตรวจสอบสิทธิ์หลังจากฝังกิจกรรมของคุณแล้ว คุณจะแสดงเนื้อหาจริงได้ หากไม่ คุณสามารถแจ้งให้ผู้ใช้ทราบว่าการดำเนินการดังกล่าวไม่ได้รับอนุญาตและบล็อกเนื้อหาได้
ใช้เมธอด ActivityEmbeddingController#isActivityEmbedded()
จากไลบรารี WindowManager ของ Jetpack เพื่อตรวจสอบว่าโฮสต์ฝังกิจกรรมของคุณหรือไม่ เช่น
Kotlin
fun isActivityEmbedded(activity: Activity): Boolean { return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity) }
Java
boolean isActivityEmbedded(Activity activity) { return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity); }
ข้อจำกัดด้านขนาดขั้นต่ำ
ระบบ Android จะใช้ความสูงและความกว้างขั้นต่ำที่ระบุไว้ในองค์ประกอบ app
manifest <layout>
กับกิจกรรมที่ฝัง หากแอปพลิเคชันไม่ได้ระบุความสูงและความกว้างขั้นต่ำ ระบบจะใช้ค่าเริ่มต้น (sw220dp
)
หากโฮสต์พยายามปรับขนาดคอนเทนเนอร์ที่ฝังให้เล็กกว่าขนาดขั้นต่ำ คอนเทนเนอร์ที่ฝังจะขยายขนาดให้เต็มขอบเขตของงาน
<activity-alias>
หากต้องการให้การฝังกิจกรรมที่เชื่อถือได้หรือไม่เชื่อถือได้ทํางานกับองค์ประกอบ <activity-alias>
คุณต้องใส่ android:knownActivityEmbeddingCerts
หรือ android:allowUntrustedActivityEmbedding
ลงในกิจกรรมเป้าหมายแทนที่จะเป็นอีเมลแทน นโยบายที่ยืนยันความปลอดภัยในเซิร์ฟเวอร์ระบบจะอิงตามการตั้งค่าสถานะที่กําหนดในเป้าหมาย ไม่ใช่อีเมลแทน
แอปพลิเคชันโฮสต์
แอปพลิเคชันโฮสต์ใช้การฝังกิจกรรมข้ามแอปในลักษณะเดียวกับที่ใช้การฝังกิจกรรมในแอปเดียว ออบเจ็กต์ SplitPairRule
และ SplitPairFilter
หรือ ActivityRule
และ ActivityFilter
จะระบุกิจกรรมที่ฝังไว้และการแยกหน้าต่างงาน กฎการแยกจะกำหนดแบบคงที่ใน XML หรือที่รันไทม์โดยใช้การเรียก Jetpack WindowManager API
หากแอปพลิเคชันโฮสต์พยายามฝังกิจกรรมที่ไม่ได้เลือกใช้การฝังข้ามแอป กิจกรรมดังกล่าวจะครอบครองขอบเขตงานทั้งหมด ด้วยเหตุนี้ แอปพลิเคชันโฮสต์จึงต้องทราบว่ากิจกรรมเป้าหมายอนุญาตให้ฝังข้ามแอปหรือไม่
หากกิจกรรมที่ฝังเริ่มกิจกรรมใหม่ในแท็บงานเดียวกัน และกิจกรรมใหม่ไม่ได้เลือกใช้การฝังข้ามแอป กิจกรรมดังกล่าวจะครอบครองขอบเขตแท็บงานทั้งหมดแทนการวางซ้อนกิจกรรมในคอนเทนเนอร์ที่ฝัง
แอปพลิเคชันโฮสต์สามารถฝังกิจกรรมของตัวเองได้โดยไม่มีข้อจำกัด ตราบใดที่กิจกรรมเปิดขึ้นในแท็บงานเดียวกัน
แยกตัวอย่าง
แยกจากหน้าต่างแบบเต็ม
ไม่ต้องมีการปรับโครงสร้าง คุณสามารถกําหนดค่าสําหรับการแยกแบบคงที่หรือขณะรันไทม์ แล้วเรียกใช้ Context#startActivity()
โดยไม่ต้องมีพารามิเตอร์เพิ่มเติม
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
แยกโดยค่าเริ่มต้น
เมื่อหน้า Landing Page ของแอปพลิเคชันออกแบบมาให้แบ่งออกเป็นคอนเทนเนอร์ 2 รายการบนหน้าจอขนาดใหญ่ ประสบการณ์ของผู้ใช้จะดีที่สุดเมื่อสร้างและแสดงกิจกรรมทั้ง 2 รายการพร้อมกัน อย่างไรก็ตาม เนื้อหาอาจไม่พร้อมใช้งานสําหรับคอนเทนเนอร์รองของการแยกจนกว่าผู้ใช้จะโต้ตอบกับกิจกรรมในคอนเทนเนอร์หลัก (เช่น ผู้ใช้เลือกรายการจากเมนูการนำทาง) กิจกรรมตัวยึดตําแหน่งสามารถแสดงแทนที่เนื้อหาได้จนกว่าเนื้อหาจะแสดงในคอนเทนเนอร์รองของการแยก (ดูส่วนตัวยึดตําแหน่ง)
หากต้องการสร้างการแยกที่มีตัวยึดตําแหน่ง ให้สร้างตัวยึดตําแหน่งแล้วเชื่อมโยงกับกิจกรรมหลัก โดยทําดังนี้
<SplitPlaceholderRule
window:placeholderActivityName=".PlaceholderActivity">
<ActivityFilter
window:activityName=".MainActivity"/>
</SplitPlaceholderRule>
การแยก Deep Link
เมื่อแอปได้รับ Intent กิจกรรมเป้าหมายจะแสดงเป็นส่วนรองของการแยกกิจกรรม เช่น คําขอแสดงหน้าจอรายละเอียดที่มีข้อมูลเกี่ยวกับรายการจากรายการ ในจอแสดงผลขนาดเล็ก รายละเอียดจะแสดงในหน้าต่างงานแบบเต็ม ส่วนในอุปกรณ์ขนาดใหญ่ รายละเอียดจะแสดงข้างรายการ
คำขอเปิดใช้งานควรกำหนดเส้นทางไปยังกิจกรรมหลัก และควรเปิดใช้งานกิจกรรมรายละเอียดเป้าหมายแบบแยก ระบบจะเลือกการแสดงผลที่เหมาะสมโดยอัตโนมัติ ไม่ว่าจะเป็นแบบซ้อนกันหรือแสดงคู่กัน โดยอิงตามความกว้างของการแสดงผลที่ใช้ได้
Kotlin
override fun onCreate(savedInstanceState Bundle?) { . . . RuleController.getInstance(this) .addRule(SplitPairRule.Builder(filterSet).build()) startActivity(Intent(this, DetailActivity::class.java)) }
Java
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { . . . RuleController.getInstance(this) .addRule(new SplitPairRule.Builder(filterSet).build()); startActivity(new Intent(this, DetailActivity.class)); }
ปลายทางของ Deep Link อาจเป็นกิจกรรมเดียวที่ผู้ใช้ควรเข้าถึงได้ในกองการนำทางด้านหลัง และคุณอาจไม่ต้องการปิดกิจกรรมรายละเอียดและเหลือไว้เฉพาะกิจกรรมหลัก
แต่คุณสามารถทํากิจกรรมทั้ง 2 อย่างให้เสร็จพร้อมกันได้โดยใช้แอตทริบิวต์ finishPrimaryWithSecondary
ดังนี้
<SplitPairRule
window:finishPrimaryWithSecondary="always">
<SplitPairFilter
window:primaryActivityName=".ListActivity"
window:secondaryActivityName=".DetailActivity"/>
</SplitPairRule>
ดูส่วนแอตทริบิวต์การกําหนดค่า
กิจกรรมหลายรายการในคอนเทนเนอร์ที่แยก
การซ้อนกิจกรรมหลายรายการในคอนเทนเนอร์แบบแยกช่วยให้ผู้ใช้เข้าถึงเนื้อหาเชิงลึกได้ ตัวอย่างเช่น เมื่อมีการแยกรายการเป็นรายละเอียด ผู้ใช้อาจต้องไปที่ส่วนรายละเอียดย่อย แต่ให้กิจกรรมหลักยังคงอยู่
Kotlin
class DetailActivity { . . . fun onOpenSubDetail() { startActivity(Intent(this, SubDetailActivity::class.java)) } }
Java
public class DetailActivity { . . . void onOpenSubDetail() { startActivity(new Intent(this, SubDetailActivity.class)); } }
กิจกรรมย่อยจะวางอยู่ด้านบนกิจกรรมแบบละเอียด ซึ่งจะบดบังกิจกรรมแบบละเอียด
จากนั้นผู้ใช้จะกลับไปที่ระดับรายละเอียดก่อนหน้าได้โดยไปยังส่วนต่างๆ ของกองดังนี้
การวางซ้อนกิจกรรมไว้บนกันคือลักษณะการทำงานเริ่มต้นเมื่อมีการเริ่มกิจกรรมจากกิจกรรมในคอนเทนเนอร์รองเดียวกัน กิจกรรมที่เปิดจากคอนเทนเนอร์หลักภายในการแยกที่ใช้งานอยู่จะสิ้นสุดลงในคอนเทนเนอร์รองที่ด้านบนของกองกิจกรรมด้วย
กิจกรรมในงานใหม่
เมื่อกิจกรรมในหน้าต่างงานแบบแยกเริ่มกิจกรรมในงานใหม่ งานใหม่จะแยกจากงานที่มีการแยกและแสดงเต็มหน้าต่าง หน้าจอล่าสุดจะแสดงงาน 2 รายการ ได้แก่ งานในหน้าจอแยกและงานใหม่
การเปลี่ยนกิจกรรม
กิจกรรมสามารถแทนที่ในกองคอนเทนเนอร์รองได้ เช่น เมื่อใช้กิจกรรมหลักสำหรับการนําทางระดับบนสุดและกิจกรรมรองเป็นปลายทางที่เลือก การเลือกแต่ละรายการจากการนําทางระดับบนสุดควรเริ่มกิจกรรมใหม่ในคอนเทนเนอร์รองและนํากิจกรรมหรือกิจกรรมที่เคยมีก่อนหน้านี้ออก
หากแอปไม่ทำกิจกรรมในคอนเทนเนอร์รองให้เสร็จสิ้นเมื่อตัวเลือกการไปยังส่วนต่างๆ เปลี่ยนแปลง การนำทางกลับอาจทำให้เกิดความสับสนเมื่อยุบการแยก (เมื่อพับอุปกรณ์) เช่น หากคุณมีเมนูในแผงหลักและหน้าจอ A และ B ซ้อนกันในแผงรอง เมื่อผู้ใช้พับโทรศัพท์ หน้าจอ B จะวางซ้อนบนหน้าจอ A และหน้าจอ A จะวางซ้อนบนเมนู เมื่อผู้ใช้กลับไปจาก B ตัวเลือก A จะปรากฏขึ้นแทนเมนู
ในกรณีเช่นนี้ คุณต้องนำหน้าจอ ก ออกจากกองซ้อนด้านหลัง
ลักษณะการทำงานเริ่มต้นเมื่อเปิดในคอนเทนเนอร์ใหม่ทางด้านข้างของส่วนแยกที่มีอยู่คือการวางคอนเทนเนอร์รองใหม่ไว้ที่ด้านบนและเก็บคอนเทนเนอร์เก่าไว้ในกองซ้อนด้านหลัง คุณสามารถกําหนดค่าการแยกเพื่อล้างคอนเทนเนอร์รองก่อนหน้าด้วย clearTop
และเปิดใช้งานกิจกรรมใหม่ได้ตามปกติ
<SplitPairRule
window:clearTop="true">
<SplitPairFilter
window:primaryActivityName=".Menu"
window:secondaryActivityName=".ScreenA"/>
<SplitPairFilter
window:primaryActivityName=".Menu"
window:secondaryActivityName=".ScreenB"/>
</SplitPairRule>
Kotlin
class MenuActivity { . . . fun onMenuItemSelected(selectedMenuItem: Int) { startActivity(Intent(this, classForItem(selectedMenuItem))) } }
Java
public class MenuActivity { . . . void onMenuItemSelected(int selectedMenuItem) { startActivity(new Intent(this, classForItem(selectedMenuItem))); } }
หรือจะใช้กิจกรรมรองเดียวกันก็ได้ และจากกิจกรรมหลัก (เมนู) ให้ส่ง Intent ใหม่ที่แก้ไขได้ในอินสแตนซ์เดียวกัน แต่ทริกเกอร์สถานะหรือการอัปเดต UI ในคอนเทนเนอร์รอง
การแยกหลายรายการ
แอปสามารถให้การนําทางแบบเจาะลึกหลายระดับได้โดยการเปิดใช้งานกิจกรรมเพิ่มเติมทางด้านข้าง
เมื่อกิจกรรมในคอนเทนเนอร์รองเปิดกิจกรรมใหม่ขึ้นด้านข้าง ระบบจะสร้างการแยกใหม่เหนือการแยกที่มีอยู่
กองซ้อนที่ย้อนกลับจะมีกิจกรรมทั้งหมดที่เปิดไว้ก่อนหน้านี้ เพื่อให้ผู้ใช้ไปยังการแยกกลุ่ม A/B ได้หลังจากทํา C เสร็จแล้ว
หากต้องการแยกใหม่ ให้เปิดกิจกรรมใหม่ไว้ด้านข้างจากคอนเทนเนอร์รองที่มีอยู่ ประกาศการกําหนดค่าสําหรับทั้งกลุ่ม A/B และ B/C และเปิดใช้งานกิจกรรม C ตามปกติจาก B
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
<SplitPairFilter
window:primaryActivityName=".B"
window:secondaryActivityName=".C"/>
</SplitPairRule>
Kotlin
class B { . . . fun onOpenC() { startActivity(Intent(this, C::class.java)) } }
Java
public class B { . . . void onOpenC() { startActivity(new Intent(this, C.class)); } }
ตอบสนองต่อการเปลี่ยนแปลงสถานะการแยก
กิจกรรมต่างๆ ในแอปอาจมีองค์ประกอบ UI ที่ทํางานแบบเดียวกัน เช่น การควบคุมที่เปิดหน้าต่างที่มีการตั้งค่าบัญชี
หากกิจกรรม 2 รายการที่มีองค์ประกอบ UI เหมือนกันอยู่ในส่วนที่แยกกัน การแสดงองค์ประกอบในทั้ง 2 กิจกรรมจะซ้ำซ้อนและอาจทำให้เกิดความสับสน
หากต้องการทราบว่ากิจกรรมอยู่ในสถานะแยกหรือไม่ ให้ตรวจสอบโฟลว์ SplitController.splitInfoList
หรือลงทะเบียนโปรแกรมฟังกับ SplitControllerCallbackAdapter
เพื่อดูการเปลี่ยนแปลงในสถานะแยก จากนั้นปรับ UI ให้เหมาะสม ดังนี้
Kotlin
val layout = layoutInflater.inflate(R.layout.activity_main, null) val view = layout.findViewById<View>(R.id.infoButton) lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { splitController.splitInfoList(this@SplitDeviceActivity) // The activity instance. .collect { list -> view.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE } } }
Java
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { . . . new SplitControllerCallbackAdapter(SplitController.getInstance(this)) .addSplitListener( this, Runnable::run, splitInfoList -> { View layout = getLayoutInflater().inflate(R.layout.activity_main, null); layout.findViewById(R.id.infoButton).setVisibility( splitInfoList.isEmpty() ? View.VISIBLE : View.GONE); }); }
โคโริวทีนสามารถเปิดใช้งานได้ในทุกสถานะวงจร แต่โดยทั่วไปจะเปิดขึ้นในสถานะ STARTED
เพื่อประหยัดทรัพยากร (ดูข้อมูลเพิ่มเติมที่ใช้โคโริวทีน Kotlin กับคอมโพเนนต์ที่รับรู้วงจร)
คุณสามารถเรียกใช้การเรียกกลับได้ในสถานะวงจรใดก็ได้ รวมถึงเมื่อกิจกรรมหยุดลง โดยปกติแล้ว ผู้ฟังควรจดทะเบียนใน onStart()
และไม่ได้จดทะเบียนใน onStop()
โมดัลแบบเต็มหน้าต่าง
กิจกรรมบางอย่างจะบล็อกไม่ให้ผู้ใช้โต้ตอบกับแอปพลิเคชันจนกว่าจะมีการดําเนินการบางอย่างที่ระบุ เช่น กิจกรรมในหน้าจอการเข้าสู่ระบบ หน้าจอรับทราบนโยบาย หรือข้อความแสดงข้อผิดพลาด กิจกรรมแบบโมดัลควรป้องกันไม่ให้ปรากฏในการแยก
คุณสามารถบังคับให้กิจกรรมแสดงเต็มหน้าต่างงานได้เสมอโดยใช้การกำหนดค่าการขยาย ดังนี้
<ActivityRule
window:alwaysExpand="true">
<ActivityFilter
window:activityName=".FullWidthActivity"/>
</ActivityRule>
กิจกรรมเสร็จสิ้น
ผู้ใช้สามารถทำงานให้เสร็จสมบูรณ์ในแต่ละด้านของการแยกหน้าจอโดยการปัดจากขอบของจอแสดงผล ดังนี้
หากตั้งค่าอุปกรณ์ให้ใช้ปุ่มย้อนกลับแทนการไปยังส่วนต่างๆ ด้วยท่าทางสัมผัส ระบบจะส่งอินพุตไปยังกิจกรรมที่มีโฟกัส ซึ่งเป็นกิจกรรมที่มีการแตะหรือเปิดใช้งานล่าสุด
ผลที่การสิ้นสุดกิจกรรมทั้งหมดในคอนเทนเนอร์หนึ่งมีต่อคอนเทนเนอร์อีกคอนเทนเนอร์หนึ่งจะขึ้นอยู่กับการกําหนดค่าการแยก
แอตทริบิวต์การกําหนดค่า
คุณสามารถระบุแอตทริบิวต์กฎคู่แยกเพื่อกําหนดค่าผลที่การสิ้นสุดกิจกรรมทั้งหมดในฝั่งหนึ่งของการแยกมีผลต่อกิจกรรมในฝั่งอีกฝั่งของการแยก แอตทริบิวต์ดังกล่าวมีดังนี้
window:finishPrimaryWithSecondary
— ผลกระทบที่การทํากิจกรรมทั้งหมดในคอนเทนเนอร์รองต่อกิจกรรมในคอนเทนเนอร์หลักwindow:finishSecondaryWithPrimary
— ผลกระทบที่การทํากิจกรรมทั้งหมดในคอนเทนเนอร์หลักมีต่อกิจกรรมในคอนเทนเนอร์รอง
ค่าที่เป็นไปได้ของแอตทริบิวต์ ได้แก่
always
— ทำงานในคอนเทนเนอร์ที่เชื่อมโยงให้เสร็จสิ้นเสมอnever
— ไม่เคยทำกิจกรรมในคอนเทนเนอร์ที่เกี่ยวข้องให้เสร็จสิ้นadjacent
— จบกิจกรรมในคอนเทนเนอร์ที่เชื่อมโยงเมื่อคอนเทนเนอร์ 2 รายการแสดงอยู่ติดกัน แต่จะไม่จบเมื่อคอนเทนเนอร์ 2 รายการซ้อนกัน
เช่น
<SplitPairRule
<!-- Do not finish primary container activities when all secondary container activities finish. -->
window:finishPrimaryWithSecondary="never"
<!-- Finish secondary container activities when all primary container activities finish. -->
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
การกำหนดค่าเริ่มต้น
เมื่อกิจกรรมทั้งหมดในคอนเทนเนอร์เดียวของการแยกเสร็จสิ้น คอนเทนเนอร์ที่เหลือจะครอบครองทั้งหน้าต่าง
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
ทำกิจกรรมให้เสร็จด้วยกัน
ดำเนินการในคอนเทนเนอร์หลักให้เสร็จโดยอัตโนมัติเมื่อกิจกรรมทั้งหมดในคอนเทนเนอร์รองเสร็จสิ้นแล้ว โดยทำดังนี้
<SplitPairRule
window:finishPrimaryWithSecondary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
ดำเนินการในคอนเทนเนอร์รองให้เสร็จโดยอัตโนมัติเมื่อกิจกรรมทั้งหมดในคอนเทนเนอร์หลักเสร็จสิ้นแล้ว โดยทำดังนี้
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
สิ้นสุดกิจกรรมพร้อมกันเมื่อกิจกรรมทั้งหมดในคอนเทนเนอร์หลักหรือคอนเทนเนอร์รองสิ้นสุด
<SplitPairRule
window:finishPrimaryWithSecondary="always"
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
ทํากิจกรรมหลายอย่างในคอนเทนเนอร์ให้เสร็จ
หากมีกิจกรรมหลายรายการซ้อนกันในคอนเทนเนอร์แบบแยก การทำกิจกรรมที่ด้านล่างของกองให้เสร็จสมบูรณ์จะไม่ทำให้กิจกรรมที่ด้านบนเสร็จสมบูรณ์โดยอัตโนมัติ
ตัวอย่างเช่น หากมีกิจกรรม 2 รายการอยู่ในคอนเทนเนอร์รอง โดย C อยู่ด้านบนของ B
และการกําหนดค่าของการแยกจะกําหนดโดยการกำหนดค่าของกิจกรรม A และ B
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
การสิ้นสุดกิจกรรมยอดนิยมจะเก็บการแยกไว้
การทำกิจกรรมด้านล่าง (รูท) ของคอนเทนเนอร์รองจนเสร็จสมบูรณ์จะไม่นำกิจกรรมที่อยู่ด้านบนออก ดังนั้นจึงยังคงการแยกไว้
ระบบจะดำเนินการตามกฎเพิ่มเติมสำหรับการสิ้นสุดกิจกรรมร่วมกัน เช่น การสิ้นสุดกิจกรรมรองด้วยกิจกรรมหลัก
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
และเมื่อมีการกำหนดค่าการแยกให้จบทั้งช่องหลักและรองพร้อมกัน
<SplitPairRule
window:finishPrimaryWithSecondary="always"
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
เปลี่ยนพร็อพเพอร์ตี้การแยกขณะรันไทม์
คุณจะเปลี่ยนพร็อพเพอร์ตี้ของการแยกที่ใช้งานอยู่และแสดงอยู่ไม่ได้ การเปลี่ยนแปลงกฎการแยกจะส่งผลต่อการเริ่มกิจกรรมและคอนเทนเนอร์ใหม่ แต่จะไม่ส่งผลต่อการแยกที่มีอยู่และที่ใช้งานอยู่
หากต้องการเปลี่ยนพร็อพเพอร์ตี้ของการแยกที่ใช้งานอยู่ ให้ทํากิจกรรมในแถบข้างให้เสร็จแล้วเปิดแถบข้างอีกครั้งด้วยการกำหนดค่าใหม่
พร็อพเพอร์ตี้การแยกแบบไดนามิก
Android 15 (API ระดับ 35) ขึ้นไปที่รองรับ Jetpack WindowManager 1.4 ขึ้นไปมีฟีเจอร์แบบไดนามิกที่ช่วยให้สามารถกำหนดค่าการแยกการฝังกิจกรรมได้ ซึ่งรวมถึงฟีเจอร์ต่อไปนี้
- การขยายแผง: ตัวแบ่งแบบอินเทอร์แอกทีฟที่ลากได้ช่วยให้ผู้ใช้ปรับขนาดแผงในการแสดงผลแบบแยกได้
- การปักหมุดกองกิจกรรม: ผู้ใช้สามารถปักหมุดเนื้อหาในคอนเทนเนอร์หนึ่งและแยกการไปยังส่วนต่างๆ ในคอนเทนเนอร์นั้นจากการไปยังส่วนต่างๆ ในคอนเทนเนอร์อื่น
- การทำให้หน้าจอสลัวเมื่อแสดงกล่องโต้ตอบแบบเต็มหน้าจอ: เมื่อแสดงกล่องโต้ตอบ แอปจะระบุได้ว่าจะสลัวทั้งหน้าต่างงานหรือเฉพาะคอนเทนเนอร์ที่เปิดกล่องโต้ตอบ
การขยายแผง
การขยายแผงช่วยให้ผู้ใช้ปรับพื้นที่หน้าจอที่จัดสรรให้กับกิจกรรม 2 รายการในเลย์เอาต์แบบ 2 แผงได้
หากต้องการปรับแต่งลักษณะที่ปรากฏของตัวแบ่งหน้าต่างและตั้งค่าช่วงการลากของตัวแบ่ง ให้ทำดังนี้
สร้างอินสแตนซ์ของ
DividerAttributes
ปรับแต่งแอตทริบิวต์ตัวแบ่ง ดังนี้
color
: สีของตัวคั่นแผงแบบลากได้widthDp
: ความกว้างของตัวคั่นแผงแบบลากได้ ตั้งค่าเป็นWIDTH_SYSTEM_DEFAULT
เพื่อให้ระบบกำหนดความกว้างของส่วนแบ่งช่วงการลาก: เปอร์เซ็นต์ขั้นต่ำของหน้าจอที่แผงแต่ละแผงครอบครองได้ อยู่ในช่วง 0.33 ถึง 0.66 ตั้งค่าเป็น
DRAG_RANGE_SYSTEM_DEFAULT
เพื่อให้ระบบกำหนดช่วงการลาก
Kotlin
val splitAttributesBuilder: SplitAttributes.Builder = SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) if (WindowSdkExtensions.getInstance().extensionVersion >= 6) { splitAttributesBuilder.setDividerAttributes( DividerAttributes.DraggableDividerAttributes.Builder() .setColor(getColor(context, R.color.divider_color)) .setWidthDp(4) .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT) .build() ) } val splitAttributes: SplitAttributes = splitAttributesBuilder.build()
Java
SplitAttributes.Builder splitAttributesBuilder = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT); if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) { splitAttributesBuilder.setDividerAttributes( new DividerAttributes.DraggableDividerAttributes.Builder() .setColor(ContextCompat.getColor(context, R.color.divider_color)) .setWidthDp(4) .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT) .build() ); } SplitAttributes splitAttributes = splitAttributesBuilder.build();
การปักหมุดสแต็กกิจกรรม
การปักหมุดกองงานช่วยให้ผู้ใช้ปักหมุดหน้าต่างที่แยกออกเป็น 1 หน้าต่างได้เพื่อให้กิจกรรมคงอยู่เหมือนเดิมขณะที่ผู้ใช้ไปยังส่วนต่างๆ ในหน้าต่างอีกบาน การปักหมุดกองงานช่วยให้คุณทำงานหลายอย่างพร้อมกันได้ดีขึ้น
หากต้องการเปิดใช้การปักหมุดกองงานกิจกรรมในแอป ให้ทำดังนี้
เพิ่มปุ่มลงในไฟล์เลย์เอาต์ของกิจกรรมที่ต้องการปักหมุด เช่น กิจกรรมแบบละเอียดของเลย์เอาต์รายการแบบละเอียด
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/detailActivity" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white" tools:context=".DetailActivity"> <TextView android:id="@+id/textViewItemDetail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="36sp" android:textColor="@color/obsidian" app:layout_constraintBottom_toTopOf="@id/pinButton" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.appcompat.widget.AppCompatButton android:id="@+id/pinButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/pin_this_activity" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/textViewItemDetail"/> </androidx.constraintlayout.widget.ConstraintLayout>
ในเมธอด
onCreate()
ของกิจกรรม ให้ตั้งค่า Listener ของ onclick ในปุ่ม ดังนี้Kotlin
pinButton = findViewById(R.id.pinButton) pinButton.setOnClickListener { val splitAttributes: SplitAttributes = SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.66f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build() val pinSplitRule = SplitPinRule.Builder() .setSticky(true) .setDefaultSplitAttributes(splitAttributes) .build() SplitController.getInstance(applicationContext).pinTopActivityStack(taskId, pinSplitRule) }
Java
Button pinButton = findViewById(R.id.pinButton); pinButton.setOnClickListener( (view) => { SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.66f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build(); SplitPinRule pinSplitRule = new SplitPinRule.Builder() .setSticky(true) .setDefaultSplitAttributes(splitAttributes) .build(); SplitController.getInstance(getApplicationContext()).pinTopActivityStack(getTaskId(), pinSplitRule); });
หรี่แสงกล่องโต้ตอบแบบเต็มหน้าจอ
โดยปกติแล้ว กิจกรรมจะปรับการแสดงผลให้สลัวลงเพื่อดึงดูดความสนใจไปที่กล่องโต้ตอบ ในการฝังกิจกรรม แผงทั้ง 2 แผงของการแสดงผลแบบแยกหน้าจอควรสลัว ไม่ใช่แค่แผงที่มีกิจกรรมที่เปิดกล่องโต้ตอบ เพื่อให้ได้รับประสบการณ์การใช้งาน UI แบบรวม
เมื่อใช้ WindowManager 1.4 ขึ้นไป หน้าต่างแอปทั้งหมดจะสลัวลงโดยค่าเริ่มต้นเมื่อกล่องโต้ตอบเปิดขึ้น (ดู EmbeddingConfiguration.DimAreaBehavior.ON_TASK
)
หากต้องการหรี่เฉพาะคอนเทนเนอร์ของกิจกรรมที่เปิดกล่องโต้ตอบ ให้ใช้
EmbeddingConfiguration.DimAreaBehavior.ON_ACTIVITY_STACK
ดึงข้อมูลกิจกรรมจากหน้าต่างที่แยกออกเป็นแบบเต็มหน้าจอ
สร้างการกําหนดค่าใหม่ที่แสดงหน้าต่างกิจกรรมด้านข้างแบบเต็ม จากนั้นเปิดกิจกรรมอีกครั้งโดยมี Intent ที่แก้ไขเป็นอินสแตนซ์เดียวกัน
ตรวจสอบการรองรับการแยกขณะรันไทม์
การฝังกิจกรรมใช้ได้ใน Android 12L (API ระดับ 32) ขึ้นไป แต่จะใช้ได้ในอุปกรณ์บางรุ่นที่ใช้แพลตฟอร์มเวอร์ชันเก่ากว่าด้วย หากต้องการตรวจสอบความพร้อมใช้งานของฟีเจอร์ขณะรันไทม์ ให้ใช้พร็อพเพอร์ตี้ SplitController.splitSupportStatus
หรือเมธอด SplitController.getSplitSupportStatus()
ดังนี้
Kotlin
if (SplitController.getInstance(this).splitSupportStatus == SplitController.SplitSupportStatus.SPLIT_AVAILABLE) { // Device supports split activity features. }
Java
if (SplitController.getInstance(this).getSplitSupportStatus() == SplitController.SplitSupportStatus.SPLIT_AVAILABLE) { // Device supports split activity features. }
หากระบบไม่รองรับการแยก ระบบจะเปิดกิจกรรมที่ด้านบนของกองกิจกรรม (ตามรูปแบบการฝังที่ไม่ใช่กิจกรรม)
ป้องกันการลบล้างระบบ
ผู้ผลิตอุปกรณ์ Android (ผู้ผลิตอุปกรณ์ดั้งเดิมหรือ OEM) สามารถใช้การฝังกิจกรรมเป็นฟังก์ชันของระบบอุปกรณ์ได้ ระบบจะระบุกฎการแยกสำหรับแอปหลายกิจกรรม ซึ่งจะลบล้างลักษณะการทำงานแบบหลายหน้าต่างของแอป การลบล้างของระบบจะบังคับให้แอปหลายกิจกรรมเข้าสู่โหมดการฝังกิจกรรมที่ระบบกําหนด
การฝังกิจกรรมของระบบสามารถปรับปรุงการแสดงของแอปผ่านเลย์เอาต์หลายแผง เช่น list-detail โดยไม่ต้องเปลี่ยนแปลงแอป อย่างไรก็ตาม การฝังกิจกรรมของระบบอาจทำให้เกิดเลย์เอาต์แอปที่ไม่ถูกต้อง ข้อบกพร่อง หรือขัดแย้งกับการฝังกิจกรรมที่แอปนำมาใช้
แอปสามารถป้องกันหรืออนุญาตการฝังกิจกรรมของระบบได้โดยการตั้งค่าพร็อพเพอร์ตี้ในไฟล์ Manifest ของแอป เช่น
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<property
android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"
android:value="true|false" />
</application>
</manifest>
ชื่อพร็อพเพอร์ตี้จะกำหนดไว้ในออบเจ็กต์ Jetpack WindowManager WindowProperties
ตั้งค่าเป็น false
หากแอปของคุณใช้การฝังกิจกรรม หรือหากต้องการป้องกันไม่ให้ระบบใช้กฎการฝังกิจกรรมกับแอปของคุณ ให้ตั้งค่าเป็น true
เพื่ออนุญาตให้ระบบใช้การฝังกิจกรรมที่ระบบกําหนดกับแอปของคุณ
ข้อจำกัด ข้อจํากัด และข้อควรทราบ
- เฉพาะแอปโฮสต์ของงานซึ่งระบุว่าเป็นเจ้าของกิจกรรมรูทในงานนี้เท่านั้นที่จะจัดระเบียบและฝังกิจกรรมอื่นๆ ในงานนี้ได้ หากกิจกรรมที่รองรับการฝังและการแยกทำงานในแท็บงานที่เป็นของแอปพลิเคชันอื่น การฝังและการแยกจะไม่ทำงานกับกิจกรรมเหล่านั้น
- กิจกรรมจะจัดระเบียบได้ภายในงานเดียวเท่านั้น การเริ่มกิจกรรมในงานใหม่จะวางกิจกรรมนั้นในหน้าต่างใหม่ที่ขยายออกเสมอ นอกเหนือการแยกที่มีอยู่
- เฉพาะกิจกรรมในกระบวนการเดียวกันเท่านั้นที่จะจัดระเบียบและแยกได้ การเรียกกลับ
SplitInfo
จะรายงานเฉพาะกิจกรรมที่อยู่ในกระบวนการเดียวกันเท่านั้น เนื่องจากไม่มีวิธีที่จะทราบเกี่ยวกับกิจกรรมในกระบวนการอื่น - กฎกิจกรรมคู่หรือกฎกิจกรรมเดี่ยวแต่ละรายการจะมีผลกับการเปิดใช้งานกิจกรรมที่เกิดขึ้นหลังจากลงทะเบียนกฎเท่านั้น ขณะนี้ยังไม่มีวิธีอัปเดตการแยกที่มีอยู่หรือพร็อพเพอร์ตี้ภาพ
- การกําหนดค่าตัวกรองคู่ที่แยกต้องตรงกับ Intent ที่ใช้เมื่อเปิดใช้งานกิจกรรมโดยสมบูรณ์ การจับคู่จะเกิดขึ้นเมื่อเริ่มกิจกรรมใหม่จากกระบวนการแอปพลิเคชัน จึงอาจไม่ทราบชื่อคอมโพเนนต์ที่แก้ไขในภายหลังในกระบวนการของระบบเมื่อใช้ Intent ที่ไม่ชัด หากไม่ทราบชื่อคอมโพเนนต์ ณ เวลาที่เปิดใช้งาน คุณสามารถใช้ไวลด์การ์ดแทน ("*/*") และทำการกรองตามการดำเนินการตามเจตนา
- ปัจจุบันยังไม่มีวิธีย้ายกิจกรรมระหว่างคอนเทนเนอร์ หรือย้ายกิจกรรมเข้าและออกจากการแยกหลังจากที่สร้างแล้ว ระบบจะสร้างการแยกโดยไลบรารี WindowManager เฉพาะเมื่อมีการเริ่มกิจกรรมใหม่ที่มีกฎที่ตรงกัน และระบบจะทำลายการแยกเมื่อกิจกรรมสุดท้ายในคอนเทนเนอร์แยกเสร็จสิ้น
- กิจกรรมจะเปิดอีกครั้งได้เมื่อการกําหนดค่ามีการเปลี่ยนแปลง ดังนั้นเมื่อสร้างหรือนําการแยกออกและขอบเขตของกิจกรรมมีการเปลี่ยนแปลง กิจกรรมจะทําการทำลายอินสแตนซ์ก่อนหน้าอย่างสมบูรณ์และสร้างอินสแตนซ์ใหม่ได้ ดังนั้น นักพัฒนาแอปควรระมัดระวังเรื่องต่างๆ เช่น การเริ่มกิจกรรมใหม่จากการเรียกกลับของวงจร
- อุปกรณ์ต้องมีอินเทอร์เฟซส่วนขยายหน้าต่างเพื่อรองรับการฝังกิจกรรม อุปกรณ์หน้าจอขนาดใหญ่เกือบทั้งหมดที่ใช้ Android 12L (API ระดับ 32) ขึ้นไปจะมีอินเทอร์เฟซนี้ อย่างไรก็ตาม อุปกรณ์หน้าจอขนาดใหญ่บางรุ่นที่ไม่สามารถเรียกใช้กิจกรรมหลายรายการจะไม่มีอินเทอร์เฟซส่วนขยายหน้าต่าง หากอุปกรณ์หน้าจอขนาดใหญ่ไม่รองรับโหมดหลายหน้าต่าง อุปกรณ์อาจไม่รองรับการฝังกิจกรรม
แหล่งข้อมูลเพิ่มเติม
- Codelabs:
- เส้นทางการเรียนรู้ — การฝังกิจกรรม
- แอปตัวอย่าง — activity-embedding