Thiết bị có thể gập lại giúp mang đến trải nghiệm xem độc đáo. Chế độ màn hình sau và chế độ màn hình đôi cho phép bạn tạo các tính năng màn hình đặc biệt cho thiết bị có thể gập lại chẳng hạn như bản xem trước ảnh chân dung tự chụp bằng camera sau và đồng thời nhưng khác nhau trên màn hình trong và ngoài.
Chế độ màn hình sau
Thông thường, khi thiết bị có thể gập lại được mở ra, chỉ có màn hình trong ở trạng thái hoạt động. Chế độ màn hình sau cho phép bạn di chuyển một hoạt động sang màn hình ngoài của thiết bị có thể gập lại thiết bị thường hướng ra ngoài (lưng thiết bị hướng ra ngoài) khi thiết bị được mở ra. Màn hình trong sẽ tự động tắt.
Một ứng dụng mới là hiển thị bản xem trước của máy ảnh trên màn hình ngoài để người dùng có thể tự chụp ảnh chân dung bằng camera sau. Nhờ đó, hiệu suất chụp ảnh thường tốt hơn rất nhiều so với camera trước.
Nhằm kích hoạt chế độ màn hình sau, người dùng sẽ trả lời một hộp thoại để cho phép ứng dụng chuyển đổi màn hình, ví dụ:

Hệ thống sẽ tạo hộp thoại này nên bạn không cần tự phát triển. Các hộp thoại khác nhau sẽ xuất hiện, tuỳ thuộc vào trạng thái của thiết bị; ví dụ: hệ thống sẽ hướng dẫn người dùng mở thiết bị ra nếu thiết bị đang ở trạng thái gập. Bạn không thể tuỳ chỉnh hộp thoại và nó có thể khác nhau tuỳ theo thiết bị của nhiều OEM (Nhà sản xuất thiết bị gốc).
Bạn có thể dùng thử chế độ màn hình sau bằng ứng dụng máy ảnh trên Pixel Fold. Xem mẫu trong lớp học lập trình Tối ưu hoá ứng dụng máy ảnh trên thiết bị có thể gập lại bằng Jetpack WindowManager.
Chế độ màn hình đôi
Chế độ Dual-screen (Màn hình đôi) cho phép bạn hiển thị nội dung cùng lúc trên cả hai màn hình của thiết bị có thể gập lại. Pixel Fold chạy Android 14 có chế độ Dual Screen (API cấp 34) trở lên.
Một ví dụ về trường hợp sử dụng là trình phiên dịch trên màn hình đôi.

Bật các chế độ theo cách có lập trình
Bạn có thể truy cập vào chế độ màn hình sau và chế độ màn hình đôi thông qua API Jetpack WindowManager, kể từ thư viện phiên bản 1.2.0-beta03.
Thêm phần phụ thuộc WindowManager vào tệp build.gradle
của mô-đun ứng dụng:
Groovy
dependencies { implementation "androidx.window:window:1.2.0-beta03" }
Kotlin
dependencies { implementation("androidx.window:window:1.2.0-beta03") }
Điểm truy cập là WindowAreaController
, cung cấp
thông tin và hành vi liên quan đến việc di chuyển cửa sổ giữa các màn hình hoặc giữa các màn hình
khu vực hiển thị trên thiết bị. WindowAreaController
cho phép bạn truy vấn danh sách các đối tượng WindowAreaInfo
hiện có.
Hãy dùng WindowAreaInfo
để truy cập vào WindowAreaSession
, một giao diện
biểu thị một tính năng đang hoạt động trên khu vực cửa sổ. Sử dụng WindowAreaSession
để xác định
tình trạng sẵn có của một WindowAreaCapability
cụ thể.
Mỗi chức năng liên quan đến một WindowAreaCapability.Operation
cụ thể.
Trong phiên bản 1.2.0-beta03, Jetpack WindowManager hỗ trợ 2 loại thao tác:
WindowAreaCapability.Operation.OPERATION_PRESENT_ON_AREA
, tức là dùng để khởi động chế độ màn hình đôiWindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA
, dùng để khởi động chế độ màn hình sau
Dưới đây là ví dụ về cách khai báo các biến cho chế độ màn hình sau và chế độ màn hình đôi trong hoạt động chính của ứng dụng:
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;
Dưới đây là cách khởi tạo các biến trong phương thức onCreate()
của hoạt động:
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; } } });
Trước khi bắt đầu một thao tác, hãy kiểm tra tính sẵn có của chức năng:
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. }
Chế độ màn hình đôi
Ví dụ sau đây giúp đóng phiên nếu chức năng này vốn đang hoạt động hoặc
nếu không, hãy gọi hàm 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); } }
Hãy lưu ý đến việc dùng hoạt động chính của ứng dụng làm
Đối số WindowAreaPresentationSessionCallback
.
API này dùng phương thức tiếp cận của trình nghe: khi đưa ra yêu cầu trình chiếu nội dung lên màn hình khác của một thiết bị có thể gập lại, bạn sẽ bắt đầu một phiên được trả về thông qua phương thức onSessionStarted()
của trình nghe. Khi đóng phiên, bạn sẽ nhận được thông báo xác nhận trong phương thức onSessionEnded()
.
Để tạo trình nghe, hãy triển khai WindowAreaPresentationSessionCallback
giao diện:
Kotlin
class MainActivity : AppCompatActivity(), windowAreaPresentationSessionCallback
Java
public class MainActivity extends AppCompatActivity implements WindowAreaPresentationSessionCallback
Trình nghe cần triển khai các phương thức onSessionStarted()
, onSessionEnded(),
và onContainerVisibilityChanged()
. Phương thức gọi lại sẽ thông báo cho bạn về trạng thái phiên và cho phép bạn cập nhật ứng dụng cho phù hợp.
Lệnh gọi lại onSessionStarted()
nhận được WindowAreaSessionPresenter
dưới dạng
một đối số. Đối số là vùng chứa cho phép bạn truy cập vào một khu vực cửa sổ
và hiện nội dung. Hệ thống có thể tự động loại bỏ bản trình bày khi người dùng rời khỏi cửa sổ chính của ứng dụng hoặc có thể đóng bản trình bày bằng cách gọi WindowAreaSessionPresenter#close()
.
Đối với các lệnh gọi lại khác, để đơn giản, bạn chỉ cần kiểm tra nội dung hàm để xem có lỗi nào không rồi ghi lại trạng thái:
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); }
Để duy trì tính nhất quán trong toàn bộ hệ sinh thái, hãy sử dụng công cụ Màn hình kép biểu tượng để cho người dùng biết cách bật hoặc tắt chế độ màn hình đôi.
Để xem ví dụ về cách thức hoạt động, hãy xem DualScreenActivity.kt.
Chế độ màn hình sau
Tương tự như ví dụ về chế độ Dual Screen, ví dụ sau về
Hàm toggleRearDisplayMode()
đóng phiên nếu chức năng này là
đã hoạt động hoặc gọi transferActivityToWindowArea()
hàm:
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); } }
Trong trường hợp này, hoạt động hiển thị được dùng dưới dạng một WindowAreaSessionCallback
, dễ triển khai hơn vì lệnh gọi lại này không nhận một công cụ trình chiếu cho phép hiển thị nội dung trên một khu vực cửa sổ mà lại chuyển toàn bộ hoạt động sang một khu vực khác:
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}"); } }
Để duy trì tính nhất quán trong toàn bộ hệ sinh thái, hãy sử dụng Camera sau biểu tượng để hướng dẫn người dùng cách bật hoặc tắt chế độ màn hình sau.
Tài nguyên khác
- Lớp học lập trình Tối ưu hoá ứng dụng máy ảnh trên thiết bị có thể gập lại nhờ Jetpack WindowManager
- Thông tin tóm tắt về gói
androidx.window.area
- Mã mẫu Jetpack WindowManager: