อุปกรณ์แบบพับได้มอบประสบการณ์การรับชมที่ไม่เหมือนใคร โหมดจอแสดงผลด้านหลังและโหมด Dual Screen ช่วยให้คุณสร้างฟีเจอร์จอแสดงผลพิเศษสำหรับอุปกรณ์แบบพับได้ เช่น การแสดงตัวอย่างเซลฟีด้วยกล้องหลังและการแสดงผลที่แตกต่างกันแต่พร้อมกันบนหน้าจอด้านในและด้านนอก
โหมดจอแสดงผลด้านหลัง
โดยปกติเมื่อกางอุปกรณ์แบบพับได้ หน้าจอด้านในเท่านั้นที่จะทำงาน โหมดจอแสดงผลด้านหลังช่วยให้คุณย้ายกิจกรรมไปยังหน้าจอด้านนอกของอุปกรณ์แบบพับได้ ซึ่งโดยปกติจะหันออกจากผู้ใช้ขณะที่กางอุปกรณ์ หน้าจอด้านในจะปิดโดยอัตโนมัติ
แอปพลิเคชันใหม่นี้จะแสดงตัวอย่างกล้องบนหน้าจอด้านนอก เพื่อให้ผู้ใช้ถ่ายเซลฟีด้วยกล้องหลังได้ ซึ่งโดยปกติแล้วกล้องหลังจะมีประสิทธิภาพในการถ่ายภาพที่ดีกว่ากล้องหน้ามาก
หากต้องการเปิดใช้โหมดจอแสดงผลด้านหลัง ผู้ใช้จะต้องตอบกล่องโต้ตอบเพื่ออนุญาตให้แอปสลับหน้าจอ เช่น
ระบบจะสร้างกล่องโต้ตอบขึ้นมาเอง คุณจึงไม่ต้องพัฒนาอะไร กล่องโต้ตอบต่างๆ จะปรากฏขึ้นตามสถานะของอุปกรณ์ เช่น ระบบจะแนะนำให้ผู้ใช้กางอุปกรณ์หากอุปกรณ์ปิดอยู่ คุณปรับแต่งกล่องโต้ตอบไม่ได้ และกล่องโต้ตอบอาจแตกต่างกันไปในอุปกรณ์จาก OEM ต่างๆ
คุณสามารถลองใช้โหมดจอแสดงผลด้านหลังกับแอปกล้อง Pixel Fold ดูตัวอย่าง การใช้งานได้ใน Codelab เรื่อง เพิ่มประสิทธิภาพแอปกล้องในอุปกรณ์แบบพับได้ด้วย Jetpack WindowManager
โหมด Dual Screen
โหมด Dual Screen ช่วยให้คุณแสดงเนื้อหาบนจอแสดงผลทั้ง 2 จอของอุปกรณ์แบบพับได้พร้อมกัน โหมด Dual Screen ใช้งานได้ใน Pixel Fold ที่ใช้ Android 14 (ระดับ API 34) ขึ้นไป
ตัวอย่างกรณีการใช้งานคือล่ามแบบ Dual Screen
เปิดใช้โหมดด้วยโปรแกรม
คุณเข้าถึงโหมดจอแสดงผลด้านหลังและโหมด Dual Screen ได้ผ่าน Jetpack WindowManager API ตั้งแต่เวอร์ชันไลบรารี 1.2.0-beta03 เป็นต้นไป
เพิ่มทรัพยากร Dependency ของ WindowManager ลงในไฟล์ build.gradle ของโมดูลแอป
Kotlin
dependencies {
// Define window_version in your project's build configuration.
implementation("androidx.window:window:$window_version")
}
ดึงดูด
dependencies {
// TODO: Define window_version in your project's build configuration.
implementation "androidx.window:window:$window_version"
}
จุดเริ่มต้นคือ WindowAreaController ซึ่งให้ข้อมูลและลักษณะการทำงานที่เกี่ยวข้องกับการย้ายหน้าต่างระหว่างจอแสดงผลหรือระหว่างพื้นที่แสดงผลในอุปกรณ์ WindowAreaController ช่วยให้คุณค้นหารายการ
ออบเจ็กต์ WindowAreaInfo ที่พร้อมใช้งานได้
ใช้ WindowAreaInfo เพื่อเข้าถึง WindowAreaSession ซึ่งเป็นอินเทอร์เฟซที่
แสดงถึงฟีเจอร์พื้นที่หน้าต่างที่ใช้งานอยู่ ใช้ WindowAreaSession เพื่อกำหนด
ความพร้อมใช้งานของ WindowAreaCapability ที่เฉพาะเจาะจง
ความสามารถแต่ละอย่างจะเกี่ยวข้องกับ WindowAreaCapability.Operation ที่เฉพาะเจาะจง
ในเวอร์ชัน 1.2.0-beta03 Jetpack WindowManager รองรับการดำเนินการ 2 ประเภท ได้แก่
WindowAreaCapability.Operation.OPERATION_PRESENT_ON_AREAซึ่งใช้เพื่อเริ่มโหมด Dual ScreenWindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREAซึ่งใช้เพื่อเริ่มโหมดจอแสดงผลด้านหลัง
ตัวอย่างวิธีประกาศตัวแปรสำหรับโหมดจอแสดงผลด้านหลังและโหมด Dual Screen ในกิจกรรมหลักของแอป
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.
}
โหมด Dual Screen
ตัวอย่างต่อไปนี้จะปิดเซสชันหากความสามารถใช้งานอยู่แล้ว หรือ
เรียกใช้ฟังก์ชัน 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() ของ Listener เมื่อปิด
เซสชัน คุณจะได้รับการยืนยันในเมธอด onSessionEnded()
หากต้องการสร้าง Listener ให้ใช้ WindowAreaPresentationSessionCallback
อินเทอร์เฟซ
Kotlin
class ExampleActivity : ComponentActivity(), WindowAreaPresentationSessionCallback {
Java
public class MainActivity extends AppCompatActivity implements WindowAreaPresentationSessionCallback
Listener ต้องใช้เมธอด onSessionStarted(), onSessionEnded(),
และ onContainerVisibilityChanged() เมธอดเรียกกลับจะแจ้งให้คุณทราบสถานะเซสชันและช่วยให้คุณอัปเดตแอปได้ตามความเหมาะสม
การเรียกกลับ onSessionStarted() จะรับ WindowAreaSessionPresenter เป็น
อาร์กิวเมนต์ อาร์กิวเมนต์คือคอนเทนเนอร์ที่ช่วยให้คุณเข้าถึงพื้นที่หน้าต่างและแสดงเนื้อหาได้ ระบบสามารถยกเลิกการนำเสนอโดยอัตโนมัติเมื่อผู้ใช้ออกจากหน้าต่างแอปพลิเคชันหลัก หรือปิดการนำเสนอได้โดยการเรียกใช้ WindowAreaSessionPresenter#close()
สำหรับการเรียกกลับอื่นๆ ให้ตรวจสอบข้อผิดพลาดในเนื้อหาฟังก์ชันและบันทึกสถานะเพื่อความเรียบง่าย
Kotlin
override fun onSessionStarted(session: WindowAreaSessionPresenter) { windowAreaSession = session session.setContentView(ComposeView(session.context).apply { setContent { MyScreen() } }) } 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()
ซึ่งคล้ายกับตัวอย่างโหมด Dual Screen
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 toggleRearDisplayMode() {
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
Rear Display API ทำงานด้วยแนวทาง Listener โดยเมื่อคุณส่งคำขอให้ย้ายเนื้อหาไปยังจอแสดงผลอีกจอหนึ่ง คุณจะเริ่มเซสชันที่ส่งคืนผ่านเมธอด onSessionStarted() ของ Listener หากต้องการกลับไปที่จอแสดงผลด้านใน (และใหญ่กว่า) ให้ปิดเซสชัน แล้วคุณจะได้รับการยืนยันในเมธอด onSessionEnded()
Kotlin
override fun onSessionStarted(session: WindowAreaSession) { 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}");
}
}
ใช้ไอคอนอย่างเป็นทางการของ กล้องหลัง เพื่อระบุให้ผู้ใช้ทราบวิธีเปิดหรือปิดใช้โหมดจอแสดงผลด้านหลัง เพื่อรักษาความสอดคล้องกันในระบบนิเวศ
แหล่งข้อมูลเพิ่มเติม
- Codelab เรื่องเพิ่มประสิทธิภาพแอปกล้องในอุปกรณ์แบบพับได้ด้วย Jetpack WindowManager
androidx.window.areaสรุปแพ็กเกจ- โค้ดตัวอย่าง Jetpack WindowManager: