已合并
master - docs(wms-layout): add AdjustLayout sample #9388
yyuehao创建于 5月15日
master - docs(wms-layout): add AdjustLayout sample #9388
已合并
共 44 个文件变更+1636-1
| @@ -53,4 +53,4 @@ export default class NocalculationsupportAbility extends UIAbility { | |||
| 53 | // Ability has back to background | 53 | // Ability has back to background |
| 54 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); | 54 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); |
| 55 | } | 55 | } |
| 56 | -} | 56 | +} |
| @@ -0,0 +1,12 @@ | |||
| 1 | +/node_modules | ||
| 2 | +/oh_modules | ||
| 3 | +/local.properties | ||
| 4 | +/.idea | ||
| 5 | +**/build | ||
| 6 | +/.hvigor | ||
| 7 | +.cxx | ||
| 8 | +/.clangd | ||
| 9 | +/.clang-format | ||
| 10 | +/.clang-tidy | ||
| 11 | +**/.test | ||
| 12 | +/.appanalyzer | ||
| @@ -0,0 +1,25 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +{ | ||
| 17 | + "app": { | ||
| 18 | + "bundleName": "com.samples.adjustlayout", | ||
| 19 | + "vendor": "example", | ||
| 20 | + "versionCode": 1000000, | ||
| 21 | + "versionName": "1.0.0", | ||
| 22 | + "icon": "$media:layered_image", | ||
| 23 | + "label": "$string:app_name" | ||
| 24 | + } | ||
| 25 | +} | ||
| @@ -0,0 +1,7 @@ | |||
| 1 | +{ | ||
| 2 | + "layered-image": | ||
| 3 | + { | ||
| 4 | + "background" : "$media:background", | ||
| 5 | + "foreground" : "$media:foreground" | ||
| 6 | + } | ||
| 7 | +} | ||
| @@ -0,0 +1,93 @@ | |||
| 1 | +# AdjustLayout简介 | ||
| 2 | + | ||
| 3 | +### 介绍 | ||
| 4 | + | ||
| 5 | +在多窗口场景下,开发者需要对主窗口和子窗口进行灵活的布局调整。本示例演示了如何对主窗口设置宽高比例限制,以及如何创建并管理子窗口,包括子窗口的显示/隐藏、拖拽大小、拖拽位置、设置窗口尺寸限制等功能。通过本示例,开发者可以掌握窗口布局调整的核心API使用方法。 | ||
| 6 | + | ||
| 7 | +### 效果预览 | ||
| 8 | + | ||
| 9 | +| 主窗口 | 子窗口 | 交互效果 | | ||
| 10 | +|----------------------------------------|----------------------------------------|----------------------------------------| | ||
| 11 | +|  |  |  | | ||
| 12 | + | ||
| 13 | +### 使用说明 | ||
| 14 | + | ||
| 15 | +1. 启动应用后,会显示主窗口,同时创建子窗口 | ||
| 16 | +2. 点击"开启主窗比例限制"按钮,主窗口将保持固定的宽高比例(1.5) | ||
| 17 | +3. 点击"显示子窗/隐藏子窗"按钮,可以控制子窗口的显示和隐藏 | ||
| 18 | +4. 点击"开启子窗拖拽大小/关闭子窗拖拽大小"按钮,可以控制子窗口是否允许拖拽调整大小 | ||
| 19 | +5. 点击"开启子窗拖拽位置/关闭子窗拖拽位置"按钮,可以控制子窗口是否允许拖拽移动位置 | ||
| 20 | +6. 在输入框中设置子窗口的 minWidth、maxWidth、minHeight、maxHeight,点击"设置子窗口 windowLimits"按钮应用尺寸限制 | ||
| 21 | + | ||
| 22 | +### 工程目录 | ||
| 23 | + | ||
| 24 | +``` | ||
| 25 | +entry/src/main/ets/ | ||
| 26 | +|---main | ||
| 27 | +| |---ets | ||
| 28 | +| | |---entryability | ||
| 29 | +| | | |---EntryAbility.ets // 创建主窗口和子窗口,设置窗口属性 | ||
| 30 | +| | |---entrybackupability | ||
| 31 | +| | |---pages | ||
| 32 | +| | | |---Index.ets // 主窗口页面,提供窗口控制功能 | ||
| 33 | +| | | |---SubWindowPage.ets // 子窗口页面,显示窗口状态信息 | ||
| 34 | +| |---resources | ||
| 35 | +| |---module.json5 | ||
| 36 | +|---ohosTest | ||
| 37 | +| |---ets | ||
| 38 | +| | |---test | ||
| 39 | +| | | |---Ability.test.ets // 自动化测试代码 | ||
| 40 | +| | | |---List.test.ets // 测试套件入口 | ||
| 41 | +``` | ||
| 42 | + | ||
| 43 | +### 具体实现 | ||
| 44 | + | ||
| 45 | +窗口布局调整的方法在EntryAbility和Index中实现,源码参考:[EntryAbility.ets](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkUISample/ArkUIWindowSamples/AdjustLayout/entry/src/main/ets/entryability/EntryAbility.ets) | ||
| 46 | + | ||
| 47 | +**EntryAbility.ets** | ||
| 48 | +- 使用 `getMainWindowSync` 获取应用主窗口 | ||
| 49 | +- 使用 `setWindowLimits` 设置主窗口的最小/最大宽高限制 | ||
| 50 | +- 使用 `windowStage.createSubWindow` 创建子窗口 | ||
| 51 | +- 使用 `resizeAsync` 和 `moveWindowToAsync` 设置子窗口初始尺寸和位置 | ||
| 52 | +- 使用 `enableDrag` 设置子窗口是否可拖拽 | ||
| 53 | +- 注册 `windowRectChange` 监听窗口尺寸变化 | ||
| 54 | + | ||
| 55 | +**Index.ets** | ||
| 56 | +- 使用 `setAspectRatio` 设置主窗口的宽高比例限制 | ||
| 57 | +- 使用 `resetAspectRatio` 取消宽高比例限制 | ||
| 58 | +- 使用 `showWindow` 和 `minimize` 控制子窗口显示/隐藏 | ||
| 59 | +- 使用 `setWindowLimits` 设置子窗口的尺寸限制 | ||
| 60 | +- 使用 `startMoving` 启动子窗口的拖拽移动 | ||
| 61 | + | ||
| 62 | +**SubWindowPage.ets** | ||
| 63 | +- 显示当前子窗口的尺寸信息 | ||
| 64 | +- 根据窗口尺寸动态切换布局样式(Compact Layout) | ||
| 65 | +- 处理子窗口标题栏的拖拽移动事件 | ||
| 66 | + | ||
| 67 | +### 相关权限 | ||
| 68 | + | ||
| 69 | +不涉及 | ||
| 70 | + | ||
| 71 | +### 依赖 | ||
| 72 | + | ||
| 73 | +不涉及 | ||
| 74 | + | ||
| 75 | +### 约束与限制 | ||
| 76 | + | ||
| 77 | +1.本示例仅支持标准系统上运行, 支持设备:华为手机、平板、2in1。 | ||
| 78 | + | ||
| 79 | +2.本示例为Stage模型,支持API Version 23及以上版本SDK。 | ||
| 80 | + | ||
| 81 | +3.本示例需要使用DevEco Studio 6.0.0 Release及以上版本才可编译运行。 | ||
| 82 | + | ||
| 83 | +### 下载 | ||
| 84 | + | ||
| 85 | +如需单独下载本工程,执行如下命令: | ||
| 86 | + | ||
| 87 | +``` | ||
| 88 | +git init | ||
| 89 | +git config core.sparsecheckout true | ||
| 90 | +echo code/DocsSample/ArkUISample/ArkUIWindowSamples/AdjustLayout > .git/info/sparse-checkout | ||
| 91 | +git remote add origin https://gitcode.com/openharmony/applications_app_samples.git | ||
| 92 | +git pull origin master | ||
| 93 | +``` | ||
| @@ -0,0 +1,43 @@ | |||
| 1 | +{ | ||
| 2 | + "app": { | ||
| 3 | + "signingConfigs": [], | ||
| 4 | + "products": [ | ||
| 5 | + { | ||
| 6 | + "name": "default", | ||
| 7 | + "signingConfig": "default", | ||
| 8 | + "targetSdkVersion": 20, | ||
| 9 | + "compileSdkVersion": 20, | ||
| 10 | + "compatibleSdkVersion": 20, | ||
| 11 | + "runtimeOS": "OpenHarmony", | ||
| 12 | + "buildOption": { | ||
| 13 | + "strictMode": { | ||
| 14 | + "caseSensitiveCheck": true, | ||
| 15 | + "useNormalizedOHMUrl": true | ||
| 16 | + } | ||
| 17 | + } | ||
| 18 | + } | ||
| 19 | + ], | ||
| 20 | + "buildModeSet": [ | ||
| 21 | + { | ||
| 22 | + "name": "debug" | ||
| 23 | + }, | ||
| 24 | + { | ||
| 25 | + "name": "release" | ||
| 26 | + } | ||
| 27 | + ] | ||
| 28 | + }, | ||
| 29 | + "modules": [ | ||
| 30 | + { | ||
| 31 | + "name": "entry", | ||
| 32 | + "srcPath": "./entry", | ||
| 33 | + "targets": [ | ||
| 34 | + { | ||
| 35 | + "name": "default", | ||
| 36 | + "applyToProducts": [ | ||
| 37 | + "default" | ||
| 38 | + ] | ||
| 39 | + } | ||
| 40 | + ] | ||
| 41 | + } | ||
| 42 | + ] | ||
| 43 | +} | ||
| @@ -0,0 +1,47 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2025 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +{ | ||
| 17 | + "files": [ | ||
| 18 | + "**/*.ets" | ||
| 19 | + ], | ||
| 20 | + "ignore": [ | ||
| 21 | + "**/src/ohosTest/**/*", | ||
| 22 | + "**/src/test/**/*", | ||
| 23 | + "**/src/mock/**/*", | ||
| 24 | + "**/node_modules/**/*", | ||
| 25 | + "**/oh_modules/**/*", | ||
| 26 | + "**/build/**/*", | ||
| 27 | + "**/.preview/**/*" | ||
| 28 | + ], | ||
| 29 | + "ruleSet": [ | ||
| 30 | + "plugin:@performance/recommended", | ||
| 31 | + "plugin:@typescript-eslint/recommended" | ||
| 32 | + ], | ||
| 33 | + "rules": { | ||
| 34 | + "@security/no-unsafe-aes": "error", | ||
| 35 | + "@security/no-unsafe-hash": "error", | ||
| 36 | + "@security/no-unsafe-mac": "warn", | ||
| 37 | + "@security/no-unsafe-dh": "error", | ||
| 38 | + "@security/no-unsafe-dsa": "error", | ||
| 39 | + "@security/no-unsafe-ecdsa": "error", | ||
| 40 | + "@security/no-unsafe-rsa-encrypt": "error", | ||
| 41 | + "@security/no-unsafe-rsa-sign": "error", | ||
| 42 | + "@security/no-unsafe-rsa-key": "error", | ||
| 43 | + "@security/no-unsafe-dsa-key": "error", | ||
| 44 | + "@security/no-unsafe-dh-key": "error", | ||
| 45 | + "@security/no-unsafe-3des": "error" | ||
| 46 | + } | ||
| 47 | +} | ||
| @@ -0,0 +1,6 @@ | |||
| 1 | +/node_modules | ||
| 2 | +/oh_modules | ||
| 3 | +/.preview | ||
| 4 | +/build | ||
| 5 | +/.cxx | ||
| 6 | +/.test | ||
| @@ -0,0 +1,48 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +{ | ||
| 17 | + "apiType": "stageMode", | ||
| 18 | + "buildOption": { | ||
| 19 | + "resOptions": { | ||
| 20 | + "copyCodeResource": { | ||
| 21 | + "enable": false | ||
| 22 | + } | ||
| 23 | + } | ||
| 24 | + }, | ||
| 25 | + "buildOptionSet": [ | ||
| 26 | + { | ||
| 27 | + "name": "release", | ||
| 28 | + "arkOptions": { | ||
| 29 | + "obfuscation": { | ||
| 30 | + "ruleOptions": { | ||
| 31 | + "enable": false, | ||
| 32 | + "files": [ | ||
| 33 | + "./obfuscation-rules.txt" | ||
| 34 | + ] | ||
| 35 | + } | ||
| 36 | + } | ||
| 37 | + } | ||
| 38 | + }, | ||
| 39 | + ], | ||
| 40 | + "targets": [ | ||
| 41 | + { | ||
| 42 | + "name": "default" | ||
| 43 | + }, | ||
| 44 | + { | ||
| 45 | + "name": "ohosTest", | ||
| 46 | + } | ||
| 47 | + ] | ||
| 48 | +} | ||
| @@ -0,0 +1,21 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; | ||
| 17 | + | ||
| 18 | +export default { | ||
| 19 | + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ | ||
| 20 | + plugins: [] /* Custom plugin to extend the functionality of Hvigor. */ | ||
| 21 | +} | ||
| @@ -0,0 +1,38 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +# Define project specific obfuscation rules here. | ||
| 17 | +# You can include the obfuscation configuration files in the current module's build-profile.json5. | ||
| 18 | +# | ||
| 19 | +# For more details, see | ||
| 20 | +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/source-obfuscation | ||
| 21 | + | ||
| 22 | +# Obfuscation options: | ||
| 23 | +# -disable-obfuscation: disable all obfuscations | ||
| 24 | +# -enable-property-obfuscation: obfuscate the property names | ||
| 25 | +# -enable-toplevel-obfuscation: obfuscate the names in the global scope | ||
| 26 | +# -compact: remove unnecessary blank spaces and all line feeds | ||
| 27 | +# -remove-log: remove all console.* statements | ||
| 28 | +# -print-namecache: print the name cache that contains the mapping from the old names to new names | ||
| 29 | +# -apply-namecache: reuse the given cache file | ||
| 30 | + | ||
| 31 | +# Keep options: | ||
| 32 | +# -keep-property-name: specifies property names that you want to keep | ||
| 33 | +# -keep-global-name: specifies names that you want to keep in the global scope | ||
| 34 | + | ||
| 35 | +-enable-property-obfuscation | ||
| 36 | +-enable-toplevel-obfuscation | ||
| 37 | +-enable-filename-obfuscation | ||
| 38 | +-enable-export-obfuscation | ||
| @@ -0,0 +1,25 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +{ | ||
| 17 | + "name": "entry", | ||
| 18 | + "version": "1.0.0", | ||
| 19 | + "description": "Please describe the basic information.", | ||
| 20 | + "main": "", | ||
| 21 | + "author": "", | ||
| 22 | + "license": "", | ||
| 23 | + "dependencies": {} | ||
| 24 | +} | ||
| 25 | + | ||
| @@ -0,0 +1,207 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +// [Start sub_move_resize] | ||
| 17 | +// EntryAbility.ets | ||
| 18 | +import { UIAbility } from '@kit.AbilityKit'; | ||
| 19 | +import { window, display } from '@kit.ArkUI'; | ||
| 20 | +import { BusinessError } from '@kit.BasicServicesKit'; | ||
| 21 | +import hilog from '@ohos.hilog'; | ||
| 22 | + | ||
| 23 | +const DOMAIN = 0x0000; | ||
| 24 | +const TAG = 'Sample_AdjustLayout'; | ||
| 25 | + | ||
| 26 | +// [StartExclude sub_move_resize] | ||
| 27 | +export default class MainAbility extends UIAbility { | ||
| 28 | + private mainWindow?: window.Window; | ||
| 29 | + private subWindow?: window.Window; | ||
| 30 | + | ||
| 31 | + onWindowStageCreate(windowStage: window.WindowStage): void { | ||
| 32 | + hilog.info(DOMAIN, TAG, 'onWindowStageCreate start'); | ||
| 33 | + | ||
| 34 | + this.mainWindow = windowStage.getMainWindowSync(); | ||
| 35 | + AppStorage.setOrCreate<window.Window>('MAIN_WINDOW', this.mainWindow); | ||
| 36 | + | ||
| 37 | + // 初始化参数 | ||
| 38 | + this.initAppStorageState(); | ||
| 39 | + | ||
| 40 | + // 加载主窗口页面内容 | ||
| 41 | + windowStage.loadContent('pages/Index', (err: BusinessError) => { | ||
| 42 | + if (err && err.code) { | ||
| 43 | + hilog.error(DOMAIN, TAG, `load Index failed: ${err.code}, ${err.message}`); | ||
| 44 | + return; | ||
| 45 | + } | ||
| 46 | + hilog.info(DOMAIN, TAG, 'load Index callback success'); | ||
| 47 | + }); | ||
| 48 | + | ||
| 49 | + // 步骤2:注册主窗口尺寸变化监听 | ||
| 50 | + this.mainWindow.on('windowRectChange', (data: window.RectChangeOptions) => { | ||
| 51 | + this.onWindowRectChange(data); | ||
| 52 | + }); | ||
| 53 | + | ||
| 54 | + // 设置主窗口尺寸限制 | ||
| 55 | + this.setMainWindowLimitsSafe(); | ||
| 56 | + | ||
| 57 | + // 步骤4:创建子窗口,为子窗口指定初始位置大小 | ||
| 58 | + this.createSubWindow(windowStage); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + 初始化应用存储状态 | ||
| 63 | + 设置窗口相关的默认配置参数 | ||
| 64 | + */ | ||
| 65 | + private initAppStorageState(): void { | ||
| 66 | + AppStorage.setOrCreate('WINDOW_ASPECT_RATIO_ENABLED', false); | ||
| 67 | + AppStorage.setOrCreate('SUB_WINDOW_VISIBILITY_ENABLED', false); | ||
| 68 | + AppStorage.setOrCreate('SUB_WINDOW_RESIZE_ENABLED', true); | ||
| 69 | + AppStorage.setOrCreate('SUB_WINDOW_MOVE_ENABLED', true); | ||
| 70 | + AppStorage.setOrCreate('WINDOW_SIZE', { | ||
| 71 | + width: 600, | ||
| 72 | + height: 300 | ||
| 73 | + }); | ||
| 74 | + } | ||
| 75 | + /** | ||
| 76 | + 注册主窗口尺寸变化监听 | ||
| 77 | + 监听主窗口rect变化事件并记录日志 | ||
| 78 | + */ | ||
| 79 | + private onWindowRectChange(options: window.RectChangeOptions): void { | ||
| 80 | + // ... | ||
| 81 | + hilog.info(DOMAIN, TAG, `main rect change: ${JSON.stringify(options)}`); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + /** | ||
| 85 | + 设置主窗口安全尺寸限制 | ||
| 86 | + 根据屏幕尺寸设置主窗口的最小和最大宽高限制 | ||
| 87 | + */ | ||
| 88 | + private setMainWindowLimitsSafe(): void { | ||
| 89 | + if (!this.mainWindow) { | ||
| 90 | + return; | ||
| 91 | + } | ||
| 92 | + let screenWidth = 0; | ||
| 93 | + let screenHeight = 0; | ||
| 94 | + | ||
| 95 | + try { | ||
| 96 | + const defaultDisplay = display.getDefaultDisplaySync(); | ||
| 97 | + screenWidth = defaultDisplay.width; | ||
| 98 | + screenHeight = defaultDisplay.height; | ||
| 99 | + } catch (err) { | ||
| 100 | + hilog.error(DOMAIN, TAG, `get display failed: ${JSON.stringify(err)}`); | ||
| 101 | + return; | ||
| 102 | + } | ||
| 103 | + let windowLimits: window.WindowLimits = { | ||
| 104 | + minWidth: Math.floor(screenWidth * 0.5), | ||
| 105 | + minHeight: Math.floor(screenHeight * 0.5), | ||
| 106 | + maxWidth: Math.floor(screenWidth * 1.2), | ||
| 107 | + maxHeight: Math.floor(screenHeight * 1.2) | ||
| 108 | + } | ||
| 109 | + let promise = this.mainWindow.setWindowLimits(windowLimits); | ||
| 110 | + promise.then(() => { | ||
| 111 | + hilog.info(DOMAIN, TAG, 'set main window limits success'); | ||
| 112 | + }).catch((err: Error) => { | ||
| 113 | + const businessError = err as BusinessError; | ||
| 114 | + hilog.error(DOMAIN, TAG, `set main window limits failed: ${JSON.stringify(businessError)}`); | ||
| 115 | + }); | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | +// [EndExclude sub_move_resize] | ||
| 119 | + /** | ||
| 120 | + * 创建子窗口并调整位置大小 | ||
| 121 | + */ | ||
| 122 | + private createSubWindow(windowStage: window.WindowStage): void { | ||
| 123 | + if (!this.mainWindow) { | ||
| 124 | + return; | ||
| 125 | + } | ||
| 126 | + const mainRect = this.mainWindow.getWindowProperties().windowRect; | ||
| 127 | + const subWidth = 600; | ||
| 128 | + const subHeight = 300; | ||
| 129 | + const margin = 24; | ||
| 130 | + | ||
| 131 | + // 计算子窗口位置:放在主窗口右下角 | ||
| 132 | + const subX = Math.max( | ||
| 133 | + mainRect.left + margin, | ||
| 134 | + mainRect.left + mainRect.width - subWidth - margin | ||
| 135 | + ); | ||
| 136 | + | ||
| 137 | + const subY = Math.max( | ||
| 138 | + mainRect.top + margin, | ||
| 139 | + mainRect.top + mainRect.height - subHeight - margin | ||
| 140 | + ); | ||
| 141 | + | ||
| 142 | + // 创建子窗口 | ||
| 143 | + windowStage.createSubWindow('DemoSubWindow').then((subWindow: window.Window) => { | ||
| 144 | + hilog.info(DOMAIN, TAG, 'createSubWindow success'); | ||
| 145 | + | ||
| 146 | + this.subWindow = subWindow; | ||
| 147 | + AppStorage.setOrCreate<window.Window>('SUB_WINDOW', subWindow); | ||
| 148 | + AppStorage.setOrCreate<window.Size>('WINDOW_SIZE', { | ||
| 149 | + width: subWidth, | ||
| 150 | + height: subHeight | ||
| 151 | + }); | ||
| 152 | + | ||
| 153 | + // 注册子窗口尺寸变化监听 | ||
| 154 | + this.registerSubWindowRectChange(subWindow); | ||
| 155 | + // 设置子窗口尺寸 | ||
| 156 | + this.subWindow!.resizeAsync(subWidth, subHeight); | ||
| 157 | + // 移动子窗口位置 | ||
| 158 | + this.subWindow!.moveWindowToAsync(subX, subY); | ||
| 159 | + // 子窗口设置为默认可拖拽 | ||
| 160 | + this.subWindow!.enableDrag(true); | ||
| 161 | + // 加载子窗口内容页面 | ||
| 162 | + this.loadSubWindowContent(this.subWindow!); | ||
| 163 | + }).catch((err: Error) => { | ||
| 164 | + const businessError = err as BusinessError; | ||
| 165 | + hilog.error(DOMAIN, TAG, `createSubWindow failed: ${JSON.stringify(businessError)}`); | ||
| 166 | + }); | ||
| 167 | + } | ||
| 168 | +// [End sub_move_resize] | ||
| 169 | + | ||
| 170 | + /** | ||
| 171 | + 注册子窗口尺寸变化监听 | ||
| 172 | + 监听子窗口rect变化事件并更新应用存储中的窗口尺寸信息 | ||
| 173 | + */ | ||
| 174 | + private registerSubWindowRectChange(subWindow: window.Window): void { | ||
| 175 | + subWindow.on('windowRectChange', (data: window.RectChangeOptions) => { | ||
| 176 | + const rect = data.rect; | ||
| 177 | + AppStorage.setOrCreate('WINDOW_SIZE', { | ||
| 178 | + width: rect.width, | ||
| 179 | + height: rect.height | ||
| 180 | + }); | ||
| 181 | + hilog.info(DOMAIN, TAG, `sub rect change: ${JSON.stringify(data)}`); | ||
| 182 | + }); | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + /** | ||
| 186 | + 加载子窗口内容页面 | ||
| 187 | + 设置子窗口的UI内容为SubWindowPage页面 | ||
| 188 | + */ | ||
| 189 | + private loadSubWindowContent(subWindow: window.Window): void { | ||
| 190 | + subWindow.setUIContent('pages/SubWindowPage').then(() => { | ||
| 191 | + console.info('Succeeded in loading the content.'); | ||
| 192 | + }).catch((err: Error) => { | ||
| 193 | + const businessError = err as BusinessError; | ||
| 194 | + console.error(`Failed to load the content. Cause code: ${businessError.code}, message: ${businessError.message}`); | ||
| 195 | + }); | ||
| 196 | + } | ||
| 197 | + onWindowStageDestroy(): void { | ||
| 198 | + try { | ||
| 199 | + this.mainWindow?.off('windowRectChange'); | ||
| 200 | + this.subWindow?.off('windowRectChange'); | ||
| 201 | + this.subWindow?.destroyWindow(); | ||
| 202 | + } catch (err) { | ||
| 203 | + const businessError = err as BusinessError; | ||
| 204 | + hilog.error(DOMAIN, TAG, `destroy window failed: ${JSON.stringify(businessError)}`); | ||
| 205 | + } | ||
| 206 | + } | ||
| 207 | +} | ||
| @@ -0,0 +1,31 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +import { hilog } from '@kit.PerformanceAnalysisKit'; | ||
| 17 | +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; | ||
| 18 | + | ||
| 19 | +const DOMAIN = 0x0000; | ||
| 20 | + | ||
| 21 | +export default class EntryBackupAbility extends BackupExtensionAbility { | ||
| 22 | + async onBackup() { | ||
| 23 | + hilog.info(DOMAIN, 'Sample_AdjustLayout', 'onBackup ok'); | ||
| 24 | + await Promise.resolve(); | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + async onRestore(bundleVersion: BundleVersion) { | ||
| 28 | + hilog.info(DOMAIN, 'Sample_AdjustLayout', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); | ||
| 29 | + await Promise.resolve(); | ||
| 30 | + } | ||
| 31 | +} | ||
Acode/DocsSample/ArkUISample/ArkUIWindowSamples/AdjustLayout/entry/src/main/ets/pages/Index.ets+344-0
| @@ -0,0 +1,344 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +// [Start aspectRatio] | ||
| 17 | +// [Start enableDrag] | ||
| 18 | +// Index.ets | ||
| 19 | +import { window } from '@kit.ArkUI'; | ||
| 20 | +import { BusinessError } from '@kit.BasicServicesKit'; | ||
| 21 | +import hilog from '@ohos.hilog'; | ||
| 22 | + | ||
| 23 | +const DOMAIN = 0x0000; | ||
| 24 | +const TAG = 'IndexPage'; | ||
| 25 | + | ||
| 26 | +// [StartExclude aspectRatio] | ||
| 27 | +// [StartExclude enableDrag] | ||
| 28 | +@Entry | ||
| 29 | +@Component | ||
| 30 | +struct Index { | ||
| 31 | + @StorageLink('WINDOW_ASPECT_RATIO_ENABLED') aspectRatioEnabled: boolean = false; | ||
| 32 | + @StorageLink('SUB_WINDOW_VISIBILITY_ENABLED')showSubWindowEnabled: boolean = false; | ||
| 33 | + @StorageLink('SUB_WINDOW_RESIZE_ENABLED') resizeEnabled: boolean = true; | ||
| 34 | + @StorageLink('SUB_WINDOW_MOVE_ENABLED') moveEnabled: boolean = true; | ||
| 35 | + | ||
| 36 | + @State minWidthText: string = '200'; | ||
| 37 | + @State maxWidthText: string = '1000'; | ||
| 38 | + @State minHeightText: string = '100'; | ||
| 39 | + @State maxHeightText: string = '500'; | ||
| 40 | + @State resultText: string = '等待操作'; | ||
| 41 | + | ||
| 42 | + private getMainWindow(): window.Window | undefined { | ||
| 43 | + return AppStorage.get<window.Window>('MAIN_WINDOW'); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + private getSubWindow(): window.Window | undefined { | ||
| 47 | + return AppStorage.get<window.Window>('SUB_WINDOW'); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + /** | ||
| 51 | + | ||
| 52 | + 控制子窗口显示隐藏 | ||
| 53 | + */ | ||
| 54 | + private setSubWindowVisibilityEnabled(enabled: boolean): void { | ||
| 55 | + const subWindow = this.getSubWindow(); | ||
| 56 | + if (!subWindow) { | ||
| 57 | + this.resultText = '子窗口未创建'; | ||
| 58 | + return; | ||
| 59 | + } | ||
| 60 | + if (enabled) { | ||
| 61 | + subWindow.showWindow().then(() => { | ||
| 62 | + AppStorage.setOrCreate('SUB_WINDOW_VISIBILITY_ENABLED', enabled); | ||
| 63 | + this.resultText = '已显示子窗口'; | ||
| 64 | + console.info('drr:Succeeded in show subwindow.'); | ||
| 65 | + }).catch((err: Error) => { | ||
| 66 | + const businessError = err as BusinessError; | ||
| 67 | + this.resultText = `显示子窗口失败: ${JSON.stringify(businessError)}`; | ||
| 68 | + hilog.error(DOMAIN, TAG, `show subwindow failed: ${JSON.stringify(businessError)}`); | ||
| 69 | + }); | ||
| 70 | + } else { | ||
| 71 | + subWindow.minimize().then(() => { | ||
| 72 | + AppStorage.setOrCreate('SUB_WINDOW_VISIBILITY_ENABLED', enabled); | ||
| 73 | + this.resultText = '已隐藏子窗口'; | ||
| 74 | + console.info('drr:Succeeded in hide subwindow.'); | ||
| 75 | + }).catch((err: Error) => { | ||
| 76 | + const businessError = err as BusinessError; | ||
| 77 | + this.resultText = `隐藏子窗口失败: ${JSON.stringify(businessError)}`; | ||
| 78 | + hilog.error(DOMAIN, TAG, `hide subwindow failed: ${JSON.stringify(businessError)}`); | ||
| 79 | + }); | ||
| 80 | + } | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | +// [EndExclude enableDrag] | ||
| 84 | + /** | ||
| 85 | + 通过enableDrag控制子窗口的拖拽缩放功能 | ||
| 86 | + */ | ||
| 87 | + private setSubWindowResizeEnabled(enabled: boolean): void { | ||
| 88 | + const subWindow = this.getSubWindow(); | ||
| 89 | + if (!subWindow) { | ||
| 90 | + this.resultText = '子窗口未创建'; | ||
| 91 | + return; | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + subWindow.enableDrag(enabled) | ||
| 95 | + .then(() => { | ||
| 96 | + AppStorage.setOrCreate<boolean>('SUB_WINDOW_RESIZE_ENABLED', enabled); | ||
| 97 | + this.resultText = enabled ? '已开启子窗拖拽大小' : '已关闭子窗拖拽大小'; | ||
| 98 | + }) | ||
| 99 | + .catch((err: Error) => { | ||
| 100 | + const businessError = err as BusinessError; | ||
| 101 | + this.resultText = `设置拖拽大小失败: ${JSON.stringify(businessError)}`; | ||
| 102 | + hilog.error(DOMAIN, TAG, `set resize drag failed: ${JSON.stringify(businessError)}`); | ||
| 103 | + }); | ||
| 104 | + } | ||
| 105 | +// [End enableDrag] | ||
| 106 | + | ||
| 107 | + /** | ||
| 108 | + 通过startMoving控制子窗口的拖拽移动功能 | ||
| 109 | + */ | ||
| 110 | + private setSubWindowMoveEnabled(enabled: boolean): void { | ||
| 111 | + AppStorage.setOrCreate('SUB_WINDOW_MOVE_ENABLED', enabled); | ||
| 112 | + this.resultText = enabled ? '已开启子窗拖拽位置' : '已关闭子窗拖拽位置'; | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | +// [EndExclude aspectRatio] | ||
| 116 | + /** | ||
| 117 | + 步骤2:为主窗口设置指定宽高比例 | ||
| 118 | + */ | ||
| 119 | + private setWindowAspectRatioEnabled(enabled: boolean): void { | ||
| 120 | + const mainWindow = this.getMainWindow(); | ||
| 121 | + if (!mainWindow) { | ||
| 122 | + this.resultText = '找不到主窗口'; | ||
| 123 | + return; | ||
| 124 | + } | ||
| 125 | + if (enabled) { | ||
| 126 | + const ratio = 1.5; | ||
| 127 | + // 设置可布局内容宽高比限制 | ||
| 128 | + mainWindow.setAspectRatio(ratio) | ||
| 129 | + .then(() => { | ||
| 130 | + AppStorage.setOrCreate('WINDOW_ASPECT_RATIO_ENABLED', enabled); | ||
| 131 | + this.resultText = '已开启比例限制'; | ||
| 132 | + }) | ||
| 133 | + .catch((err: Error) => { | ||
| 134 | + const businessError = err as BusinessError; | ||
| 135 | + this.resultText = `设置比例限制失败: ${JSON.stringify(businessError)}`; | ||
| 136 | + hilog.error(DOMAIN, TAG, `set aspect ratio failed: ${JSON.stringify(businessError)}`); | ||
| 137 | + }); | ||
| 138 | + } else { | ||
| 139 | + // 取消可布局内容宽高比限制 | ||
| 140 | + mainWindow.resetAspectRatio() | ||
| 141 | + .then(() => { | ||
| 142 | + AppStorage.setOrCreate('WINDOW_ASPECT_RATIO_ENABLED', enabled); | ||
| 143 | + this.resultText = '已关闭比例限制'; | ||
| 144 | + }) | ||
| 145 | + .catch((err: Error) => { | ||
| 146 | + const businessError = err as BusinessError; | ||
| 147 | + this.resultText = `重置比例限制失败: ${JSON.stringify(businessError)}`; | ||
| 148 | + hilog.error(DOMAIN, TAG, `reset aspect ratio failed: ${JSON.stringify(businessError)}`); | ||
| 149 | + }); | ||
| 150 | + } | ||
| 151 | + } | ||
| 152 | +// [End aspectRatio] | ||
| 153 | + | ||
| 154 | + private parsePositiveInt(value: string): number | undefined { | ||
| 155 | + const trimValue = value.trim(); | ||
| 156 | + | ||
| 157 | + if (!/^\d+$/.test(trimValue)) { | ||
| 158 | + return undefined; | ||
| 159 | + } | ||
| 160 | + const result = Number.parseInt(trimValue); | ||
| 161 | + | ||
| 162 | + if (!Number.isFinite(result) || result <= 0) { | ||
| 163 | + return undefined; | ||
| 164 | + } | ||
| 165 | + return result; | ||
| 166 | + } | ||
| 167 | + | ||
| 168 | + /** | ||
| 169 | + 为子窗口设置windowLimits | ||
| 170 | + */ | ||
| 171 | + private setSubWindowLimits(): void { | ||
| 172 | + const subWindow = this.getSubWindow(); | ||
| 173 | + if (!subWindow) { | ||
| 174 | + this.resultText = '子窗口未创建'; | ||
| 175 | + return; | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | + const minWidth = this.parsePositiveInt(this.minWidthText); | ||
| 179 | + const maxWidth = this.parsePositiveInt(this.maxWidthText); | ||
| 180 | + const minHeight = this.parsePositiveInt(this.minHeightText); | ||
| 181 | + const maxHeight = this.parsePositiveInt(this.maxHeightText); | ||
| 182 | + if ( | ||
| 183 | + minWidth === undefined || | ||
| 184 | + maxWidth === undefined || | ||
| 185 | + minHeight === undefined || | ||
| 186 | + maxHeight === undefined | ||
| 187 | + ) { | ||
| 188 | + this.resultText = '输入非法:windowLimits 必须是正整数'; | ||
| 189 | + return; | ||
| 190 | + } | ||
| 191 | + if (minWidth > maxWidth) { | ||
| 192 | + this.resultText = '输入非法:minWidth 不能大于 maxWidth'; | ||
| 193 | + return; | ||
| 194 | + } | ||
| 195 | + if (minHeight > maxHeight) { | ||
| 196 | + this.resultText = '输入非法:minHeight 不能大于 maxHeight'; | ||
| 197 | + return; | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + subWindow.setWindowLimits({ | ||
| 201 | + minWidth: minWidth, | ||
| 202 | + maxWidth: maxWidth, | ||
| 203 | + minHeight: minHeight, | ||
| 204 | + maxHeight: maxHeight | ||
| 205 | + }) | ||
| 206 | + .then(() => { | ||
| 207 | + this.resultText = | ||
| 208 | + `设置成功:width[${minWidth}, ${maxWidth}], height[${minHeight}, ${maxHeight}]`; | ||
| 209 | + }) | ||
| 210 | + .catch((err: Error) => { | ||
| 211 | + const businessError = err as BusinessError; | ||
| 212 | + this.resultText = `设置 windowLimits 失败: ${JSON.stringify(businessError)}`; | ||
| 213 | + hilog.error(DOMAIN, TAG, `set windowLimits failed: ${JSON.stringify(businessError)}`); | ||
| 214 | + }); | ||
| 215 | + } | ||
| 216 | + | ||
| 217 | + build() { | ||
| 218 | + Scroll() { | ||
| 219 | + Column() { | ||
| 220 | + Text('Main Window') | ||
| 221 | + .fontSize(28) | ||
| 222 | + .fontWeight(FontWeight.Bold) | ||
| 223 | + | ||
| 224 | + Button(this.aspectRatioEnabled ? '关闭主窗比例限制' : '开启主窗比例限制') | ||
| 225 | + .width('80%') | ||
| 226 | + .height(40) | ||
| 227 | + .onClick(() => { | ||
| 228 | + this.setWindowAspectRatioEnabled(!this.aspectRatioEnabled); | ||
| 229 | + }) | ||
| 230 | + | ||
| 231 | + Button(this.showSubWindowEnabled ? '隐藏子窗' : '显示子窗') | ||
| 232 | + .width('80%') | ||
| 233 | + .height(40) | ||
| 234 | + .margin({ top: 12 }) | ||
| 235 | + .onClick(() => { | ||
| 236 | + this.setSubWindowVisibilityEnabled(!this.showSubWindowEnabled); | ||
| 237 | + }) | ||
| 238 | + | ||
| 239 | + Button(this.resizeEnabled ? '关闭子窗拖拽大小' : '开启子窗拖拽大小') | ||
| 240 | + .width('80%') | ||
| 241 | + .height(40) | ||
| 242 | + .margin({ top: 12 }) | ||
| 243 | + .onClick(() => { | ||
| 244 | + this.setSubWindowResizeEnabled(!this.resizeEnabled); | ||
| 245 | + }) | ||
| 246 | + | ||
| 247 | + Button(this.moveEnabled ? '关闭子窗拖拽位置' : '开启子窗拖拽位置') | ||
| 248 | + .width('80%') | ||
| 249 | + .height(40) | ||
| 250 | + .margin({ top: 12 }) | ||
| 251 | + .onClick(() => { | ||
| 252 | + this.setSubWindowMoveEnabled(!this.moveEnabled); | ||
| 253 | + }) | ||
| 254 | + | ||
| 255 | + Column() { | ||
| 256 | + Text('子窗口 windowLimits') | ||
| 257 | + .fontSize(18) | ||
| 258 | + .fontWeight(FontWeight.Bold) | ||
| 259 | + .margin({ bottom: 12 }) | ||
| 260 | + | ||
| 261 | + Row() { | ||
| 262 | + Text('minWidth') | ||
| 263 | + .width(90) | ||
| 264 | + .fontSize(14) | ||
| 265 | + | ||
| 266 | + TextInput({ text: this.minWidthText }) | ||
| 267 | + .type(InputType.Number) | ||
| 268 | + .width(160) | ||
| 269 | + .onChange((value: string) => { | ||
| 270 | + this.minWidthText = value; | ||
| 271 | + }) | ||
| 272 | + | ||
| 273 | + Text('maxWidth') | ||
| 274 | + .width(90) | ||
| 275 | + .fontSize(14) | ||
| 276 | + | ||
| 277 | + TextInput({ text: this.maxWidthText }) | ||
| 278 | + .type(InputType.Number) | ||
| 279 | + .width(160) | ||
| 280 | + .onChange((value: string) => { | ||
| 281 | + this.maxWidthText = value; | ||
| 282 | + }) | ||
| 283 | + } | ||
| 284 | + .margin({ top: 8 }) | ||
| 285 | + | ||
| 286 | + Row() { | ||
| 287 | + Text('minHeight') | ||
| 288 | + .width(90) | ||
| 289 | + .fontSize(14) | ||
| 290 | + | ||
| 291 | + TextInput({ text: this.minHeightText }) | ||
| 292 | + .type(InputType.Number) | ||
| 293 | + .width(160) | ||
| 294 | + .onChange((value: string) => { | ||
| 295 | + this.minHeightText = value; | ||
| 296 | + }) | ||
| 297 | + | ||
| 298 | + Text('maxHeight') | ||
| 299 | + .width(90) | ||
| 300 | + .fontSize(14) | ||
| 301 | + | ||
| 302 | + TextInput({ text: this.maxHeightText }) | ||
| 303 | + .type(InputType.Number) | ||
| 304 | + .width(160) | ||
| 305 | + .onChange((value: string) => { | ||
| 306 | + this.maxHeightText = value; | ||
| 307 | + }) | ||
| 308 | + } | ||
| 309 | + .margin({ top: 8 }) | ||
| 310 | + | ||
| 311 | + Button('设置子窗口 windowLimits') | ||
| 312 | + .width('80%') | ||
| 313 | + .height(44) | ||
| 314 | + .margin({ top: 16 }) | ||
| 315 | + .onClick(() => { | ||
| 316 | + this.setSubWindowLimits(); | ||
| 317 | + }) | ||
| 318 | + } | ||
| 319 | + .width('90%') | ||
| 320 | + .padding(16) | ||
| 321 | + .margin({ top: 24 }) | ||
| 322 | + .border({ | ||
| 323 | + width: 1, | ||
| 324 | + color: '#D0D7DE', | ||
| 325 | + radius: 12 | ||
| 326 | + }) | ||
| 327 | + .backgroundColor('#FFFFFF') | ||
| 328 | + | ||
| 329 | + Text(this.resultText) | ||
| 330 | + .id('resultText') | ||
| 331 | + .fontSize(14) | ||
| 332 | + .fontColor('#333333') | ||
| 333 | + .margin({ top: 20 }) | ||
| 334 | + .width('90%') | ||
| 335 | + } | ||
| 336 | + .width('100%') | ||
| 337 | + .padding({ top: 40, bottom: 40 }) | ||
| 338 | + .alignItems(HorizontalAlign.Center) | ||
| 339 | + } | ||
| 340 | + .width('100%') | ||
| 341 | + .height('100%') | ||
| 342 | + .backgroundColor('#F5F7FA') | ||
| 343 | + } | ||
| 344 | +} | ||
| @@ -0,0 +1,147 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +// [Start startMoving] | ||
| 17 | +import { window } from '@kit.ArkUI'; | ||
| 18 | +import { BusinessError } from '@kit.BasicServicesKit'; | ||
| 19 | +import hilog from '@ohos.hilog'; | ||
| 20 | + | ||
| 21 | +const DOMAIN = 0x0000; | ||
| 22 | +const TAG = 'Sample_AdjustLayout'; | ||
| 23 | + | ||
| 24 | +@Entry | ||
| 25 | +@Component | ||
| 26 | +struct SubWindowPage { | ||
| 27 | + @StorageProp('WINDOW_SIZE') windowSize: window.Size = { | ||
| 28 | + width: 600, | ||
| 29 | + height: 300 | ||
| 30 | + }; | ||
| 31 | + | ||
| 32 | + @StorageLink('SUB_WINDOW_VISIBILITY_ENABLED') showSubWindowEnabled: boolean = false; | ||
| 33 | + @StorageProp('SUB_WINDOW_RESIZE_ENABLED') resizeEnabled: boolean = true; | ||
| 34 | + @StorageProp('SUB_WINDOW_MOVE_ENABLED') moveEnabled: boolean = true; | ||
| 35 | + | ||
| 36 | + private isCompact(): boolean { | ||
| 37 | + return this.windowSize.width < 500 || this.windowSize.height < 240; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + 通过startMoving为子窗口接入系统拖拽移动 | ||
| 42 | + */ | ||
| 43 | + private startMoveSubWindow(): void { | ||
| 44 | + const subWindow = AppStorage.get<window.Window>('SUB_WINDOW'); | ||
| 45 | + if (!subWindow) { | ||
| 46 | + return; | ||
| 47 | + } | ||
| 48 | + subWindow.startMoving().then(() => { | ||
| 49 | + hilog.info(DOMAIN, TAG, 'startMoving success'); | ||
| 50 | + }).catch((err: Error) => { | ||
| 51 | + const businessError = err as BusinessError; | ||
| 52 | + hilog.error(DOMAIN, TAG, `startMoving failed: ${JSON.stringify(businessError)}`); | ||
| 53 | + }); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + build() { | ||
| 57 | + Column() { | ||
| 58 | + Row() { | ||
| 59 | + Text(this.moveEnabled ? 'Sub Window - Move Enabled' : 'Sub Window - Move Disabled') | ||
| 60 | + .fontSize(14) | ||
| 61 | + .fontWeight(FontWeight.Bold) | ||
| 62 | + .fontColor(Color.White) | ||
| 63 | + Blank() | ||
| 64 | + Text(`${this.windowSize.width} × ${this.windowSize.height}`) | ||
| 65 | + .fontSize(12) | ||
| 66 | + .fontColor('#DDE7FF') | ||
| 67 | + } | ||
| 68 | + .height(44) | ||
| 69 | + .width('100%') | ||
| 70 | + .padding({ left: 16, right: 16 }) | ||
| 71 | + .backgroundColor(this.moveEnabled ? '#2F5BEA' : '#666666') | ||
| 72 | + .onTouch((event: TouchEvent) => { | ||
| 73 | + if (event.type === TouchType.Down && this.moveEnabled) { | ||
| 74 | + this.startMoveSubWindow(); | ||
| 75 | + } | ||
| 76 | + }) | ||
| 77 | + | ||
| 78 | + Column() { | ||
| 79 | + Text('当前子窗口大小') | ||
| 80 | + .fontSize(16) | ||
| 81 | + .fontWeight(FontWeight.Bold) | ||
| 82 | + | ||
| 83 | + Text(`width = ${this.windowSize.width}px`) | ||
| 84 | + .fontSize(14) | ||
| 85 | + .margin({ top: 8 }) | ||
| 86 | + | ||
| 87 | + Text(`height = ${this.windowSize.height}px`) | ||
| 88 | + .fontSize(14) | ||
| 89 | + .margin({ top: 4 }) | ||
| 90 | + | ||
| 91 | + Text(this.resizeEnabled ? '大小拖拽:开启' : '大小拖拽:关闭') | ||
| 92 | + .fontSize(13) | ||
| 93 | + .fontColor('#666666') | ||
| 94 | + .margin({ top: 12 }) | ||
| 95 | + | ||
| 96 | + Text(this.moveEnabled ? '位置拖拽:开启' : '位置拖拽:关闭') | ||
| 97 | + .fontSize(13) | ||
| 98 | + .fontColor('#666666') | ||
| 99 | + .margin({ top: 4 }) | ||
| 100 | + | ||
| 101 | + if (this.isCompact()) { | ||
| 102 | + Text('Compact Layout') | ||
| 103 | + .fontSize(18) | ||
| 104 | + .fontWeight(FontWeight.Bold) | ||
| 105 | + .margin({ top: 16 }) | ||
| 106 | + } else { | ||
| 107 | + Row() { | ||
| 108 | + Column() { | ||
| 109 | + Text('Left Panel') | ||
| 110 | + .fontSize(18) | ||
| 111 | + .fontWeight(FontWeight.Bold) | ||
| 112 | + } | ||
| 113 | + .width('45%') | ||
| 114 | + .height(80) | ||
| 115 | + .justifyContent(FlexAlign.Center) | ||
| 116 | + .backgroundColor('#EEF3FF') | ||
| 117 | + | ||
| 118 | + Column() { | ||
| 119 | + Text('Right Panel') | ||
| 120 | + .fontSize(18) | ||
| 121 | + .fontWeight(FontWeight.Bold) | ||
| 122 | + } | ||
| 123 | + .width('55%') | ||
| 124 | + .height(80) | ||
| 125 | + .justifyContent(FlexAlign.Center) | ||
| 126 | + .backgroundColor('#FFFFFF') | ||
| 127 | + } | ||
| 128 | + .width('100%') | ||
| 129 | + .margin({ top: 16 }) | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + .width('100%') | ||
| 133 | + .layoutWeight(1) | ||
| 134 | + .justifyContent(FlexAlign.Center) | ||
| 135 | + .alignItems(HorizontalAlign.Center) | ||
| 136 | + } | ||
| 137 | + .width('100%') | ||
| 138 | + .height('100%') | ||
| 139 | + .backgroundColor('#FFFFFF') | ||
| 140 | + .border({ | ||
| 141 | + width: 4, | ||
| 142 | + color: '#FF4D4F', | ||
| 143 | + radius: 0 | ||
| 144 | + }) | ||
| 145 | + } | ||
| 146 | +} | ||
| 147 | +// [End startMoving] | ||
| @@ -0,0 +1,67 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2025 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +{ | ||
| 17 | + "module": { | ||
| 18 | + "name": "entry", | ||
| 19 | + "type": "entry", | ||
| 20 | + "description": "$string:module_desc", | ||
| 21 | + "mainElement": "EntryAbility", | ||
| 22 | + "deviceTypes": [ | ||
| 23 | + "default", | ||
| 24 | + "tablet", | ||
| 25 | + "2in1" | ||
| 26 | + ], | ||
| 27 | + "deliveryWithInstall": true, | ||
| 28 | + "installationFree": false, | ||
| 29 | + "pages": "$profile:main_pages", | ||
| 30 | + "abilities": [ | ||
| 31 | + { | ||
| 32 | + "name": "EntryAbility", | ||
| 33 | + "srcEntry": "./ets/entryability/EntryAbility.ets", | ||
| 34 | + "description": "$string:EntryAbility_desc", | ||
| 35 | + "icon": "$media:layered_image", | ||
| 36 | + "label": "$string:EntryAbility_label", | ||
| 37 | + "startWindowIcon": "$media:startIcon", | ||
| 38 | + "startWindowBackground": "$color:start_window_background", | ||
| 39 | + "exported": true, | ||
| 40 | + "skills": [ | ||
| 41 | + { | ||
| 42 | + "entities": [ | ||
| 43 | + "entity.system.home" | ||
| 44 | + ], | ||
| 45 | + "actions": [ | ||
| 46 | + "ohos.want.action.home" | ||
| 47 | + ] | ||
| 48 | + } | ||
| 49 | + ] | ||
| 50 | + } | ||
| 51 | + ], | ||
| 52 | + "extensionAbilities": [ | ||
| 53 | + { | ||
| 54 | + "name": "EntryBackupAbility", | ||
| 55 | + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", | ||
| 56 | + "type": "backup", | ||
| 57 | + "exported": false, | ||
| 58 | + "metadata": [ | ||
| 59 | + { | ||
| 60 | + "name": "ohos.extension.backup", | ||
| 61 | + "resource": "$profile:backup_config" | ||
| 62 | + } | ||
| 63 | + ], | ||
| 64 | + } | ||
| 65 | + ] | ||
| 66 | + } | ||
| 67 | +} | ||
| @@ -0,0 +1,8 @@ | |||
| 1 | +{ | ||
| 2 | + "color": [ | ||
| 3 | + { | ||
| 4 | + "name": "start_window_background", | ||
| 5 | + "value": "#FFFFFF" | ||
| 6 | + } | ||
| 7 | + ] | ||
| 8 | +} | ||
| @@ -0,0 +1,16 @@ | |||
| 1 | +{ | ||
| 2 | + "string": [ | ||
| 3 | + { | ||
| 4 | + "name": "module_desc", | ||
| 5 | + "value": "module description" | ||
| 6 | + }, | ||
| 7 | + { | ||
| 8 | + "name": "EntryAbility_desc", | ||
| 9 | + "value": "description" | ||
| 10 | + }, | ||
| 11 | + { | ||
| 12 | + "name": "EntryAbility_label", | ||
| 13 | + "value": "label" | ||
| 14 | + } | ||
| 15 | + ] | ||
| 16 | +} | ||
| @@ -0,0 +1,7 @@ | |||
| 1 | +{ | ||
| 2 | + "layered-image": | ||
| 3 | + { | ||
| 4 | + "background" : "$media:background", | ||
| 5 | + "foreground" : "$media:foreground" | ||
| 6 | + } | ||
| 7 | +} | ||
| @@ -0,0 +1,8 @@ | |||
| 1 | +{ | ||
| 2 | + "color": [ | ||
| 3 | + { | ||
| 4 | + "name": "start_window_background", | ||
| 5 | + "value": "#000000" | ||
| 6 | + } | ||
| 7 | + ] | ||
| 8 | +} | ||
| @@ -0,0 +1,16 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2025 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | +{ | ||
| 16 | +} | ||
| @@ -0,0 +1,182 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2025 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +import hilog from '@ohos.hilog'; | ||
| 17 | +import { describe, it, expect } from '@ohos/hypium'; | ||
| 18 | +import { Driver, ON } from '@kit.TestKit'; | ||
| 19 | +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; | ||
| 20 | + | ||
| 21 | +const TAG = '[Sample_AdjustLayout]'; | ||
| 22 | +const DOMAIN = 0xFF00 | ||
| 23 | +const BUNDLE = 'AdjustLayout_'; | ||
| 24 | + | ||
| 25 | +export default function abilityTest() { | ||
| 26 | + | ||
| 27 | + describe('EntryAbilityTest', () => { | ||
| 28 | + /** | ||
| 29 | + * 打开应用 | ||
| 30 | + */ | ||
| 31 | + it(BUNDLE + 'StartAbility_001', 0, async (done: Function) => { | ||
| 32 | + hilog.info(DOMAIN, TAG, BUNDLE + "StartAbility_001, begin"); | ||
| 33 | + let driver = Driver.create(); | ||
| 34 | + let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); | ||
| 35 | + try { | ||
| 36 | + await abilityDelegator.startAbility({ | ||
| 37 | + bundleName: 'com.samples.adjustlayout', | ||
| 38 | + abilityName: 'EntryAbility' | ||
| 39 | + }); | ||
| 40 | + } catch (exception) { | ||
| 41 | + hilog.info(DOMAIN, TAG, BUNDLE + `StartAbility_001 exception = ${JSON.stringify(exception)}`); | ||
| 42 | + expect().assertFail(); | ||
| 43 | + } | ||
| 44 | + await driver.delayMs(1000); | ||
| 45 | + await driver.assertComponentExist(ON.text('Main Window')); | ||
| 46 | + done(); | ||
| 47 | + hilog.info(DOMAIN, TAG, BUNDLE + 'StartAbility_001 end'); | ||
| 48 | + }) | ||
| 49 | + | ||
| 50 | + /** | ||
| 51 | + * 点击开启主窗比例限制按钮 | ||
| 52 | + */ | ||
| 53 | + it(BUNDLE + 'ClickAspectRatioButton_001', 2, async () => { | ||
| 54 | + hilog.info(DOMAIN, TAG, BUNDLE + "ClickAspectRatioButton_001, begin"); | ||
| 55 | + let driver = Driver.create(); | ||
| 56 | + let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); | ||
| 57 | + try { | ||
| 58 | + await abilityDelegator.startAbility({ | ||
| 59 | + bundleName: 'com.samples.adjustlayout', | ||
| 60 | + abilityName: 'EntryAbility' | ||
| 61 | + }); | ||
| 62 | + } catch (exception) { | ||
| 63 | + hilog.info(DOMAIN, TAG, BUNDLE + `ClickAspectRatioButton_001 exception = ${JSON.stringify(exception)}`); | ||
| 64 | + expect().assertFail(); | ||
| 65 | + } | ||
| 66 | + await driver.delayMs(1000); | ||
| 67 | + await driver.assertComponentExist(ON.text('开启主窗比例限制')); | ||
| 68 | + let clickBtn = await driver.findComponent(ON.text('开启主窗比例限制')); | ||
| 69 | + await clickBtn.click(); | ||
| 70 | + await driver.delayMs(1000); | ||
| 71 | + await driver.assertComponentExist(ON.text('关闭主窗比例限制')); | ||
| 72 | + | ||
| 73 | + hilog.info(DOMAIN, TAG, BUNDLE + `ClickAspectRatioButton_001 end`); | ||
| 74 | + }) | ||
| 75 | + | ||
| 76 | + /** | ||
| 77 | + * 点击显示子窗按钮 | ||
| 78 | + */ | ||
| 79 | + it(BUNDLE + 'ClickShowSubWindowButton_001', 2, async () => { | ||
| 80 | + hilog.info(DOMAIN, TAG, BUNDLE + "ClickShowSubWindowButton_001, begin"); | ||
| 81 | + let driver = Driver.create(); | ||
| 82 | + let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); | ||
| 83 | + try { | ||
| 84 | + await abilityDelegator.startAbility({ | ||
| 85 | + bundleName: 'com.samples.adjustlayout', | ||
| 86 | + abilityName: 'EntryAbility' | ||
| 87 | + }); | ||
| 88 | + } catch (exception) { | ||
| 89 | + hilog.info(DOMAIN, TAG, BUNDLE + `ClickShowSubWindowButton_001 exception = ${JSON.stringify(exception)}`); | ||
| 90 | + expect().assertFail(); | ||
| 91 | + } | ||
| 92 | + await driver.delayMs(1000); | ||
| 93 | + await driver.assertComponentExist(ON.text('显示子窗')); | ||
| 94 | + let clickBtn = await driver.findComponent(ON.text('显示子窗')); | ||
| 95 | + await clickBtn.click(); | ||
| 96 | + await driver.delayMs(1000); | ||
| 97 | + await driver.assertComponentExist(ON.text('隐藏子窗')); | ||
| 98 | + | ||
| 99 | + hilog.info(DOMAIN, TAG, BUNDLE + `ClickShowSubWindowButton_001 end`); | ||
| 100 | + }) | ||
| 101 | + | ||
| 102 | + /** | ||
| 103 | + * 点击关闭子窗拖拽大小按钮 | ||
| 104 | + */ | ||
| 105 | + it(BUNDLE + 'ClickResizeButton_001', 2, async () => { | ||
| 106 | + hilog.info(DOMAIN, TAG, BUNDLE + "ClickResizeButton_001, begin"); | ||
| 107 | + let driver = Driver.create(); | ||
| 108 | + let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); | ||
| 109 | + try { | ||
| 110 | + await abilityDelegator.startAbility({ | ||
| 111 | + bundleName: 'com.samples.adjustlayout', | ||
| 112 | + abilityName: 'EntryAbility' | ||
| 113 | + }); | ||
| 114 | + } catch (exception) { | ||
| 115 | + hilog.info(DOMAIN, TAG, BUNDLE + `ClickResizeButton_001 exception = ${JSON.stringify(exception)}`); | ||
| 116 | + expect().assertFail(); | ||
| 117 | + } | ||
| 118 | + await driver.delayMs(1000); | ||
| 119 | + await driver.assertComponentExist(ON.text('关闭子窗拖拽大小')); | ||
| 120 | + let clickBtn = await driver.findComponent(ON.text('关闭子窗拖拽大小')); | ||
| 121 | + await clickBtn.click(); | ||
| 122 | + await driver.delayMs(1000); | ||
| 123 | + await driver.assertComponentExist(ON.text('开启子窗拖拽大小')); | ||
| 124 | + | ||
| 125 | + hilog.info(DOMAIN, TAG, BUNDLE + `ClickResizeButton_001 end`); | ||
| 126 | + }) | ||
| 127 | + | ||
| 128 | + /** | ||
| 129 | + * 点击关闭子窗拖拽位置按钮 | ||
| 130 | + */ | ||
| 131 | + it(BUNDLE + 'ClickMoveButton_001', 2, async () => { | ||
| 132 | + hilog.info(DOMAIN, TAG, BUNDLE + "ClickMoveButton_001, begin"); | ||
| 133 | + let driver = Driver.create(); | ||
| 134 | + let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); | ||
| 135 | + try { | ||
| 136 | + await abilityDelegator.startAbility({ | ||
| 137 | + bundleName: 'com.samples.adjustlayout', | ||
| 138 | + abilityName: 'EntryAbility' | ||
| 139 | + }); | ||
| 140 | + } catch (exception) { | ||
| 141 | + hilog.info(DOMAIN, TAG, BUNDLE + `ClickMoveButton_001 exception = ${JSON.stringify(exception)}`); | ||
| 142 | + expect().assertFail(); | ||
| 143 | + } | ||
| 144 | + await driver.delayMs(1000); | ||
| 145 | + await driver.assertComponentExist(ON.text('关闭子窗拖拽位置')); | ||
| 146 | + let clickBtn = await driver.findComponent(ON.text('关闭子窗拖拽位置')); | ||
| 147 | + await clickBtn.click(); | ||
| 148 | + await driver.delayMs(1000); | ||
| 149 | + await driver.assertComponentExist(ON.text('开启子窗拖拽位置')); | ||
| 150 | + | ||
| 151 | + hilog.info(DOMAIN, TAG, BUNDLE + `ClickMoveButton_001 end`); | ||
| 152 | + }) | ||
| 153 | + | ||
| 154 | + /** | ||
| 155 | + * 点击设置子窗口 windowLimits 按钮 | ||
| 156 | + */ | ||
| 157 | + it(BUNDLE + 'ClickSetWindowLimitsButton_001', 2, async () => { | ||
| 158 | + hilog.info(DOMAIN, TAG, BUNDLE + "ClickSetWindowLimitsButton_001, begin"); | ||
| 159 | + let driver = Driver.create(); | ||
| 160 | + let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); | ||
| 161 | + try { | ||
| 162 | + await abilityDelegator.startAbility({ | ||
| 163 | + bundleName: 'com.samples.adjustlayout', | ||
| 164 | + abilityName: 'EntryAbility' | ||
| 165 | + }); | ||
| 166 | + } catch (exception) { | ||
| 167 | + hilog.info(DOMAIN, TAG, BUNDLE + `ClickSetWindowLimitsButton_001 exception = ${JSON.stringify(exception)}`); | ||
| 168 | + expect().assertFail(); | ||
| 169 | + } | ||
| 170 | + await driver.delayMs(1000); | ||
| 171 | + // 找到设置按钮(按钮上的文本) | ||
| 172 | + let clickBtn = await driver.findComponent(ON.text('设置子窗口 windowLimits')); | ||
| 173 | + await clickBtn.click(); | ||
| 174 | + await driver.delayMs(1000); | ||
| 175 | + // 通过 id 定位 resultText 组件,验证点击后有响应 | ||
| 176 | + await driver.assertComponentExist(ON.id('resultText')); | ||
| 177 | + | ||
| 178 | + hilog.info(DOMAIN, TAG, BUNDLE + `ClickSetWindowLimitsButton_001 end`); | ||
| 179 | + }) | ||
| 180 | + | ||
| 181 | + }) | ||
| 182 | +} | ||
| @@ -0,0 +1,19 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2025 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | +import abilityTest from './Ability.test'; | ||
| 16 | + | ||
| 17 | +export default function testsuite() { | ||
| 18 | + abilityTest(); | ||
| 19 | +} | ||
| @@ -0,0 +1,31 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2025 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +{ | ||
| 17 | + "module": { | ||
| 18 | + "name": "entry_test", | ||
| 19 | + "type": "feature", | ||
| 20 | + "deviceTypes": [ | ||
| 21 | + "phone", | ||
| 22 | + "tablet", | ||
| 23 | + "2in1", | ||
| 24 | + "car", | ||
| 25 | + "wearable", | ||
| 26 | + "tv" | ||
| 27 | + ], | ||
| 28 | + "deliveryWithInstall": true, | ||
| 29 | + "installationFree": false | ||
| 30 | + } | ||
| 31 | +} | ||
| @@ -0,0 +1,20 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2025 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +import localUnitTest from './LocalUnit.test'; | ||
| 17 | + | ||
| 18 | +export default function testsuite() { | ||
| 19 | + localUnitTest(); | ||
| 20 | +} | ||
| @@ -0,0 +1,48 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2025 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; | ||
| 17 | + | ||
| 18 | +export default function localUnitTest() { | ||
| 19 | + describe('localUnitTest', () => { | ||
| 20 | + // Defines a test suite. Two parameters are supported: test suite name and test suite function. | ||
| 21 | + beforeAll(() => { | ||
| 22 | + // Presets an action, which is performed only once before all test cases of the test suite start. | ||
| 23 | + // This API supports only one parameter: preset action function. | ||
| 24 | + }); | ||
| 25 | + beforeEach(() => { | ||
| 26 | + // Presets an action, which is performed before each unit test case starts. | ||
| 27 | + // The number of execution times is the same as the number of test cases defined by **it**. | ||
| 28 | + // This API supports only one parameter: preset action function. | ||
| 29 | + }); | ||
| 30 | + afterEach(() => { | ||
| 31 | + // Presets a clear action, which is performed after each unit test case ends. | ||
| 32 | + // The number of execution times is the same as the number of test cases defined by **it**. | ||
| 33 | + // This API supports only one parameter: clear action function. | ||
| 34 | + }); | ||
| 35 | + afterAll(() => { | ||
| 36 | + // Presets a clear action, which is performed after all test cases of the test suite end. | ||
| 37 | + // This API supports only one parameter: clear action function. | ||
| 38 | + }); | ||
| 39 | + it('assertContain', 0, () => { | ||
| 40 | + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. | ||
| 41 | + let a = 'abc'; | ||
| 42 | + let b = 'b'; | ||
| 43 | + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. | ||
| 44 | + expect(a).assertContain(b); | ||
| 45 | + expect(a).assertEqual(a); | ||
| 46 | + }); | ||
| 47 | + }); | ||
| 48 | +} | ||
| @@ -0,0 +1,38 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +{ | ||
| 17 | + "modelVersion": "6.0.0", | ||
| 18 | + "dependencies": { | ||
| 19 | + }, | ||
| 20 | + "execution": { | ||
| 21 | + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | "ultrafine" | false ]. Default: "normal" */ | ||
| 22 | + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ | ||
| 23 | + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ | ||
| 24 | + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ | ||
| 25 | + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ | ||
| 26 | + // "optimizationStrategy": "memory" /* Define the optimization strategy. Value: [ "memory" | "performance" ]. Default: "memory" */ | ||
| 27 | + }, | ||
| 28 | + "logging": { | ||
| 29 | + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ | ||
| 30 | + }, | ||
| 31 | + "debugging": { | ||
| 32 | + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ | ||
| 33 | + }, | ||
| 34 | + "nodeOptions": { | ||
| 35 | + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ | ||
| 36 | + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ | ||
| 37 | + } | ||
| 38 | +} | ||
| @@ -0,0 +1,21 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2025 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +import { appTasks } from '@ohos/hvigor-ohos-plugin'; | ||
| 17 | + | ||
| 18 | +export default { | ||
| 19 | + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ | ||
| 20 | + plugins: [] /* Custom plugin to extend the functionality of Hvigor. */ | ||
| 21 | +} | ||
| @@ -0,0 +1,24 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2025 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | +{ | ||
| 16 | + "modelVersion": "6.0.0", | ||
| 17 | + "description": "Please describe the basic information.", | ||
| 18 | + "dependencies": { | ||
| 19 | + }, | ||
| 20 | + "devDependencies": { | ||
| 21 | + "@ohos/hypium": "1.0.25", | ||
| 22 | + "@ohos/hamock": "1.0.0" | ||
| 23 | + } | ||
| 24 | +} | ||
| @@ -0,0 +1,10 @@ | |||
| 1 | +# AdjustLayout示例测试用例归档 | ||
| 2 | +## 用例表 | ||
| 3 | +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | | ||
| 4 | +|------------| --- |----------------------|------------| --- |------| | ||
| 5 | +| 拉起应用 |设备正常运行| | 成功拉起应用 | 是 | Pass | | ||
| 6 | +| 设置主窗比例限制 |设备正常运行| 点击"开启主窗比例限制"按钮 | 主窗口保持宽高比1.5 | 是 | Pass | | ||
| 7 | +| 显示/隐藏子窗 |设备正常运行| 点击"显示子窗/隐藏子窗"按钮 | 子窗口显示或隐藏 | 是 | Pass | | ||
| 8 | +| 开启/关闭子窗拖拽大小 |设备正常运行| 点击拖拽大小控制按钮 | 子窗口可/不可拖拽调整大小 | 是 | Pass | | ||
| 9 | +| 开启/关闭子窗拖拽位置 |设备正常运行| 点击拖拽位置控制按钮 | 子窗口可/不可拖拽移动位置 | 是 | Pass | | ||
| 10 | +| 设置子窗windowLimits |设备正常运行| 输入尺寸限制参数并点击设置按钮 | 子窗口尺寸限制生效 | 是 | Pass | | ||
| @@ -12,6 +12,7 @@ | |||
| 12 | * See the License for the specific language governing permissions and | 12 | * See the License for the specific language governing permissions and |
| 13 | * limitations under the License. | 13 | * limitations under the License. |
| 14 | */ | 14 | */ |
| 15 | + | ||
| 15 | { | 16 | { |
| 16 | "modelVersion": "6.0.0", | 17 | "modelVersion": "6.0.0", |
| 17 | "dependencies": { | 18 | "dependencies": { |
json5文件补充版权头