支援桌面視窗

有了「電腦分割視窗」功能,使用者可以同時執行多個應用程式,並調整應用程式視窗大小,享受多功能桌機般的體驗。

圖 1 顯示啟用電腦分割視窗模式後,畫面的組織方式。注意事項:

  • 使用者可以同時並排執行多個應用程式。
  • 工作列固定顯示在螢幕底部,並顯示執行中的應用程式。使用者可以釘選應用程式,以便快速存取。
  • 全新的可自訂標題列會在每個視窗頂端顯示,並提供縮小和放大等控制項。
圖 1. 平板電腦上的電腦分割視窗。

根據預設,應用程式會在 Android 平板電腦上以全螢幕模式開啟。如要以電腦分割視窗模式啟動應用程式,請按住畫面頂端的視窗控點,然後在 UI 中拖曳控點,如圖 2 所示。

如果應用程式在電腦分割視窗模式下開啟,其他應用程式也會在電腦視窗中開啟。

圖 2. 按住並拖曳應用程式視窗控點,即可進入電腦分割視窗模式。

使用者也可以透過選單叫用電腦分割視窗功能,方法是輕觸或按一下視窗控點,或使用鍵盤快速鍵 Meta 鍵 (Windows、Command 或搜尋) + Ctrl + 向下鍵,選單會顯示在視窗控點下方。

如要結束電腦分割視窗模式,使用者可以關閉所有開啟的視窗,或是抓住電腦視窗頂端的視窗控點,然後將應用程式拖曳到螢幕頂端。Meta + H 鍵盤快速鍵也會結束桌面視窗模式,並再次以全螢幕模式執行應用程式。

如要返回電腦分割視窗模式,請在「最近」畫面中輕觸或按一下電腦空間圖塊。

可調整大小和相容性模式

在電腦分割視窗模式下,鎖定螢幕方向的應用程式可自由調整大小。也就是說,即使活動鎖定為直向,使用者仍可將應用程式大小調整為橫向視窗。

圖 3. 將僅限直向應用程式的視窗調整為橫向。

如果應用程式宣告為不可調整大小 (即 resizeableActivity = false),系統會縮放其 UI,但維持相同的顯示比例。

圖 4. 無法調整大小的應用程式 UI 會隨著視窗大小調整而縮放。

如果相機應用程式鎖定螢幕方向或宣告無法調整大小,系統會特別處理這類應用程式的相機觀景窗:視窗可完全調整大小,但觀景窗會維持相同顯示比例。如果應用程式假設一律以直向或橫向模式執行,就會硬式編碼或做出其他假設,導致預覽或擷取的圖片螢幕方向或顯示比例計算錯誤,造成圖片遭到拉伸、方向有誤或上下顛倒。

在應用程式準備好實作完全回應式相機觀景窗之前,特殊處理方式可提供更基本的使用者體驗,減輕錯誤假設可能造成的影響。

如要進一步瞭解相機應用程式的相容模式,請參閱「裝置相容模式」。

圖 5. 視窗大小調整時,相機觀景窗會保留顯示比例。

可自訂的標頭插邊

即使處於沉浸模式,以電腦分割視窗模式執行的所有應用程式都會有標題列。您可以自訂這個工具列,避免應用程式內容遭到遮蔽,並直接在標頭空間中繪製自訂 UI 元素。

實作自訂標頭前後的 Chrome。
圖 6. 實作自訂標頭前後的 Chrome 畫面。

實作

如要在標題列中繪製自訂內容,第一步是將標題列背景設為透明。如要這麼做,請將 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():自動設定 Composable 的高度,與系統的標題列相符,協助自訂背景填滿說明文字區域,不必硬式編碼像素值。

  • WindowInsets.captionBar:提供桌面視窗控制項 (「關閉」、「最大化」等) 的尺寸,讓 UI 在進入或離開電腦分割視窗時自動縮放或隱藏。

詳情請參閱「關於視窗插邊」。除了標題,您還可以在說明文字列中顯示其他 UI 元素,例如分頁 (如 Google Chrome 中的分頁)、搜尋列或個人資料虛擬人偶。

使用者介面

為避免 UI 與系統按鈕重疊,Android 15 提供 WindowInsets#getBoundingRects() 方法。這個方法會傳回 Rect 物件清單,代表系統元素佔用的區域。說明文字列中剩餘的空間是安全區域,可安全地放置自訂內容。

使用 APPEARANCE_LIGHT_CAPTION_BARS 切換淺色和深色主題的系統說明文字元素外觀。在 Compose 中使用 WindowInsets.Companion.captionBar(),或在 Views 中使用 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 元素 (例如分頁或文件) 拖曳出應用程式視窗,啟動新的應用程式例項。使用者也可以在同一應用程式的不同例項之間移動元素。

圖 7. 將分頁拖曳出桌面視窗,即可啟動新的 Chrome 執行個體。

使用拖曳功能轉移資料

如要將可組合函式設定為多實體拖曳的拖曳來源,讓使用者將內容拖曳到應用程式的其他實體,或將內容放到畫面的空白區域來建立實體,請使用 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
    )
}

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
        }
    }
)

主要步驟:

其他最佳化

自訂應用程式啟動方式,以及從電腦分割視窗模式切換到全螢幕模式。

指定預設大小和位置

並非所有應用程式 (即使可調整大小) 都需要大視窗才能為使用者帶來價值。您可以使用 ActivityOptions#setLaunchBounds() 方法,在啟動活動時指定預設大小和位置。

從桌面空間進入全螢幕模式

應用程式可以呼叫 Activity#requestFullScreenMode() 進入全螢幕模式。這個方法會直接從電腦分割視窗顯示應用程式全螢幕。