手机多窗口
Tauri 支持在 Android 和 iOS 上使用多个窗口,允许你的应用在平板电脑上并排显示内容,或在 iPad 上显示在不同的场景中。
🌐 Tauri supports multiple windows on Android and iOS, allowing your app to display content side-by-side on tablets or in separate scenes on iPad.
在 Android 上,多窗口是通过 Activity Embedding 实现的,它允许系统在大屏幕上并排显示两个活动。
🌐 On Android, multi-window is implemented using Activity Embedding, which lets the system display two activities side by side on large screens.
在 iOS 上,多窗口使用 UIScene API,这允许 iPad 用户在独立的窗口中打开你的应用的多个实例。
🌐 On iOS, multi-window uses the UIScene API, which allows iPad users to open multiple instances of your app in separate windows.
在手机上,系统通常不会将两个窗口并排布局。在Android上,创建另一个窗口仍然会启动一个单独的活动,但在手机尺寸的显示屏上,它通常会被推到活动返回栈上——因此按下返回会回到之前的活动,而不是关闭分屏。在iOS上(尤其是 iPhone),打开或创建另一个窗口通常会用新场景的内容替换当前的界面,而不会同时保持两个窗口可见;真正的并行窗口体验仍然存在于iPad(以及舞台管理器)上。
🌐 On phones, the system usually does not lay out two windows side by side. On Android, creating another window still launches a separate activity, but on handset-sized displays it is typically pushed onto the activity back stack—so Back returns to the previous activity instead of closing a split. On iOS (especially iPhone), opening or creating another window often replaces the current UI with the new scene’s content rather than keeping both visible at once; true concurrent windows remain an iPad (and Stage Manager) experience.
🌐 Shared Setup
两个平台都需要权限才能从前端创建新窗口。
🌐 Both platforms require a capability permission to create new windows from the frontend.
🌐 Capabilities
将 core:webview:allow-create-webview-window 权限添加到你的能力文件中,以便你的前端可以创建新窗口。
🌐 Add the core:webview:allow-create-webview-window permission to your capability file so your frontend can create new windows.
如果你要创建多个窗口,请在 windows 数组中使用通配符或列出每个窗口标签:
🌐 If you are creating multiple windows, use a wildcard or list every window label in the windows array:
{ "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", "description": "Capability for the main window", "windows": ["main"], "permissions": ["core:default", "core:webview:allow-create-webview-window"]}Android 多窗口使用 Activity Embedding 在大屏幕(平板、可折叠设备)上并排分割活动。你需要为每种窗口类型创建一个 Android Activity,配置分割规则,并注册初始化程序。
🌐 Android multi-window uses Activity Embedding to split activities side by side on large screens (tablets, foldables). You need to create an Android Activity for each window type, configure split rules, and register an initializer.
-
将所需的 AndroidX 库添加到你的
build.gradle.kts中:src-tauri/gen/android/app/build.gradle.kts dependencies {// ... existing dependenciesimplementation("androidx.window:window:1.5.0")implementation("androidx.startup:startup-runtime:1.2.0")} -
为每种额外的窗口类型创建一个 Kotlin 类。每个活动都必须继承
TauriActivity:src-tauri/gen/android/app/src/main/java/com/example/app/DetailActivity.kt package com.example.appimport android.os.Bundleimport android.os.PersistableBundleclass DetailActivity: TauriActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)}} -
通过添加
tools命名空间和嵌入属性来注册新活动并启用活动嵌入:src-tauri/gen/android/app/src/main/AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><application ...><propertyandroid:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"android:value="true" /><!-- Existing MainActivity --><activityandroid:name=".MainActivity"android:exported="true"...>...</activity><!-- New activity for the detail window --><activity android:name=".DetailActivity" android:exported="true" /><!-- Register the split initializer --><provider android:name="androidx.startup.InitializationProvider"android:authorities="${applicationId}.androidx-startup"android:exported="false"tools:node="merge"><meta-data android:name="${applicationId}.SplitInitializer"android:value="androidx.startup" /></provider></application></manifest> -
初始化程序在应用启动时加载分割对规则:
src-tauri/gen/android/app/src/main/java/com/example/app/SplitInitializer.kt package com.example.appimport android.content.Contextimport androidx.startup.Initializerimport androidx.window.core.ExperimentalWindowApiimport androidx.window.embedding.RuleController@OptIn(ExperimentalWindowApi::class)class SplitInitializer : Initializer<RuleController> {override fun create(context: Context): RuleController {return RuleController.getInstance(context).apply {setRules(RuleController.parseRules(context, R.xml.main_split_config))}}override fun dependencies(): List<Class<out Initializer<*>>> {return emptyList()}} -
创建一个 XML 资源,告诉系统如何配对活动并分屏:
src-tauri/gen/android/app/src/main/res/xml/main_split_config.xml <resourcesxmlns:window="http://schemas.android.com/apk/res-auto"><SplitPairRulewindow:splitRatio="0.33"window:splitLayoutDirection="locale"window:splitMinWidthDp="840"window:splitMaxAspectRatioInPortrait="alwaysAllow"window:finishPrimaryWithSecondary="never"window:finishSecondaryWithPrimary="never"window:clearTop="false"><SplitPairFilterwindow:primaryActivityName=".MainActivity"window:secondaryActivityName=".DetailActivity"/></SplitPairRule></resources>关键属性:
splitRatio—— 屏幕如何被划分(0.33 表示主要活动占三分之一)splitMinWidthDp— 激活分屏的最小屏幕宽度(840dp 适用于平板电脑)splitMaxAspectRatioInPortrait— 设置为alwaysAllow以在纵向模式下启用分屏primaryActivityName/secondaryActivityName— 哪一对活动会触发分裂
在 iOS 上,多窗口使用 UIScene API。iPad 用户可以通过长按应用图标并选择“新窗口”来打开新窗口,或者你的应用可以通过编程方式创建它们。
🌐 On iOS, multi-window uses the UIScene API. iPad users can open new windows by long-pressing the app icon and selecting “New window”, or your app can create them programmatically.
-
在你的
src-tauri目录中创建一个Info.ios.plist文件来声明场景支持:src-tauri/Info.ios.plist <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN""http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>UIApplicationSceneManifest</key><dict><key>UIApplicationSupportsMultipleScenes</key><true/><key>UISceneConfigurations</key><dict/></dict></dict></plist> -
当用户在 iPad 上请求新窗口时(例如长按应用图标),Tauri 会发出一个
RunEvent::SceneRequested事件。处理它以创建一个新窗口:src-tauri/src/lib.rs #[cfg_attr(mobile, tauri::mobile_entry_point)]pub fn run() {#[cfg(target_os = "ios")]let mut counter = 0;tauri::Builder::default().setup(|app| {tauri::WebviewWindowBuilder::new(app, "main", tauri::WebviewUrl::default()).build()?;Ok(())}).build(tauri::generate_context!()).expect("error while running tauri application").run(move |app, event| {#[cfg(target_os = "ios")]if let tauri::RunEvent::SceneRequested { .. } = event {counter += 1;tauri::WebviewWindowBuilder::new(app,format!("main-{counter}"),tauri::WebviewUrl::default(),).build().unwrap();}#[cfg(not(target_os = "ios"))]let _ = (app, event);});}
🌐 Creating Windows
你可以从 Rust 和前端 JavaScript API 创建额外的窗口。WebviewWindowBuilder(Rust)和 WebviewWindow(JavaScript)接受特定于平台的选项:
🌐 You can create additional windows from both Rust and the frontend JavaScript API. The WebviewWindowBuilder (Rust) and WebviewWindow (JavaScript) accept platform-specific options:
安卓选项:
🌐 Android options:
activityName— 要为此窗口创建的 Android Activity 类的名称。createdByActivityName—— 创建此窗口的活动的名称。这决定了新活动属于哪个活动栈,对于拆分规则的正确运行非常重要。如果未设置,它会自动从管理器继承(例如,当从Window或Webview句柄构建时)。
iOS 选项:
🌐 iOS options:
requestedBySceneIdentifier— 设置请求创建此新场景的 UIScene 的标识符,从而在两个场景之间建立关系。默认情况下,系统使用前台场景。如果未设置,则会自动从管理器继承。
import { WebviewWindow } from '@tauri-apps/api/webviewWindow';
function openDetail(id) { const webview = new WebviewWindow(`detail-${id}`, { url: `detail/${id}`, activityName: 'DetailActivity', }); webview.once('tauri://created', () => { console.log('window created'); }); webview.once('tauri://error', (e) => { console.error(e); });}use tauri::Manager;
let main_window = app.get_webview_window("main").unwrap();// use the main_window instance so the relationships are determined automaticallylet builder = tauri::WebviewWindowBuilder::new(main_window, "detail", tauri::WebviewUrl::App("detail/1".into()));
#[cfg(target_os = "android")]let builder = builder.activity_name("DetailActivity");
let window = builder.build()?;🌐 Window Instance APIs
一旦创建了一个窗口,你可以获取它的平台特定标识符:
🌐 Once a window has been created, you can retrieve its platform-specific identifier:
const activityName = await window.activityName();const sceneId = await window.sceneIdentifier();#[cfg(target_os = "android")]let activity = window.activity_name()?;
#[cfg(target_os = "ios")]let scene_id = window.scene_identifier()?;这些获取器在创建相关窗口时用于引用窗口的身份非常有用。例如,你可以读取一个窗口的 activityName 并将其作为新窗口的 createdByActivityName,或者读取 sceneIdentifier 并将其作为 requestedBySceneIdentifier。
🌐 These getters are useful for referencing a window’s identity when creating related windows. For example, you can read a window’s activityName to pass as createdByActivityName on a new window, or read sceneIdentifier to pass as requestedBySceneIdentifier.
Tauri 中文网 - 粤ICP备13048890号
Nodejs.cn 旗下网站