อุปกรณ์แบบพับได้มอบประสบการณ์การรับชมที่ไม่เหมือนใคร โหมดจอแสดงผลด้านหลังและโหมดหน้าจอคู่ช่วยให้คุณสร้างฟีเจอร์การแสดงผลพิเศษสำหรับอุปกรณ์แบบพับได้ เช่น การแสดงตัวอย่างเซลฟีจากกล้องหลังและการแสดงผลที่แตกต่างกันพร้อมกันบนหน้าจอด้านในและด้านนอก
โหมดจอแสดงผลด้านหลัง
โดยปกติแล้ว เมื่อกางอุปกรณ์แบบพับได้ออก จะมีเพียงหน้าจอด้านในเท่านั้นที่ทำงาน โหมดจอแสดงผลด้านหลังช่วยให้คุณย้ายกิจกรรมไปยังหน้าจอด้านนอกของอุปกรณ์พับได้ ซึ่งมักหันออกจากผู้ใช้ขณะที่กางอุปกรณ์ออก จอแสดงผลด้านในจะปิดโดยอัตโนมัติ
แอปพลิเคชันใหม่คือการแสดงตัวอย่างกล้องบนหน้าจอด้านนอก เพื่อให้ผู้ใช้ถ่ายเซลฟีด้วยกล้องหลังได้ ซึ่งโดยปกติแล้วจะให้ประสิทธิภาพการถ่ายภาพที่ดีกว่ากล้องหน้ามาก
หากต้องการเปิดใช้งานโหมดจอแสดงผลด้านหลัง ผู้ใช้จะตอบสนองต่อกล่องโต้ตอบเพื่ออนุญาตให้แอป สลับหน้าจอ เช่น

ระบบจะสร้างกล่องโต้ตอบ ดังนั้นคุณจึงไม่จำเป็นต้องพัฒนาอะไรอีก กล่องโต้ตอบต่างๆ จะปรากฏขึ้นโดยขึ้นอยู่กับสถานะของอุปกรณ์ เช่น ระบบจะบอกให้ผู้ใช้กางอุปกรณ์ออกหากอุปกรณ์ปิดอยู่ คุณไม่สามารถปรับแต่ง และอาจแตกต่างกันไปตามอุปกรณ์จาก OEM ต่างๆ
คุณสามารถลองใช้โหมดจอแสดงผลด้านหลังกับแอปกล้อง Pixel Fold ดูได้ ดูตัวอย่างการใช้งานได้ในโค้ดแล็บเพิ่มประสิทธิภาพแอปกล้องในอุปกรณ์แบบพับได้ด้วย Jetpack WindowManager
โหมดหน้าจอคู่
โหมด 2 หน้าจอช่วยให้คุณแสดงเนื้อหาบนจอแสดงผลทั้ง 2 จอของอุปกรณ์พับได้ ในเวลาเดียวกัน โหมดหน้าจอคู่พร้อมใช้งานใน Pixel Fold ที่ใช้ Android 14 (API ระดับ 34) ขึ้นไป
ตัวอย่างกรณีการใช้งานคือโปรแกรมแปลภาษาแบบ Dual Screen

เปิดใช้โหมดต่างๆ แบบเป็นโปรแกรม
คุณสามารถเข้าถึงโหมดจอแสดงผลด้านหลังและโหมดหน้าจอคู่ผ่าน Jetpack WindowManager API เริ่มจากไลบรารีเวอร์ชัน 1.2.0-beta03
เพิ่มทรัพยากร Dependency ของ WindowManager ลงในไฟล์โมดูล build.gradle
ของแอปดังนี้
Groovy
dependencies { implementation "androidx.window:window:1.2.0-beta03" }
Kotlin
dependencies { implementation("androidx.window:window:1.2.0-beta03") }
จุดแรกเข้าคือ WindowAreaController
ซึ่งให้ข้อมูลและลักษณะการทํางานที่เกี่ยวข้องกับการย้ายหน้าต่างระหว่างจอแสดงผลหรือระหว่างพื้นที่แสดงผลในอุปกรณ์ WindowAreaController
ให้คุณค้นหารายการของ
ออบเจ็กต์ WindowAreaInfo
ที่พร้อมใช้งาน
ใช้ WindowAreaInfo
เพื่อเข้าถึง WindowAreaSession
ซึ่งเป็นอินเทอร์เฟซที่
แสดงคุณลักษณะพื้นที่หน้าต่างที่ใช้งานอยู่ ใช้ WindowAreaSession
เพื่อระบุความพร้อมจำหน่ายสินค้าของ WindowAreaCapability
ที่เฉพาะเจาะจง
ความสามารถแต่ละอย่างเกี่ยวข้องกับ WindowAreaCapability.Operation
ที่เฉพาะเจาะจง
ในเวอร์ชัน 1.2.0-beta03 นั้น Jetpack WindowManager รองรับการดำเนินการ 2 ประเภทดังนี้
WindowAreaCapability.Operation.OPERATION_PRESENT_ON_AREA
ซึ่งก็คือ ใช้เพื่อเริ่มโหมดหน้าจอคู่WindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA
ซึ่งใช้เพื่อเริ่มโหมดการแสดงผลด้านหลัง
ต่อไปนี้คือตัวอย่างวิธีประกาศตัวแปรสำหรับโหมดจอแสดงผลด้านหลังและโหมดหน้าจอคู่ในกิจกรรมหลักของแอป
Kotlin
private lateinit var windowAreaController: WindowAreaController private lateinit var displayExecutor: Executor private var windowAreaSession: WindowAreaSession? = null private var windowAreaInfo: WindowAreaInfo? = null private var capabilityStatus: WindowAreaCapability.Status = WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED private val dualScreenOperation = WindowAreaCapability.Operation.OPERATION_PRESENT_ON_AREA private val rearDisplayOperation = WindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA
Java
private WindowAreaControllerCallbackAdapter windowAreaController = null; private Executor displayExecutor = null; private WindowAreaSessionPresenter windowAreaSession = null; private WindowAreaInfo windowAreaInfo = null; private WindowAreaCapability.Status capabilityStatus = WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED; private WindowAreaCapability.Operation dualScreenOperation = WindowAreaCapability.Operation.OPERATION_PRESENT_ON_AREA; private WindowAreaCapability.Operation rearDisplayOperation = WindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA;
วิธีเริ่มต้นตัวแปรในเมธอด onCreate()
ของกิจกรรมมีดังนี้
Kotlin
displayExecutor = ContextCompat.getMainExecutor(this) windowAreaController = WindowAreaController.getOrCreate() lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { windowAreaController.windowAreaInfos .map { info -> info.firstOrNull { it.type == WindowAreaInfo.Type.TYPE_REAR_FACING } } .onEach { info -> windowAreaInfo = info } .map { it?.getCapability(operation)?.status ?: WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED } .distinctUntilChanged() .collect { capabilityStatus = it } } }
Java
displayExecutor = ContextCompat.getMainExecutor(this); windowAreaController = new WindowAreaControllerCallbackAdapter(WindowAreaController.getOrCreate()); windowAreaController.addWindowAreaInfoListListener(displayExecutor, this); windowAreaController.addWindowAreaInfoListListener(displayExecutor, windowAreaInfos -> { for(WindowAreaInfo newInfo : windowAreaInfos){ if(newInfo.getType().equals(WindowAreaInfo.Type.TYPE_REAR_FACING)){ windowAreaInfo = newInfo; capabilityStatus = newInfo.getCapability(presentOperation).getStatus(); break; } } });
ก่อนเริ่มต้นการดำเนินการ ให้ตรวจสอบความพร้อมใช้งานของ ความสามารถ:
Kotlin
when (capabilityStatus) { WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED -> { // The selected display mode is not supported on this device. } WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNAVAILABLE -> { // The selected display mode is not available. } WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE -> { // The selected display mode is available and can be enabled. } WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE -> { // The selected display mode is already active. } else -> { // The selected display mode status is unknown. } }
Java
if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED)) { // The selected display mode is not supported on this device. } else if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNAVAILABLE)) { // The selected display mode is not available. } else if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE)) { // The selected display mode is available and can be enabled. } else if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE)) { // The selected display mode is already active. } else { // The selected display mode status is unknown. }
โหมด 2 หน้าจอ
ตัวอย่างต่อไปนี้ปิดเซสชันหากมีการใช้งานความสามารถอยู่แล้ว หรือ
หรือไม่เช่นนั้น จะเรียกฟังก์ชัน presentContentOnWindowArea()
ดังนี้
Kotlin
fun toggleDualScreenMode() { if (windowAreaSession != null) { windowAreaSession?.close() } else { windowAreaInfo?.token?.let { token -> windowAreaController.presentContentOnWindowArea( token = token, activity = this, executor = displayExecutor, windowAreaPresentationSessionCallback = this ) } } }
Java
private void toggleDualScreenMode() { if(windowAreaSession != null) { windowAreaSession.close(); } else { Binder token = windowAreaInfo.getToken(); windowAreaController.presentContentOnWindowArea( token, this, displayExecutor, this); } }
โปรดสังเกตการใช้กิจกรรมหลักของแอปเป็นอาร์กิวเมนต์ WindowAreaPresentationSessionCallback
API ใช้วิธีการ Listener กล่าวคือเมื่อคุณส่งคำขอเพื่อนำเสนอเนื้อหา
กับหน้าจออีกรุ่นหนึ่งของอุปกรณ์พับได้ คุณจะเริ่มต้นเซสชันที่ส่งคืน
ผ่านเมธอด onSessionStarted()
ของผู้ฟัง เมื่อปิดเซสชันแล้ว คุณจะได้รับอีเมลยืนยันในวิธีการ onSessionEnded()
ในการสร้าง Listener ให้ใช้ WindowAreaPresentationSessionCallback
อินเทอร์เฟซ:
Kotlin
class MainActivity : AppCompatActivity(), windowAreaPresentationSessionCallback
Java
public class MainActivity extends AppCompatActivity implements WindowAreaPresentationSessionCallback
Listener ต้องการใช้ onSessionStarted()
, onSessionEnded(),
และ onContainerVisibilityChanged()
เมธอด Callback จะแจ้งเตือน
สถานะเซสชันของคุณ และทำให้สามารถอัปเดตแอปได้ตามความเหมาะสม
การเรียกกลับ onSessionStarted()
ได้รับ WindowAreaSessionPresenter
เป็น
อาร์กิวเมนต์ โดยอาร์กิวเมนต์คือคอนเทนเนอร์ที่ช่วยให้คุณเข้าถึงพื้นที่หน้าต่างและแสดงเนื้อหาได้ ระบบอาจปิดงานนำเสนอโดยอัตโนมัติ
เมื่อผู้ใช้ออกจากหน้าต่างแอปพลิเคชันหลัก หรือสามารถนำเสนองานได้
ปิดโดยโทรหา WindowAreaSessionPresenter#close()
สําหรับการเรียกกลับอื่นๆ เพื่อความสะดวก ให้ตรวจสอบข้อผิดพลาดในเนื้อหาของฟังก์ชันและบันทึกสถานะ
Kotlin
override fun onSessionStarted(session: WindowAreaSessionPresenter) { windowAreaSession = session val view = TextView(session.context) view.text = "Hello world!" session.setContentView(view) } override fun onSessionEnded(t: Throwable?) { if(t != null) { Log.e(logTag, "Something was broken: ${t.message}") } } override fun onContainerVisibilityChanged(isVisible: Boolean) { Log.d(logTag, "onContainerVisibilityChanged. isVisible = $isVisible") }
Java
@Override public void onSessionStarted(@NonNull WindowAreaSessionPresenter session) { windowAreaSession = session; TextView view = new TextView(session.getContext()); view.setText("Hello world, from the other screen!"); session.setContentView(view); } @Override public void onSessionEnded(@Nullable Throwable t) { if(t != null) { Log.e(logTag, "Something was broken: ${t.message}"); } } @Override public void onContainerVisibilityChanged(boolean isVisible) { Log.d(logTag, "onContainerVisibilityChanged. isVisible = " + isVisible); }
ใช้ไอคอน Dual Screen อย่างเป็นทางการเพื่อแสดงวิธีเปิดหรือปิดใช้โหมด Dual Screen แก่ผู้ใช้ เพื่อให้ระบบนิเวศมีความสอดคล้องกัน
ดูตัวอย่างที่ใช้งานได้ได้ที่ DualScreenActivity.kt
โหมดจอแสดงผลด้านหลัง
ตัวอย่างฟังก์ชัน toggleRearDisplayMode()
ต่อไปนี้จะปิดเซสชันหากความสามารถดังกล่าวเปิดใช้งานอยู่แล้ว หรือจะเรียกใช้ฟังก์ชัน transferActivityToWindowArea()
ก็ได้ ซึ่งคล้ายกับตัวอย่างโหมดหน้าจอคู่
Kotlin
fun toggleRearDisplayMode() { if(capabilityStatus == WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE) { if(windowAreaSession == null) { windowAreaSession = windowAreaInfo?.getActiveSession( operation ) } windowAreaSession?.close() } else { windowAreaInfo?.token?.let { token -> windowAreaController.transferActivityToWindowArea( token = token, activity = this, executor = displayExecutor, windowAreaSessionCallback = this ) } } }
Java
void toggleDualScreenMode() { if(capabilityStatus == WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE) { if(windowAreaSession == null) { windowAreaSession = windowAreaInfo.getActiveSession( operation ) } windowAreaSession.close() } else { Binder token = windowAreaInfo.getToken(); windowAreaController.transferActivityToWindowArea(token, this, displayExecutor, this); } }
ในกรณีนี้ ระบบจะใช้กิจกรรมที่แสดงเป็น WindowAreaSessionCallback
ซึ่งติดตั้งใช้งานได้ง่ายกว่า เนื่องจากคอลแบ็กไม่ได้รับตัวนําเสนอที่อนุญาตให้แสดงเนื้อหาในพื้นที่หน้าต่าง แต่โอนกิจกรรมทั้งหมดไปยังพื้นที่อื่นแทน
Kotlin
override fun onSessionStarted() { Log.d(logTag, "onSessionStarted") } override fun onSessionEnded(t: Throwable?) { if(t != null) { Log.e(logTag, "Something was broken: ${t.message}") } }
Java
@Override public void onSessionStarted(){ Log.d(logTag, "onSessionStarted"); } @Override public void onSessionEnded(@Nullable Throwable t) { if(t != null) { Log.e(logTag, "Something was broken: ${t.message}"); } }
ใช้อย่างเป็นทางการของกล้องหลังเพื่อให้สอดคล้องกันทั่วทั้งระบบนิเวศ ไอคอนเพื่อแสดงให้ผู้ใช้เห็นถึงวิธีเปิดหรือปิดใช้โหมดจอแสดงผลด้านหลัง
แหล่งข้อมูลเพิ่มเติม
- เพิ่มประสิทธิภาพแอปกล้องบนอุปกรณ์แบบพับได้ด้วย Jetpack WindowManager Codelab
- สรุปแพ็กเกจ
androidx.window.area
- โค้ดตัวอย่างของ Jetpack WindowManager: