• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 音乐专辑页
2
3
4本小节将以音乐专辑页为例,介绍如何使用自适应布局能力和响应式布局能力适配不同尺寸窗口。本示例已经在[OpenHarmony应用示例](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-3.2-Release/code/SuperFeature/MultiDeviceAppDev/MusicAlbum)中开源,读者可以根据需要自行下载源码并运行及查看效果。
5
6
7## 页面设计
8
9音乐专辑页的页面设计如下。
10
11  | sm | md | lg |
12| -------- | -------- | -------- |
13| ![zh-cn_image_0000001381013985](figures/zh-cn_image_0000001381013985.png) | ![zh-cn_image_0000001381133197](figures/zh-cn_image_0000001381133197.png) | ![zh-cn_image_0000001329813432](figures/zh-cn_image_0000001329813432.png) |
14
15同样观察音乐专辑的页面设计,不同断点下的页面设计有较多相似的地方。
16
17据此,我们可以将页面分拆为多个组成部分。
18
191. 标题栏
20
212. 歌单封面
22
233. 歌单列表
24
254. 播放控制栏
26
27  | sm | md | lg |
28| -------- | -------- | -------- |
29| ![zh-cn_image_0000001380933349](figures/zh-cn_image_0000001380933349.jpg) | ![zh-cn_image_0000001330133330](figures/zh-cn_image_0000001330133330.jpg) | ![zh-cn_image_0000001381013989](figures/zh-cn_image_0000001381013989.jpg) |
30
31
32## 标题栏
33
34不同断点下,标题栏始终只显示“返回按钮”、“歌单”以及“更多按钮”,但“歌单”与“更多按钮”之间的间距不同。由于不同断点下标题栏的背景色也有较大差异,因此无法使用拉伸能力实现,此场景更适合使用栅格实现。我们可以将标题栏划分为“返回按钮及歌单”和“更多按钮”两部分,这两部分在不同断点下占据的列数如下图所示。另外,还可以借助OnBreakpointChange事件,调整不同断点下这两部分的背景色。
35
36  |  | sm | md | lg |
37| -------- | -------- | -------- | -------- |
38| 效果图 | ![zh-cn_image_0000001329817776](figures/zh-cn_image_0000001329817776.png) | ![zh-cn_image_0000001381018337](figures/zh-cn_image_0000001381018337.png) | ![zh-cn_image_0000001381137517](figures/zh-cn_image_0000001381137517.jpg) |
39| 栅格布局图 | ![zh-cn_image_0000001330137692](figures/zh-cn_image_0000001330137692.png) | ![zh-cn_image_0000001329977740](figures/zh-cn_image_0000001329977740.png) | ![zh-cn_image_0000001329658136](figures/zh-cn_image_0000001329658136.png) |
40
41
42```
43@Component
44export struct PlayListHeader {
45  @State moreBackgroundColor: Resource = $r('app.color.play_list_cover_background_color');
46  build() {
47    GridRow() {
48      GridCol({span: {sm:6, md: 6, lg:4}}) {
49        Row() {
50          Image($r('app.media.ic_back')).height('24vp').width('24vp')
51        }
52        .width('100%')
53        .height('50vp')
54        .justifyContent(FlexAlign.Start)
55        .alignItems(VerticalAlign.Center)
56        .padding({left:$r('app.float.default_margin')})
57        .backgroundColor($r('app.color.play_list_cover_background_color'))
58      }
59      GridCol({span: {sm:6, md: 6, lg:8}}) {
60        Row() {
61          Image($r('app.media.ic_add')).height('24vp').width('24vp')
62        }
63        .width('100%')
64        .height('50vp')
65        .justifyContent(FlexAlign.End)
66        .alignItems(VerticalAlign.Center)
67        .padding({right:$r('app.float.default_margin')})
68        .backgroundColor(this.moreBackgroundColor)
69      }
70    }.onBreakpointChange((currentBreakpoint) => {
71      // 调整不同断点下返回按钮及歌单的背景色
72      if (currentBreakpoint === 'sm') {
73        this.moreBackgroundColor = $r('app.color.play_list_cover_background_color');
74      } else {
75        this.moreBackgroundColor = $r('app.color.play_list_songs_background_color');
76      }
77    }).height('100%').width('100%')
78  }
79}
80```
81
82
83## 歌单封面
84
85歌单封面由封面图片、歌单介绍及常用操作三部分组成,这三部分的布局在md和lg断点下完全相同,但在sm断点下有较大差异。此场景同样可以用栅格实现。
86
87  |  | sm | md/lg |
88| -------- | -------- | -------- |
89| 效果图 | ![zh-cn_image_0000001329660244](figures/zh-cn_image_0000001329660244.jpg) | ![zh-cn_image_0000001381379829](figures/zh-cn_image_0000001381379829.png) |
90| 栅格布局图 | ![zh-cn_image_0000001381220165](figures/zh-cn_image_0000001381220165.png) | ![zh-cn_image_0000001381220169](figures/zh-cn_image_0000001381220169.png) |
91
92
93```
94@Component
95export default struct PlayListCover {
96  ...
97  build() {
98    Column() {
99      // 借助栅格组件实现总体布局
100      GridRow() {
101        // 歌单图片
102        GridCol({ span: { sm: 4, md: 10 }, offset: { sm: 0, md: 1, lg: 1 } }) {
103          this.CoverImage()
104        }
105        // 歌单介绍
106        GridCol({ span: { sm: 8, md: 10 }, offset: { sm: 0, md: 2, lg: 2 } }) {
107          this.CoverIntroduction()
108        }
109        // 歌单操作
110        GridCol({ span: { sm: 12, md: 10 }, offset: { sm: 0, md: 2, lg: 2 } }) {
111          this.CoverOptions()
112        }.margin({
113          top: this.currentBreakpoint === 'sm' ? 15 : 0,
114          bottom: this.currentBreakpoint === 'sm' ? 15 : 0
115        })
116      }
117      .margin({ left: this.coverMargin, right: this.coverMargin })
118    }
119    .height(this.currentBreakpoint === 'sm' ? this.coverHeight : '100%')
120    .padding({ top: this.currentBreakpoint === 'sm' ? 50 : 70 })
121  }
122}
123```
124
125
126## 歌单列表
127
128不同断点下,歌单列表的样式基本一致,但sm和md断点下是歌单列表是单列显示,lg断点下是双列显示。可以通过[List组件](https://gitee.com/fanzhaonan/docs/blob/master/zh-cn/application-dev/reference/arkui-ts/ts-container-list.md)的lanes属性实现这一效果。
129
130
131```
132@Component
133export default struct PlayList {
134  ...
135  build() {
136    Column() {
137      this.PlayAll()
138      Scroll() {
139        List() {
140          LazyForEach(new MyDataSource(songList), item => {
141            ListItem() {
142              this.SongItem(item.title, item.label, item.singer)
143            }
144          }, item => item.id)
145        }
146        .width('100%')
147        .height('100%')
148        // 配置不同断点下歌单列表的列数
149        .lanes(this.currentBreakpoint === 'lg' ? 2 : 1)
150      }
151      .backgroundColor('#fff')
152      .margin({ top: 50, bottom: this.currentBreakpoint === 'sm' ? this.coverHeight : 0 })
153    }
154    .padding({top: 50,bottom: 48})
155  }
156}
157```
158
159
160## 播放控制栏
161
162在不同断点下,播放控制栏显示的内容完全一致,唯一的区别是歌曲信息与播放控制按钮之间的间距有差异,这是典型的拉伸能力的使用场景。
163
164
165```
166@Component
167export struct MusicBar {
168  build() {
169    Row() {
170      Image($r('app.media.pic_album')).height(32).width(32).margin({right: 12})
171      SongTitle()
172      // 通过Blank组件实现拉伸能力
173      Blank()
174      Image($r('app.media.icon_play')).height(26).width(26).margin({right: 16})
175      Image($r('app.media.ic_next')).height(24).width(24).margin({right: 16})
176      Image($r('app.media.ic_Music_list')).height(24).width(24)
177    }
178    .width('100%')
179    .height(48)
180    .backgroundColor('#D8D8D8')
181    .alignItems(VerticalAlign.Center)
182    .padding({left: 16, right: 16})
183  }
184}
185```
186
187
188## 运行效果
189
190将页面中的四部分组合在一起,即可显示完整的页面。
191
192其中歌单封面和歌单列表这两部分的相对位置,在sm断点下是上下排布,在md和lg断点下是左右排布,也可以用栅格来实现目标效果。
193
194  |  | sm | md | lg |
195| -------- | -------- | -------- | -------- |
196| 效果图 | ![zh-cn_image_0000001381026609](figures/zh-cn_image_0000001381026609.jpg) | ![zh-cn_image_0000001381145789](figures/zh-cn_image_0000001381145789.jpg) | ![zh-cn_image_0000001329666380](figures/zh-cn_image_0000001329666380.jpg) |
197| 栅格布局图 | ![zh-cn_image_0000001330145976](figures/zh-cn_image_0000001330145976.png) | ![zh-cn_image_0000001381385985](figures/zh-cn_image_0000001381385985.png) | ![zh-cn_image_0000001381226321](figures/zh-cn_image_0000001381226321.png) |
198
199
200```
201@Component
202export default struct MusicContent {
203  ...
204  build() {
205    GridRow() {
206      // 歌单封面
207      GridCol({ span: { xs: 12, sm: 12, md: 6, lg: 4 } }) {
208        PlayListCover()
209      }
210      // 歌单列表
211      GridCol({ span: { xs: 12, sm: 12, md: 6, lg: 8 } }) {
212        PlayList()
213      }
214    }
215    .height('100%')
216  }
217}
218```
219
220最后将页面各部分组合在一起即可。
221
222
223```
224@Entry
225@Component
226struct Index {
227  build() {
228    Column() {
229      // 标题栏
230      PlayListHeader()
231      // 歌单
232      MusicContent()
233      // 播放控制栏
234      MusicBar()
235    }.width('100%').height('100%')
236  }
237}
238```
239
240音乐专辑页面的运行效果如下所示。
241
242  | sm | md | lg |
243| -------- | -------- | -------- |
244| ![MusicAlbum_sm_running](figures/MusicAlbum_sm_running.png) | ![MusicAlbum_md_running](figures/MusicAlbum_md_running.png) | ![MusicAlbum_lg_running](figures/MusicAlbum_lg_running.png) |
245