1# 常见问题 2 3 4## 如何查询设备类型 5 6设备类型分为default(默认设备)、tablet、tv、wearable、2in1等,有多种查询设备类型的方式。 7 81. 通过命令行的方式查询设备类型。 9 通过命令行查询指定系统参数(const.product.devicetype)进而确定设备类型<!--Del-->,详见[系统参数介绍](../../../device-dev/subsystems/subsys-boot-init-sysparam.md)<!--DelEnd-->。 10 11 12 ```shell 13 # 方法一 14 hdc shell param get "const.product.devicetype" 15 # 方法二 16 hdc shell cat /etc/param/ohos.para | grep const.product.devicetype 17 ``` 18 192. 在应用开发过程中查询设备类型。 20 - 通过deviceInfo查询设备类型,deviceInfo中各个字段的含义请参考[设备信息](../../reference/apis-basic-services-kit/js-apis-device-info.md)。 21 22 ```ts 23 import { deviceInfo } from'@kit.BasicServicesKit' 24 25 @Entry 26 @Component 27 struct GetDeviceTypeSample { 28 @State deviceType:string='unknown' 29 30 aboutToAppear() { 31 this.deviceType= deviceInfo.deviceType 32 } 33 34 build() { 35 Column() { 36 Text(this.deviceType).fontSize(24) 37 } 38 .width('100%') 39 .height('100%') 40 } 41 } 42 ``` 43 44 45## 如何在不同设备上为Ability配置不同的启动模式 46 47应用由一个或多个Ability组成,Ability支持单实例、多实例和指定实例3种[启动模式](../../application-models/uiability-launch-type.md),启动模式可以在[配置文件(module.json5)](../../quick-start/module-configuration-file.md)中通过launchType字段配置。启动模式对应Ability被启动时的行为,对启动模式的详细说明如下: 48 49| 启动模式 | 描述 | 说明 | 50| -------- | -------- | -------- | 51| multiton | 多实例 | 每次startAbility都会启动一个新的实例。 | 52| singleton | 单实例 | 系统中最多只可以存在一个实例,startAbility时,如果系统中已存在相应的Ability实例,则复用该实例。 | 53| specified | 指定实例 | 运行时由Ability内部业务决定是否创建多实例。 | 54 55默认设备屏幕尺寸较小,采用multiton启动模式不仅无法给用户提供便利,反而可能消耗更多系统资源,故通常采用singleton启动模式。平板屏幕尺寸较大且可能支持自由窗口,对于文档编辑、网页浏览等场景,使用multiton启动模式可以提升用户体验。 56 57本文中将默认设备和平板等归为同一泛类,推荐同一泛类的设备共用HAP,同时本文也介绍了如何通过自适应布局能力和响应式布局能力开发出适配不同设备的页面。这里将补充介绍,如何实现Ability在不同设备上以不同的模式启动。 58 59launchType字段配置为specified时,系统会根据AbilityStage的onAcceptWant的返回值确定是否创建新的实例。对于同一个应用,如果key已经存在,则复用该key对应的Ability,如果key不存在则新创建Ability。 60 61可以将配置文件中的launchType字段配置为specified,同时在应用中加入如下代码以实现目标效果。 62 63- 非平板设备,直接将设备类型作为key,保证每次启动的key相同,即以单实例模式运行。 64 65- 平板设备,将设备类型与毫秒级时间戳叠加作为key,保证每次启动的key不同,即以多实例模式运行。 66 67 68```ts 69// MyAbilityStage.ts 70import { AbilityStage, Want } from "@kit.AbilityKit" 71import { deviceInfo } from'@kit.BasicServicesKit' 72 73export default class MyAbilityStage extends AbilityStage { 74 //... 75 private generateKey(): string { 76 // 如果是平板,则将设备类型和毫秒级时间戳叠加作为key,保证每次启动的key都不同 77 if (deviceInfo.deviceType === 'tablet') { 78 return deviceInfo.deviceType + (new Date()).valueOf() 79 } 80 // 如果不是平板,直接以设备类型作为key,每次启动的key相同 81 return deviceInfo.deviceType 82 } 83 onAcceptWant(want: Want) : string{ 84 return this.generateKey() 85 } 86} 87``` 88 89 90## 如何开启自由窗口 91 92自由窗口功能默认是关闭的,可以通过如下方式开启自由窗口功能。 93 94 95```shell 96# 取出窗口配置文件,并将文件中的<decor enable="false"></decor>修改为<decor enable="true"></decor> 97hdc file recv system/etc/window/resources/window_manager_config.xml ./ 98# 以可读写的模式重新挂载根目录,并更新配置文件 99hdc shell mount -o rw,remount / 100hdc file send window_manager_config.xml system/etc/window/resources/window_manager_config.xml 101# 重启设备,配置生效 102hdc shell reboot 103``` 104 105屏幕较小,通过手指操作窗口较为不便时,建议外接鼠标进行操作。 106 107- 鼠标在应用顶部悬停,即可召唤出窗口工具栏。 108 109- 点击窗口工具栏中的缩放按钮(从左到右第二个),即可让应用以自由窗口的模式显示。 110 111- 在自由窗口模式下,可以通过拖动应用窗口的边框或顶角,改变窗口尺寸同时触发应用显示刷新。 112 在调整窗口尺寸的过程中,窗口尺寸可能超出屏幕尺寸。此时应用显示正常,但受限于屏幕尺寸,在屏幕中只能看到应用部分区域的显示。可以通过移动窗口位置,查看应用其它区域的显示。 113 114 | 窗口操作按钮 | 悬浮窗口显示 | 调整窗口尺寸及位置查看不同的效果 | 115 | -------- | -------- | -------- | 116 |  |  |  | 117 118 119## 如何限制自由窗口的尺寸调节范围 120 121自适应布局可以保证窗口尺寸在一定范围内变化时,页面的显示是正常的。当窗口尺寸变化较大时,就需要额外借助响应式布局能力(如断点等)调整页面结构以保证显示正常。通常每个断点都需要开发者精心适配以获得最佳的显示效果,考虑到设计及开发成本等实际因素的限制,应用不可能适配从零到正无穷的所有窗口宽度。 122 123不同设备或不同设备状态,系统默认的自由窗口尺寸的调节范围可能不同。开发者可以在[应用配置文件](../../quick-start/module-configuration-file.md)中限制应用中各个Ability的自由窗口尺寸调节范围,配置文件中影响自由窗口尺寸调节范围的字段如下表所示。 124 125| 配置文件字段 | 数据类型 | 描述 | 126| -------- | -------- | -------- | 127| minWindowWidth | 数值 | 标识该ability支持的最小的窗口宽度, 宽度单位为vp。 | 128| minWindowHeight | 数值 | 标识该ability支持的最小的窗口高度, 高度单位为vp。 | 129| maxWindowWidth | 数值 | 标识该ability支持的最大的窗口宽度,宽度单位为vp。 | 130| maxWindowHeight | 数值 | 标识该ability支持的最大的窗口高度, 高度单位为vp。 | 131| minWindowRatio | 数值 | 标识该ability支持的最小的宽高比。 | 132| maxWindowRatio | 数值 | 标识该ability支持的最大的宽高比。 | 133 134如下所示,通过配置文件分别限制自由窗口的最大和最小尺寸。 135 136 137```json 138{ 139 "module": { 140 //... 141 "abilities": [ 142 { 143 //... 144 "minWindowWidth": 320, 145 "minWindowHeight": 240, 146 "maxWindowWidth": 1440, 147 "maxWindowHeight": 900, 148 "minWindowRatio": 0.5, 149 "maxWindowRatio": 2, 150 } 151 ] 152 } 153} 154``` 155 156## 如何获取组件的尺寸 157 158实际开发过程中,开发者可能有获取页面中某个组件或某块区域的尺寸的诉求,以便通过手动计算等进行更精确的布局计算及优化。 159 160开发者可以通过[组件区域变化事件](../../reference/apis-arkui/arkui-ts/ts-universal-component-area-change-event.md)(即组件显示的尺寸、位置等发生变化时触发的事件)来获取指定组件的尺寸。 161 162如下所示,通过onAreaChange事件获取Row组件(页面中白色区域)的尺寸。 163 164 165 166```ts 167@Entry 168@Component 169struct OnAreaChangeSample { 170 @State rate: number = 0.8 171 @State info: string = '' 172 173 // 底部滑块,可以通过拖拽滑块改变容器尺寸 174 @Builder slider() { 175 Slider({ value: this.rate * 100, min: 30, max: 80, style: SliderStyle.OutSet }) 176 .blockColor(Color.White) 177 .width('60%') 178 .onChange((value: number) => { 179 this.rate = value / 100; 180 }) 181 .position({ x: '20%', y: '80%' }) 182 } 183 184 build() { 185 Column() { 186 Column() { 187 Row() { 188 Text(this.info).fontSize(20).lineHeight(22) 189 } 190 .borderRadius(12) 191 .padding(24) 192 .backgroundColor('#FFFFFF') 193 .width(this.rate * 100 + '%') 194 .onAreaChange((oldValue: Area, newValue: Area) => { 195 this.info = JSON.stringify(newValue) 196 }) 197 } 198 199 this.slider() 200 } 201 .width('100%') 202 .height('100%') 203 .backgroundColor('#F1F3F5') 204 .justifyContent(FlexAlign.Center) 205 } 206} 207``` 208## 如何解决顶部单张大图问题 209 210**解决方案** 211 212顶部背景图被拉伸时,可以通过设置背景图片的[backgroundImageSize](../../reference/apis-arkui/arkui-ts/ts-universal-attributes-background.md#backgroundimagesize)属性,使得图片大小能够合理显示,达到适配效果。 213 214**布局效果** 215 216| sm | md | 217| ----------- | ----------- | 218|  |  | 219 220**参考代码** 221 222```ts 223@Entry 224@Component 225struct ImageClip { 226 build() { 227 // 设置背景图片的backgroundImageSize属性,使得图片大小能够合理显示 228 Column() 229 .width('100%') 230 .height(300) 231 .backgroundColor('#ccc') 232 .backgroundImage($r('app.media.ImageOne')) 233 .backgroundImageSize(ImageSize.Cover) 234 .backgroundImagePosition(Alignment.Center) 235 } 236} 237``` 238 239## 如何解决Item内容过大 240 241**解决方案** 242 243在大屏上,Listitem内容会过大,页面整体浏览内容减少。可通过以下两种方法解决: 244- 设置List列的最小宽度和最大宽度,使List组件根据宽度自适应决定列数。 245- 借助栅格行组件[GridRow](../../reference/apis-arkui/arkui-ts/ts-container-gridrow.md),调整不同的断点下List组件的宽度。 246 247**布局效果** 248 249| sm | md | 250| -------- | -------- | 251| 展示1列 | 展示2列 | 252|  | | 253 254**参考代码** 255 256```ts 257@Entry 258@Component 259struct ListLayout { 260 @State data: Resource[] = new Array(5).fill($r("app.media.image")) 261 @State breakPoint: string = 'sm' 262 263 build() { 264 GridRow() { 265 GridCol({ span: { sm: 12, md: 12, lg: 12 } }) { 266 List({ space: 24 }) { 267 ForEach(this.data, (item: Resource) => { 268 ListItem() { 269 Image(item).margin({ left: 12, right: 12 }) 270 } 271 }) 272 } 273 // 设置列最小宽度和最大宽度 274 .lanes({ minLength: 300, maxLength: 360 }).padding(12) 275 } 276 }.onBreakpointChange((breakpoint: string) => { 277 this.breakPoint = breakpoint 278 }) 279 } 280} 281``` 282 283```ts 284List() { 285 // ... 286} 287// 根据断点设置List列数 288.lanes(this.breakPoint === 'sm' ? 1 : 2) 289``` 290 291## 如何解决Banner图片过大 292 293**解决方案** 294 295在大屏上,Swiper图片显示内容过大,可以通过增加Swiper展示图片数来调整图片显示大小。外层可以使用栅格组件[GridRow](../../reference/apis-arkui/arkui-ts/ts-container-gridrow.md),通过调用OnBreakpointChange事件,调整不同的断点下Swiper的前后边距,实现在不同屏幕尺寸上的显示不同Swiper图片数。 296 297**布局效果** 298 299| sm | md | 300| -------- | -------- | 301| 展示一个内容项 | 展示一个完整的内容项 + 左右相邻的部分内容项 | 302|  |  | 303 304**参考代码** 305 306```ts 307@Entry 308@Component 309struct SwiperLayout { 310 @State data: Resource[] = new Array(5).fill($r("app.media.sky")) 311 @State breakPoint: string = 'sm' 312 313 build() { 314 Row() { 315 GridRow() { 316 GridCol({ span: { sm: 12, md: 12, lg: 12 } }) { 317 Swiper() { 318 ForEach(this.data, (item: Resource) => { 319 Image(item).width('100%').height(180) 320 }) 321 } 322 .width('100%') 323 .itemSpace(24) 324 // 根据断点设置Swiper前后边距 325 .prevMargin(this.breakPoint === 'sm' ? 0 : 100) 326 .nextMargin(this.breakPoint === 'sm' ? 0 : 100) 327 } 328 }.onBreakpointChange((breakpoint: string) => { 329 this.breakPoint = breakpoint 330 }) 331 .height("60%") 332 .borderWidth(2) 333 } 334 } 335} 336``` 337 338## 如何解决信息流图片过大 339 340**解决方案** 341 342针对信息流单张图片过大的情况,设置[aspectRatio](../../reference/apis-arkui/arkui-ts/ts-universal-attributes-layout-constraints.md#aspectratio)和[constrainSize](../../reference/apis-arkui/arkui-ts/ts-universal-attributes-size.md#constraintsize)属性,可以通过对图片的布局和尺寸进行约束,达到适配效果。 343 344**布局效果** 345 346| sm | md | 347| -------- | -------- | 348|  |  | 349 350**参考代码** 351 352```ts 353@Entry 354@Component 355struct ImageConstrainSize { 356 @State breakPoint: string = 'sm' 357 build() { 358 GridRow(){ 359 GridCol({ span: { sm: 12, md: 12, lg: 12 } }){ 360 Column(){ 361 Text('一次开发,多端部署,让开发者可以基于一种设计,高效构建多端可运行的应用。一次开发,多端部署,让开发者可以基于一种设计,高效构建多端可运行的应用。') 362 // 设置aspectRatio和constrainSize属性,可以对图片的布局和尺寸进行约束 363 Image($r('app.media.ImageTwo')) 364 .width('30%') 365 .aspectRatio(0.5) 366 .constraintSize({ maxWidth: 240, minWidth: 180 }) 367 Text('一次开发,多端部署,让开发者可以基于一种设计,高效构建多端可运行的应用。一次开发,多端部署,让开发者可以基于一种设计,高效构建多端可运行的应用。') 368 }.alignItems(HorizontalAlign.Start) 369 370 } 371 }.onBreakpointChange((breakpoint: string) => { 372 this.breakPoint = breakpoint 373 }) 374 } 375} 376``` 377 378## 如何解决信息流_4宫格图片过大 379 380**解决方案** 381 382在大屏上,Grid组件里的4宫格图片大小过大,页面浏览区域变少。可以借助栅格行组件[GridRow](../../reference/apis-arkui/arkui-ts/ts-container-gridrow.md)来调整不同的断点下Grid的宽度,解决大屏上Grid组件4宫格图片过大的问题。 383 384**布局效果** 385 386| sm | md | 387| -------- | -------- | 388| 宽度占满屏幕 | 宽度占屏幕的60% | 389|  |  | 390 391**参考代码** 392 393```ts 394@Entry 395@Component 396struct GridLayout { 397 @State data: Resource[] = new Array(4).fill($r("app.media.image")) 398 @State breakPoint: string = 'sm' 399 400 build() { 401 GridRow() { 402 GridCol({ span: { sm: 12, md: 12, lg: 12 } }) { 403 Column() { 404 Text('一次开发,多端部署,让开发者可以基于一种设计,高效构建多端可运行的应用。') 405 Grid() { 406 ForEach(this.data, (item: Resource) => { 407 GridItem() { 408 Image(item).width('100%').aspectRatio(1) 409 } 410 }) 411 }.columnsTemplate('1fr 1fr') 412 .columnsGap(24) 413 .rowsGap(24) 414 // 根据断点设置Grid宽度 415 .width(this.breakPoint === 'md' ? '60%' : '100%') 416 }.width('100%').alignItems(HorizontalAlign.Start) 417 } 418 }.onBreakpointChange((breakpoint: string) => { 419 this.breakPoint = breakpoint 420 }) 421 } 422} 423``` 424## 如何解决信息流_9宫格图片过大 425 426**解决方案** 427 428在大屏上,Grid组件里的9宫格图片大小过大,页面整体浏览内容减少,可以设置Grid组件宽度和宽高比,使Grid组件保持固定大小,不会随着屏幕尺寸变化而变化。 429 430**布局效果** 431 432| sm | md | 433| -------- | -------- | 434| 图片宽高不变 | 图片宽高不变 | 435|  |  | 436 437**参考代码** 438 439```ts 440@Entry 441@Component 442struct GridWidth { 443 @State data: Resource[] = new Array(9).fill($r("app.media.sky")) 444 445 build() { 446 Column() { 447 Text('一次开发,多端部署,让开发者可以基于一种设计,高效构建多端可运行的应用。') 448 Grid() { 449 ForEach(this.data, (item: Resource) => { 450 GridItem() { 451 Image(item).width('100%').aspectRatio(1) 452 } 453 }) 454 } 455 .columnsTemplate('1fr 1fr 1fr') 456 .columnsGap(12) 457 .rowsGap(12) 458 // 设置固定宽度和宽高比 459 .width(360) 460 .aspectRatio(1) 461 .padding(12) 462 } 463 .alignItems(HorizontalAlign.Start) 464 } 465} 466```