電腦分割視窗可讓使用者在可調整大小的應用程式視窗中,同時執行多個應用程式,享有類似桌機的多功能體驗。
圖 1 顯示啟用電腦分割視窗模式後的畫面。注意事項:
- 使用者可以同時執行多個應用程式。
- 工作列固定在螢幕底部,顯示執行中的應用程式。使用者可以固定應用程式,方便快速存取。
- 全新的可自訂標題列會在每個視窗頂端加上控制項,例如最小化和最大化。
根據預設,應用程式會在 Android 平板電腦上以全螢幕開啟。如要在電腦分割視窗模式中啟動應用程式,請按住畫面頂端的視窗控點,並在 UI 中拖曳控點,如圖 2 所示。
當應用程式在電腦分割視窗中開啟時,其他應用程式也會在電腦分割視窗中開啟。
使用者也可以輕觸或點選視窗控點,或使用鍵盤快速鍵 Meta 鍵 (Windows、Command 或搜尋) + Ctrl + 向下鍵,從視窗控點下方顯示的選單叫用電腦分割視窗。
使用者可以關閉所有使用中的視窗,或抓取電腦視窗頂端的視窗控點,並將應用程式拖曳到螢幕頂端,即可退出電腦分割視窗模式。Meta + H 鍵盤快速鍵也會退出電腦分割視窗,並再次以全螢幕模式執行應用程式。
如要返回電腦分割視窗,請輕觸或按一下「最近」畫面中的電腦空間圖塊。
針對類電腦環境最佳化應用程式版面配置
由於螢幕空間增加、滑鼠和鍵盤輸入的精確度,以及對高效率的期望,電腦版體驗的設計可能與行動版設計有很大差異。
Jetpack WindowManager 提供專屬 API,可協助開發人員決定何時顯示桌面 UI,這類 UI 通常具有較高的資訊密度、不同的導覽模式,以及最佳化的滑鼠互動。
lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { windowInfoTracker.windowEngagementInfo(this@DesktopWindowingActivity) .collect { windowEngagementInfo -> if(windowEngagementInfo.hasEngagementMode(WindowEngagementInfo.EngagementMode.PRECISE_POINTER)){ showDesktopOptimizedUI() }else { showTouchOptimizedUI() } } } }
詳情請參閱「電腦版設計」。
可調整大小和相容模式
在電腦分割視窗模式下,即使應用程式鎖定螢幕方向,仍可自由調整大小。也就是說,即使活動鎖定為直向螢幕方向,使用者仍可將應用程式大小調整為橫向螢幕方向視窗。
如果應用程式宣告為不可調整大小 (即 resizeableActivity = false),則 UI 會縮放,但保持相同的顯示比例。
如果相機應用程式鎖定螢幕方向,或宣告為不可調整大小,則相機觀景窗會經過特殊處理:視窗可完全調整大小,但觀景窗會保持相同的顯示比例。如果假設應用程式一律以直向或橫向模式執行,應用程式會硬式編碼或做出假設,導致預覽或擷取圖片的方向或顯示比例計算錯誤,進而造成圖片拉伸、側向或上下顛倒。
在應用程式準備好導入完全回應式的相機觀景窗之前,特殊處理方式可提供更基本的使用者體驗,減輕錯誤假設可能造成的影響。
如要進一步瞭解相機應用程式的相容模式,請參閱「裝置相容模式」。
可自訂的標頭內嵌
在電腦分割視窗中執行的所有應用程式都有標題列,即使在沉浸模式下也是如此。您可以自訂這個列,避免應用程式內容遭到遮蓋,並直接在標頭空間中繪製自訂 UI 元素。
導入作業
如要在標頭列中繪製自訂內容,第一步是將標頭列背景設為透明。如要這麼做,請將 APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND 旗標與 WindowInsetsController 搭配使用。
window.insetsController?.setSystemBarsAppearance( WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND, WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND )
標頭列透明後,您可以設定標頭區域的樣式,使其符合應用程式設計。使用 WindowInsets.isCaptionBarVisible 偵測列是否存在,並為版面配置套用適當的高度或邊框間距。
@OptIn(ExperimentalLayoutApi::class) @Composable fun CaptionBar() { if (WindowInsets.isCaptionBarVisible) { Row( modifier = Modifier .windowInsetsTopHeight(WindowInsets.captionBar) .fillMaxWidth() .background(if (isSystemInDarkTheme()) Color.White else Color.Black), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { Text( text = "Caption Bar Title", style = MaterialTheme.typography.titleMedium, modifier = Modifier.padding(4.dp) ) } } }
setSystemBarsAppearance(appearance,mask):設定系統資訊列的視覺樣式。第一個參數定義目標外觀標記,第二個參數則做為遮罩,控制要修改哪些特定標記。windowInsetsTopHeight():自動設定可組合項的高度,使其與系統的標頭列相符,讓自訂背景填滿標題區域,不需硬式編碼像素值。WindowInsets.captionBar:提供電腦分割視窗控制項 (「關閉」、「最大化」等) 的尺寸,讓 UI 在進入或離開電腦分割視窗時自動縮放或隱藏。
詳情請參閱「關於視窗插邊」。除了標題外,您也可以在標題列中顯示其他 UI 元素,例如分頁 (如 Google Chrome)、搜尋列或個人資料顯示圖片。
使用者介面
為避免 UI 與系統按鈕重疊,Android 15 提供 WindowInsets#getBoundingRects() 方法。這個方法會傳回 Rect 物件清單,代表系統元素占用的區域。標題列中的任何剩餘空間都是安全區域,您可以在其中安全地放置自訂內容。
使用 APPEARANCE_LIGHT_CAPTION_BARS 切換淺色和深色主題的系統字幕元素外觀。在 Compose 中使用 WindowInsets.Companion.captionBar(),或在 View 中使用 WindowInsets.Type.captionBar(),即可存取插邊。
詳情請參閱「關於視窗內嵌」。
多工處理和多實體支援
多工處理是電腦分割視窗的核心功能,允許應用程式有多個執行個體,可大幅提高使用者的工作效率。
從 Android 15 開始,您可以使用 PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI。在 AndroidManifest.xml 中設定這項屬性,即可指定系統 UI 應提供選項 (例如「新增視窗」按鈕),讓應用程式在多個執行個體中啟動。
<application>
<property
android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
android:value="true" />
</application>
注意:在電腦分割視窗和其他多視窗模式環境中,新任務會在新的視窗中開啟,因此每當應用程式啟動多項任務時,請務必仔細檢查使用者歷程。
使用拖曳手勢管理應用程式執行個體
在多視窗模式下,使用者可以將 UI 元素 (例如分頁或文件) 拖曳出應用程式視窗,藉此啟動新的應用程式執行個體。使用者也可以在同一個應用程式的不同執行個體之間移動元素。
使用拖放功能轉移資料
如要將可組合項設為多實體拖曳的拖曳來源,讓使用者將內容拖曳至應用程式的其他執行個體,或將內容拖曳至螢幕空白區域來建立「新」執行個體,請使用 dragAndDropSource 修飾符。在 lambda 中,傳回 DragAndDropTransferData,並傳遞包含要轉移資料的 ClipData,以及用於設定多實體行為的旗標。
Android 15 針對電腦模式視窗和多實體互動,推出兩項重要旗標:
DRAG_FLAG_GLOBAL_SAME_APPLICATION:表示拖曳作業可以跨越視窗邊界 (適用於同一應用程式的多個執行個體)。如果設定這個標記並呼叫startDragAndDrop(),則只有屬於同一個應用程式的可見視窗,才能參與拖曳作業並接收拖曳內容。
Modifier.dragAndDropSource { _ -> DragAndDropTransferData( clipData = ClipData.newPlainText("label", "Your data"), flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION ) }
DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG:如果沒有其他視窗處理拖曳內容,使用者可以將拖曳內容拖曳到螢幕空白區域,啟動應用程式的新例項。- 使用這個旗標時,您必須使用
ClipData.Item.Builder#setIntentSender()提供IntentSender,系統會在發生未處理的拖放作業時,使用該值啟動新活動。
- 使用這個旗標時,您必須使用
Modifier.dragAndDropSource { _ -> val intent = Intent.makeMainActivity(activity.componentName).apply { putExtra("EXTRA_ITEM_ID", itemId) flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK or Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT } val pendingIntent = PendingIntent.getActivity( activity, 0, intent, PendingIntent.FLAG_IMMUTABLE ) val data = ClipData( "Item $itemId", arrayOf(ClipDescription.MIMETYPE_TEXT_INTENT), ClipData.Item.Builder().setIntentSender(pendingIntent.intentSender).build() ) DragAndDropTransferData( clipData = data, flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION or View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG, ) }
接收轉移的資料
如要接受其他例項的資料,請使用 dragAndDropTarget 修飾符。如果資料來自其他執行個體或應用程式,您必須明確要求權限。
Modifier.dragAndDropTarget( shouldStartDragAndDrop = { event -> event.toAndroidDragEvent().clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN) }, target = object : DragAndDropTarget { override fun onDrop(event: DragAndDropEvent): Boolean { requestDragAndDropPermissions(activity, event.toAndroidDragEvent()) val clipData = event.toAndroidDragEvent().clipData val item = clipData?.getItemAt(0)?.text if (item != null) { // Process the dropped text item here } return item != null } } )
主要步驟:
- 篩選:使用
shouldStartDragAndDrop檢查是否支援傳入的資料 (MIME 類型)。 - 權限:呼叫
requestDragAndDropPermissions(event)即可存取資料。 - 處理:在
onDrop回呼中擷取資料。
其他最佳化
自訂應用程式啟動方式,並將應用程式從電腦分割視窗模式切換為全螢幕。
指定預設大小和位置
並非所有應用程式都需要大視窗才能為使用者提供價值,即使是可調整大小的應用程式也一樣。您可以使用 ActivityOptions#setLaunchBounds() 方法,指定活動啟動時的預設大小和位置。
從桌面空間進入全螢幕模式
應用程式可以呼叫 Activity#requestFullScreenMode(),進入全螢幕模式。方法會直接從電腦分割視窗,以全螢幕顯示應用程式。