1# 应用市场首页
2
3
4本小节将以应用市场首页为例,介绍如何使用自适应布局能力和响应式布局能力适配不同尺寸窗口。
5
6
7## 页面设计
8
9一个典型的应用市场首页的UX设计如下所示。
10
11  | sm | md | lg |
12| -------- | -------- | -------- |
13| ![zh-cn_image_0000001328579522](figures/zh-cn_image_0000001328579522.png) | ![zh-cn_image_0000001328259918](figures/zh-cn_image_0000001328259918.png) | ![zh-cn_image_0000001379179861](figures/zh-cn_image_0000001379179861.png) |
14
15观察应用市场首页的页面设计,不同断点下的页面设计有较多相似的地方。
16
17据此,我们可以将页面分拆为多个组成部分。
18
191. 底部/侧边导航栏
20
212. 标题栏与搜索栏
22
233. 运营横幅
24
254. 快捷入口
26
275. 精品应用
28
29  | sm | md | lg |
30| -------- | -------- | -------- |
31| ![zh-cn_image_0000001379299533](figures/zh-cn_image_0000001379299533.png) | ![zh-cn_image_0000001328259922](figures/zh-cn_image_0000001328259922.png) | ![zh-cn_image_0000001379179865](figures/zh-cn_image_0000001379179865.png) |
32
33接下来我们逐一分析各部分的实现。
34
35
36## 底部/侧边导航栏
37
38在sm和md断点下,导航栏在底部;在lg断点下,导航栏在左侧。可以通过[Tab组件](../../reference/apis-arkui/arkui-ts/ts-container-tabs.md)的barPosition和vertical属性控制TabBar的位置,同时还可以通过barWidth和barHeight属性控制TabBar的尺寸。
39```ts
40//BreakpointSystem.ets
41import mediaQuery from '@ohos.mediaquery'
42
43export default class BreakpointSystem {
44  private currentBreakpoint: string = 'md'
45  private smListener?: mediaQuery.MediaQueryListener
46  private mdListener?:mediaQuery.MediaQueryListener
47  private lgListener?: mediaQuery.MediaQueryListener
48
49  private updateCurrentBreakpoint(breakpoint: string) {
50    if (this.currentBreakpoint !== breakpoint) {
51      this.currentBreakpoint = breakpoint
52      AppStorage.Set<string>('currentBreakpoint', this.currentBreakpoint)
53    }
54  }
55
56  private isBreakpointSM = (mediaQueryResult:mediaQuery.MediaQueryResult) => {
57    if (mediaQueryResult.matches) {
58      this.updateCurrentBreakpoint('sm')
59    }
60  }
61
62  private isBreakpointMD = (mediaQueryResult:mediaQuery.MediaQueryResult) => {
63    if (mediaQueryResult.matches) {
64      this.updateCurrentBreakpoint('md')
65    }
66  }
67
68  private isBreakpointLG = (mediaQueryResult:mediaQuery.MediaQueryResult) => {
69    if (mediaQueryResult.matches) {
70      this.updateCurrentBreakpoint('lg')
71    }
72  }
73
74  public register() {
75    this.smListener = mediaQuery.matchMediaSync('(320vp<=width<600vp)')
76    this.smListener.on('change', this.isBreakpointSM)
77    this.mdListener = mediaQuery.matchMediaSync('(600vp<=width<840vp)')
78    this.mdListener.on('change', this.isBreakpointMD)
79    this.lgListener = mediaQuery.matchMediaSync('(840vp<=width)')
80    this.lgListener.on('change', this.isBreakpointLG)
81  }
82
83  public unregister() {
84    this.smListener?.off('change', this.isBreakpointSM)
85    this.mdListener?.off('change', this.isBreakpointMD)
86    this.lgListener?.off('change', this.isBreakpointLG)
87  }
88}
89```
90
91```ts
92import Home from '../common/Home'; //组件请参考相关实例
93import TabBarItem from '../common/TabBarItem';
94import BreakpointSystem from '../common/BreakpointSystem'
95
96@Entry
97@Component
98struct Index {
99  @State currentIndex: number = 0
100  @StorageProp('currentBreakpoint') currentBreakpoint: string = 'md'
101  private breakpointSystem: BreakpointSystem = new BreakpointSystem()
102  private onTabChange = (index: number) => {
103    this.currentIndex = index
104  }
105
106  aboutToAppear() {
107    this.breakpointSystem.register()
108  }
109
110  aboutToDisappear() {
111    this.breakpointSystem.unregister()
112  }
113
114  @Builder
115  tabItem(index: number, title: Resource, icon: Resource, iconSelected: Resource) {
116    TabBarItem({
117      index: index,
118      currentIndex: this.currentIndex,
119      title: title,
120      icon: icon,
121      iconSelected: iconSelected
122    })
123  }
124
125  build() {
126    // 设置TabBar在主轴方向起始或结尾位置
127    Tabs({ barPosition: this.currentBreakpoint === "lg" ? BarPosition.Start : BarPosition.End }) {
128      // 首页
129      TabContent() {
130        Home()
131      }
132      .tabBar(this.tabItem(0, $r('app.string.tabBar1'), $r('app.media.ic_home_normal'),
133        $r('app.media.ic_home_actived')))
134
135      TabContent() {
136      }.tabBar(this.tabItem(1, $r('app.string.tabBar2'), $r('app.media.ic_app_normal'), $r('app.media.ic_app_actived')))
137
138      TabContent() {
139      }
140      .tabBar(this.tabItem(2, $r('app.string.tabBar3'), $r('app.media.ic_game_normal'),
141        $r('app.media.ic_mine_actived')))
142
143      TabContent() {
144      }
145      .tabBar(this.tabItem(3, $r('app.string.tabBar4'), $r('app.media.ic_search_normal'),
146        $r('app.media.ic_search_actived')))
147
148      TabContent() {
149      }
150      .tabBar(this.tabItem(4, $r('app.string.tabBar4'), $r('app.media.ic_mine_normal'),
151        $r('app.media.ic_mine_actived')))
152    }
153    .backgroundColor('#F1F3F5')
154    .barMode(BarMode.Fixed)
155    .barWidth(this.currentBreakpoint === "lg" ? 96 : '100%')
156    .barHeight(this.currentBreakpoint === "lg" ? '60%' : 56)
157    // 设置TabBar放置在水平或垂直方向
158    .vertical(this.currentBreakpoint === "lg")
159  }
160}
161```
162
163另外在sm及lg断点下,TabBar中各个Item的图标和文字是按照垂直方向排布的,在md断点下,TabBar中各个Item的图标和文字是按照水平方向排布的。
164
165
166```ts
167interface GeneratedObjectLiteralInterface_1 {
168  NORMAL: string;
169  SELECTED: string;
170}
171
172const TitleColor: GeneratedObjectLiteralInterface_1 = {
173  NORMAL: '#999',
174  SELECTED: '#0A59F7'
175}
176
177@Component
178export default struct TabBarItem {
179  @StorageProp('currentBreakpoint') currentBreakpoint: string = 'md'
180  @Prop currentIndex: number
181  private index?:number
182  private icon?:Resource
183  private iconSelected?:Resource
184  private title?:Resource
185
186  private getIcon() {
187    return this.currentIndex === this.index ? this.iconSelected : this.icon
188  }
189
190  private getFontColor() {
191    return this.currentIndex === this.index ? TitleColor.SELECTED : TitleColor.NORMAL
192  }
193
194  build() {
195    if (this.currentBreakpoint !== 'md' ) {
196      Column() {
197        Image(this.getIcon())
198          .width(24)
199          .height(24)
200          .margin(5)
201          .objectFit(ImageFit.Contain)
202        Text(this.title)
203          .fontColor(this.getFontColor())
204          .fontSize(12)
205          .fontWeight(500)
206      }.justifyContent(FlexAlign.Center).height('100%').width('100%')
207    } else {
208      Row() {
209        Image(this.getIcon())
210          .width(24)
211          .height(24)
212          .margin(5)
213          .objectFit(ImageFit.Contain)
214        Text(this.title)
215          .fontColor(this.getFontColor())
216          .fontSize(12)
217          .fontWeight(500)
218      }.justifyContent(FlexAlign.Center).height('100%').width('100%')
219    }
220  }
221}
222```
223
224
225## 标题栏与搜索栏
226
227标题栏和搜索栏,在sm和md断点下分两行显示,在lg断点下单行显示,可以通过栅格实现。在sm和md断点下,标题栏和搜索栏占满12列,此时会自动换行显示。在lg断点下,标题栏占8列而搜索栏占4列,此时标题栏和搜索栏在同一行中显示。
228
229  |  | sm/md | lg |
230| -------- | -------- | -------- |
231| 效果图 | ![zh-cn_image_0000001379385785](figures/zh-cn_image_0000001379385785.png) | ![zh-cn_image_0000001379464977](figures/zh-cn_image_0000001379464977.jpg) |
232| 栅格布局图 | ![zh-cn_image_0000001379464981](figures/zh-cn_image_0000001379464981.png) | ![zh-cn_image_0000001328745102](figures/zh-cn_image_0000001328745102.png) |
233
234
235```ts
236@Component
237export default struct IndexHeader {
238
239  @Builder searchBar() {
240    Stack({alignContent: Alignment.End}) {
241      TextInput({ placeholder: $r('app.string.search') })
242        .placeholderColor('#FF000000')
243        .placeholderFont({ size: 16, weight: 400 })
244        .textAlign(TextAlign.Start)
245        .caretColor('#FF000000')
246        .width('100%')
247        .height(40)
248        .fontWeight(400)
249        .padding({ top: 9, bottom: 9 })
250        .fontSize(16)
251        .backgroundColor(Color.White)
252
253      Image($r('app.media.ic_public_search'))
254        .width(16)
255        .height(16)
256        .margin({ right: 20 })
257    }.height(56).width('100%')
258  }
259
260  @Builder titleBar() {
261    Text($r('app.string.tabBar1'))
262      .fontSize(24)
263      .fontWeight(500)
264      .fontColor('#18181A')
265      .textAlign(TextAlign.Start)
266      .height(56)
267      .width('100%')
268  }
269
270  build() {
271    // 借助栅格实现标题栏和搜索栏在不同断点下的不同布局效果。
272    GridRow() {
273      GridCol({ span: { xs: 12, lg: 8 } }) {
274        this.titleBar()
275      }
276      GridCol({ span: { xs: 12, lg: 4 } }) {
277        this.searchBar()
278      }
279    }
280    .width('100%')
281  }
282}
283```
284
285
286## 运营横幅
287
288不同断点下的运营横幅,sm断点下显示一张图片,md断点下显示两张图片,lg断点下显示三张图片。可以通过[Swiper组件的displayCount属性](../../reference/apis-arkui/arkui-ts/ts-container-swiper.md)实现目标效果。
289
290
291```ts
292@Component
293export default struct IndexSwiper {
294  @StorageProp('currentBreakpoint') currentBreakpoint: string = 'md';
295  @Builder swiperItem(imageSrc:Resource) {
296    Image(imageSrc)
297      .width('100%')
298      .aspectRatio(2.5)
299      .objectFit(ImageFit.Fill)
300  }
301
302  build() {
303    Swiper() {
304      this.swiperItem($r('app.media.ic_public_swiper1'))
305      this.swiperItem($r('app.media.ic_public_swiper2'))
306      this.swiperItem($r('app.media.ic_public_swiper3'))
307      // ...
308    }
309    .autoPlay(true)
310    .indicator(false)
311    .itemSpace(10)
312    // 配置不同断点下运行横幅中展示的图片数量
313    .displayCount(this.currentBreakpoint === 'sm' ? 1 : (this.currentBreakpoint === 'md' ? 2 : 3))
314    .width('100%')
315    .padding({ left: 12, right: 12, bottom: 16, top: 16 })
316  }
317}
318```
319
320
321## 快捷入口
322
323在不同的断点下,快捷入口的5个图标始终均匀排布,这是典型的均分能力使用场景。图标资源文件[获取](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SuperFeature/MultiDeviceAppDev/AppMarket/entry/src/main/resources)
324```ts
325// /model/HomeData   在resourse文件中放置以下资源文件,
326interface AppItem{
327  id:string;
328  title:Resource;
329  image:Resource;
330}
331
332const appList:AppItem[] = [
333  { id: '0', title: $r('app.string.public_app1'), image: $r('app.media.ic_public_app1') },
334  { id: '1', title: $r('app.string.public_app2'), image: $r('app.media.ic_public_app2') },
335  { id: '2', title: $r('app.string.public_app3'), image: $r('app.media.ic_public_app3') },
336  { id: '3', title: $r('app.string.public_app4'), image: $r('app.media.ic_public_app4') },
337  { id: '4', title: $r('app.string.public_app5'), image: $r('app.media.ic_public_app5') },
338  { id: '5', title: $r('app.string.public_app6'), image: $r('app.media.ic_public_app6') },
339  { id: '6', title: $r('app.string.public_app7'), image: $r('app.media.ic_public_app7') },
340  { id: '7', title: $r('app.string.public_app8'), image: $r('app.media.ic_public_app8') },
341  { id: '8', title: $r('app.string.public_app9'), image: $r('app.media.ic_public_app9') },
342  { id: '9', title: $r('app.string.public_app10'), image: $r('app.media.ic_public_app10') },
343  { id: '10', title: $r('app.string.public_app1'), image: $r('app.media.ic_public_app1') },
344  { id: '11', title: $r('app.string.public_app1'), image: $r('app.media.ic_public_app1') },
345  { id: '12', title: $r('app.string.public_app2'), image: $r('app.media.ic_public_app2') },
346  { id: '13', title: $r('app.string.public_app3'), image: $r('app.media.ic_public_app3') },
347  { id: '14', title: $r('app.string.public_app4'), image: $r('app.media.ic_public_app4') },
348  { id: '15', title: $r('app.string.public_app5'), image: $r('app.media.ic_public_app5') },
349  { id: '16', title: $r('app.string.public_app6'), image: $r('app.media.ic_public_app6') },
350  { id: '17', title: $r('app.string.public_app7'), image: $r('app.media.ic_public_app7') },
351  { id: '18', title: $r('app.string.public_app8'), image: $r('app.media.ic_public_app8') },
352  { id: '19', title: $r('app.string.public_app9'), image: $r('app.media.ic_public_app9') },
353  { id: '20', title: $r('app.string.public_app10'), image: $r('app.media.ic_public_app10') }
354]
355
356const gameList:AppItem[] = [
357  { id: '21', title: $r('app.string.public_game1'), image: $r('app.media.ic_public_game1') },
358  { id: '22', title: $r('app.string.public_game2'), image: $r('app.media.ic_public_game2') },
359  { id: '23', title: $r('app.string.public_game3'), image: $r('app.media.ic_public_game3') },
360  { id: '24', title: $r('app.string.public_game4'), image: $r('app.media.ic_public_game4') },
361  { id: '25', title: $r('app.string.public_game5'), image: $r('app.media.ic_public_game5') },
362  { id: '26', title: $r('app.string.public_game6'), image: $r('app.media.ic_public_game6') },
363  { id: '27', title: $r('app.string.public_game7'), image: $r('app.media.ic_public_game7') },
364  { id: '28', title: $r('app.string.public_game8'), image: $r('app.media.ic_public_game8') },
365  { id: '29', title: $r('app.string.public_game9'), image: $r('app.media.ic_public_game9') },
366  { id: '30', title: $r('app.string.public_game10'), image: $r('app.media.ic_public_game10') },
367  { id: '31', title: $r('app.string.public_game1'), image: $r('app.media.ic_public_game1') },
368  { id: '32', title: $r('app.string.public_game2'), image: $r('app.media.ic_public_game2') },
369  { id: '33', title: $r('app.string.public_game3'), image: $r('app.media.ic_public_game3') },
370  { id: '34', title: $r('app.string.public_game4'), image: $r('app.media.ic_public_game4') },
371  { id: '35', title: $r('app.string.public_game5'), image: $r('app.media.ic_public_game5') },
372  { id: '36', title: $r('app.string.public_game6'), image: $r('app.media.ic_public_game6') },
373  { id: '37', title: $r('app.string.public_game7'), image: $r('app.media.ic_public_game7') },
374  { id: '38', title: $r('app.string.public_game8'), image: $r('app.media.ic_public_game8') },
375  { id: '39', title: $r('app.string.public_game9'), image: $r('app.media.ic_public_game9') },
376  { id: '40', title: $r('app.string.public_game10'), image: $r('app.media.ic_public_game10') }
377]
378
379const entranceIcons:AppItem[]= [
380  { id: '41',title: $r('app.string.home_categories'), image: $r('app.media.ic_home_categories') },
381  { id: '42',title: $r('app.string.home_top'), image: $r('app.media.ic_home_top') },
382  { id: '43',title: $r('app.string.home_fast'), image: $r('app.media.ic_home_fast') },
383  { id: '44',title: $r('app.string.home_flower'), image: $r('app.media.ic_home_flower') },
384  { id: '45',title: $r('app.string.home_education'), image: $r('app.media.ic_home_education') },
385]
386
387export { entranceIcons, appList, gameList }
388```
389
390
391
392
393```ts
394//model/HomeDataType
395interface AllIcons {
396  image: Resource,
397  title: Resource,
398}
399
400interface AppItem  {
401  id: string,
402  title: Resource,
403  image: Resource
404}
405
406class MyAppSource implements IDataSource {
407  private dataArray: AppItem[] = []
408  private listeners: DataChangeListener[] = []
409
410  constructor(element: AppItem[]) {
411    for (let index = 0; index < element.length; index++) {
412      this.dataArray.push(element[index])
413    }
414  }
415
416  public totalCount(): number {
417    return this.dataArray.length
418  }
419
420  public getData(index: number): AppItem {
421    return this.dataArray[index]
422  }
423
424  public addData(index: number, data: AppItem): void {
425    this.dataArray.splice(index, 0, data)
426    this.notifyDataAdd(index)
427  }
428
429  public pushData(data: AppItem): void {
430    this.dataArray.push(data)
431    this.notifyDataAdd(this.dataArray.length - 1)
432  }
433
434  registerDataChangeListener(listener: DataChangeListener): void {
435    if (this.listeners.indexOf(listener) < 0) {
436      this.listeners.push(listener)
437    }
438  }
439
440  unregisterDataChangeListener(listener: DataChangeListener): void {
441    const pos = this.listeners.indexOf(listener);
442    if (pos >= 0) {
443      this.listeners.splice(pos, 1)
444    }
445  }
446
447  notifyDataReload(): void {
448    this.listeners.forEach(listener => {
449      listener.onDataReloaded()
450    })
451  }
452
453  notifyDataAdd(index: number): void {
454    this.listeners.forEach(listener => {
455      listener.onDataAdd(index)
456    })
457  }
458
459  notifyDataChange(index: number): void {
460    this.listeners.forEach(listener => {
461      listener.onDataChange(index)
462    })
463  }
464
465  notifyDataDelete(index: number): void {
466    this.listeners.forEach(listener => {
467      listener.onDataDelete(index)
468    })
469  }
470
471  notifyDataMove(from: number, to: number): void {
472    this.listeners.forEach(listener => {
473      listener.onDataMove(from, to)
474    })
475  }
476}
477
478export { AllIcons, MyAppSource, AppItem }
479
480```
481
482```ts
483import { entranceIcons } from '../model/HomeData';
484import { AllIcons } from '../model/HomeDataType';
485
486@Component
487export default struct IndexEntrance {
488  build() {
489    // 将justifyContent参数配置为FlexAlign.SpaceEvenly实现均分布局
490    Row() {
491      ForEach(entranceIcons, (icon: AllIcons) => {
492        // 各快捷入口的图标及名称
493        Column() {
494          // ...
495          }
496      })
497    }
498    .width('100%')
499    .height(64)
500    .justifyContent(FlexAlign.SpaceEvenly)
501    .padding({ left: 12, right: 12 })
502  }
503}
504```
505
506
507## 精品应用
508
509随着可用显示区域的增加,精品应用中显示的图标数量也不断增加,这是典型的延伸能力使用场景。精品游戏的实现与精品应用类似,不再展开分析。
510
511
512```ts
513import { AppItem, MyAppSource } from '../model/HomeDataType';
514
515@Component
516export default struct IndexApps {
517  private title?: Resource;
518  @StorageProp('currentBreakpoint') currentBreakpoint: string = 'md';
519  private apps: AppItem[] = [];
520  @Builder
521  appListHeader() {
522    Row() {
523      Text(this.title)
524        .width(100)
525        .fontSize(16)
526        .textAlign(TextAlign.Start)
527        .fontWeight(500)
528      Blank()
529      Text($r('app.string.more'))
530        .fontSize(14)
531        .textAlign(TextAlign.End)
532        .fontWeight(400)
533        .margin({ right: 2 })
534      Image($r('app.media.ic_public_arrow_right'))
535        .width(12)
536        .height(18)
537        .opacity(0.9)
538        .objectFit(ImageFit.Fill)
539    }
540    .margin({ bottom: 9, top: 9 })
541    .width('100%')
542    .alignItems(VerticalAlign.Bottom)
543  }
544
545  @Builder
546  appListItem(app:AppItem) {
547    Column() {
548      Image(app.image)
549        .width(this.currentBreakpoint === 'lg' ? 80 : 56)
550        .height(this.currentBreakpoint === 'lg' ? 80 : 56)
551        .margin({ bottom: 8 })
552      Text(app.title)
553        .width(this.currentBreakpoint === 'lg' ? 80 : 56)
554        .height(16)
555        .fontSize(12)
556        .textAlign(TextAlign.Center)
557        .fontColor('#18181A')
558        .margin({ bottom: 8 })
559      Text($r('app.string.install'))
560        .width(this.currentBreakpoint === 'lg' ? 80 : 56)
561        .height(28)
562        .fontColor('#0A59F7')
563        .textAlign(TextAlign.Center)
564        .borderRadius(this.currentBreakpoint === 'lg' ? 26 : 20)
565        .fontWeight(500)
566        .fontSize(12)
567        .padding({ top: 6, bottom: 6, left: 8, right: 8 })
568        .backgroundColor('rgba(0,0,0,0.05)')
569    }
570  }
571  build() {
572    Column() {
573      this.appListHeader()
574      // 借助List组件能力,实现延伸能力场景
575      List({ space: this.currentBreakpoint === 'lg' ? 44 : 20}) {
576        LazyForEach(new MyAppSource(this.apps), (app: AppItem)=> {
577          ListItem() {
578            // 每个应用的图标、名称及安装按钮
579            this.appListItem(app)
580          }
581        })
582      }
583      .width('100%')
584      .height(this.currentBreakpoint === 'lg' ? 140 : 120)
585      .listDirection(Axis.Horizontal)
586    }
587    .width('100%')
588    .height(this.currentBreakpoint === 'lg' ? 188 : 164)
589    .padding({ bottom: 8, left: 12, right: 12 })
590  }
591}
592```
593
594
595## 运行效果
596
597将上述各页面主要部分组合在一起后,即可完成整体页面开发。
598
599
600```ts
601entry/src/main/ets                         // 代码区
602|---model
603|   |---HomeData.ets                       // 主页用到的图片资源
604|   |---HomeDataType.ets                   // 事件监听函数
605|---pages
606|   |---index.ets                          // 首页
607|---common
608|   |---BreakpointSystem.ets               // 媒体查询
609|   |---Home.ets                           // 主容器
610|   |---IndexApps.ets                      // app模块(包含安装,展示图片,更多功能)
611|   |---IndexContent.ets                   // 内容模块
612|   |---IndexEntrance.ets                  // 下一步模块(箭头跳转组件)
613|   |---IndexHeader.ets                    // 头部组件
614|   |---IndexSwiper.ets                    // 轮播图
615|   |---TabBarItem.ets                     // 导航栏
616entry/src/main/resources                   // 资源文件
617```
618
619```ts
620import IndexSwiper from './IndexSwiper';
621import IndexEntrance from './IndexEntrance';
622import IndexApps from './IndexApps';
623import { appList, gameList } from '../model/HomeData';
624import IndexHeader from './IndexHeader';
625
626@Component
627struct IndexContent {
628  // ...
629  build() {
630    List() {
631      // 运营横幅
632      ListItem() {
633        IndexSwiper()
634      }
635      // 快捷入口
636      ListItem() {
637        IndexEntrance()
638      }
639      // 精品应用
640      ListItem() {
641        IndexApps({ title: $r('app.string.boutique_application'), apps: appList })
642      }
643      // 精品游戏
644      ListItem() {
645        IndexApps({ title: $r('app.string.boutique_game'), apps: gameList })
646      }
647    }
648    .width("100%")
649  }
650}
651
652@Entry
653@Component
654export default struct Home {
655  // ...
656  build() {
657    Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Start }) {
658      // 标题栏和搜索栏
659      IndexHeader()
660      // 运营横幅、快捷入口、精品应用、精品游戏等
661      IndexContent()
662    }
663    .height('100%')
664    .backgroundColor("#F1F3F5")
665  }
666}
667```
668
669本页面的实际运行效果如下图所示。
670
671  | sm | md | lg |
672| -------- | -------- | -------- |
673| ![zh-cn_image_0000001334345550](figures/zh-cn_image_0000001334345550.jpg) | ![zh-cn_image_0000001385105477](figures/zh-cn_image_0000001385105477.jpg) | ![zh-cn_image_0000001384985569](figures/zh-cn_image_0000001384985569.jpg) |
674
675## 相关实例
676
677针对应用市场应用开发,有以下相关实例可以参考:
678
679- 应用市场开发:[典型页面场景:应用市场首页(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SuperFeature/MultiDeviceAppDev/AppMarket)
680
681
682
683<!--no_check-->