一多音樂專輯主頁
介紹
本示例展示了音樂專輯主頁。
- 頭部返回欄: 因元素單一、位置固定在頂部,因此適合采用自適應拉伸,充分利用頂部區域。
- 專輯封面: 使用柵格組件控制占比,在小尺寸屏幕下封面圖與歌單描述在同一行。
- 歌曲列表: 使用柵格組件控制寬度,在小尺寸屏幕下寬度為屏幕的100%,中尺寸屏幕下寬度為屏幕的50%,大尺寸屏幕下寬度為屏幕的75%。
- 播放器: 采用自適應拉伸,充分使用底部區域。
本示例使用一次開發多端部署中介紹的自適應布局能力和響應式布局能力進行多設備(或多窗口尺寸)適配,保證應用在不同設備或不同窗口尺寸下可以正常顯示。
用到了媒體查詢接口[@ohos.mediaquery]。
效果預覽
本示例在預覽器中的效果:
本示例在開發板上運行的效果:
使用說明:
1.啟動應用,查看本應用在全屏狀態下的效果。
2.在應用頂部,下滑出現窗口操作按鈕。(建議通過外接鼠標操作,接入鼠標只需要將鼠標移動至頂部即可出現窗口)
3.點擊懸浮圖標,將應用懸浮在其他界面上顯示。
4.拖動應用懸浮窗口的四個頂角,改變窗口尺寸,觸發應用顯示刷新。改變窗口尺寸的過程中,窗口尺寸可能超出屏幕尺寸,此時在屏幕中只能看到應用部分區域的顯示。可以通過移動窗口位置,查看應用其它區域的顯示。
開發前請熟悉鴻蒙開發指導文檔 :[gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md
]
工程目錄
AppMarket/entry/src/main/ets/
|---model
| |---MediaData.ets // 主頁用到的圖片資源
| |---SongList.ets // 歌曲數據
| |---SongModule.ets // 事件監聽函數模塊
|---pages
| |---index.ets // 首頁
|---common
| |---Content.ets // 內容組件
| |---Header.ets // 標題欄
| |---Player.ets // app模塊(包含安裝,展示圖片,更多功能)
| |---PlayList.ets // 歌單列表
| |---PlayListCover.ets // 歌單封面
具體實現
本示例介紹如何使用自適應布局能力和響應式布局能力適配不同尺寸窗口,將頁面分拆為4個部分。
標題欄
由于在不同斷點下,標題欄始終只顯示“返回按鈕”、“歌單”以及“更多按鈕”,但“歌單”與“更多按鈕”之間的間距不同。
通過柵格實現:將標題欄劃分為“返回按鈕及歌單”和“更多按鈕”兩部分,這兩部分在不同斷點下占據的列數不同。
歌單封面
通過柵格實現歌單封面,它由封面圖片、歌單介紹及常用操作三部分組成這三部分的布局在md和lg斷點下完全相同,但在sm斷點下有較大差異,[源碼參考]。
/*
* Copyright (c) 2022-2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { optionList } from '../model/SongList'
@Component
export default struct PlayListCover {
@State imgHeight: number = 0
@StorageProp('coverMargin') coverMargin: number = 0
@StorageProp('currentBreakpoint') currentBreakpoint: string = 'sm'
@StorageProp('fontSize') fontSize: number = 0
@Builder
CoverImage() {
Stack({ alignContent: Alignment.BottomStart }) {
Image($r('app.media.pic_album'))
.width('100%')
.aspectRatio(1)
.borderRadius(8)
.onAreaChange((oldArea: Area, newArea: Area) = > {
this.imgHeight = newArea.height as number
})
Text($r('app.string.collection_num'))
.letterSpacing(1)
.fontColor('#fff')
.fontSize(this.fontSize - 4)
.translate({ x: 10, y: '-100%' })
}
.width('100%')
.height('100%')
.aspectRatio(1)
}
@Builder
CoverIntroduction() {
Column() {
Text($r('app.string.list_name'))
.opacity(0.9)
.fontWeight(500)
.fontColor('#556B89')
.fontSize(this.fontSize + 2)
.margin({ bottom: 10 })
Text($r('app.string.playlist_Introduction'))
.opacity(0.6)
.width('100%')
.fontWeight(400)
.fontColor('#556B89')
.fontSize(this.fontSize - 2)
}
.width('100%')
.height(this.currentBreakpoint === 'sm' ? this.imgHeight : 70)
.alignItems(HorizontalAlign.Start)
.justifyContent(FlexAlign.Center)
.padding({ left: this.currentBreakpoint === 'sm' ? 20 : 0 })
.margin({
top: this.currentBreakpoint === 'sm' ? 0 : 30,
bottom: this.currentBreakpoint === 'sm' ? 0 : 20
})
}
@Builder
CoverOptions() {
Row() {
ForEach(optionList, item = > {
Column({ space: 4 }) {
Image(item.image).height(30).width(30)
Text(item.text)
.fontColor('#556B89')
.fontSize(this.fontSize - 1)
}
})
}
.width('100%')
.height(70)
.padding({
left: this.currentBreakpoint === 'sm' ? 20 : 0,
right: this.currentBreakpoint === 'sm' ? 20 : 0
})
.margin({
top: this.currentBreakpoint === 'sm' ? 15 : 0,
bottom: this.currentBreakpoint === 'sm' ? 15 : 0
})
.justifyContent(FlexAlign.SpaceBetween)
}
build() {
if (this.currentBreakpoint === 'sm') {
Column() {
GridRow() {
GridCol({ span: { sm: 4, md: 10 }, offset: { sm: 0, md: 1, lg: 1 } }) {
this.CoverImage()
}
GridCol({ span: { sm: 8, md: 10 }, offset: { sm: 0, md: 2, lg: 2 } }) {
this.CoverIntroduction()
}
GridCol({ span: { sm: 12, md: 10 }, offset: { sm: 0, md: 2, lg: 2 } }) {
this.CoverOptions()
}
}
.margin({ left: this.coverMargin, right: this.coverMargin })
.padding({ top: this.currentBreakpoint === 'sm' ? 50 : 70 })
}
} else {
Column() {
GridRow() {
GridCol({ span: { sm: 4, md: 10 }, offset: { sm: 0, md: 1, lg: 1 } }) {
this.CoverImage()
}
GridCol({ span: { sm: 8, md: 10 }, offset: { sm: 0, md: 2, lg: 2 } }) {
this.CoverIntroduction()
}
GridCol({ span: { sm: 12, md: 10 }, offset: { sm: 0, md: 2, lg: 2 } }) {
this.CoverOptions()
}.margin({
top: this.currentBreakpoint === 'sm' ? 15 : 0,
bottom: this.currentBreakpoint === 'sm' ? 15 : 0
})
}
.margin({ left: this.coverMargin, right: this.coverMargin })
.padding({ top: this.currentBreakpoint === 'sm' ? 50 : 70 })
}
.height('100%')
}
}
}
1、在sm斷點下,封面圖片和歌單介紹占滿12列,常用操作此時會自動換行顯示。
2、在lg和md斷點下,封面圖片,歌單和常用操作各占一行中顯示。
歌單列表
通過List組件的lanes屬性實現:在不同斷點下,歌單列表的樣式一致,但sm和md斷點下是歌單列表是單列顯示,lg斷點下是雙列顯示,[源碼參考]。
/*`HarmonyOS與OpenHarmony鴻蒙文檔籽料:mau123789是v直接拿`
* Copyright (c) 2022-2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { songList } from '../model/SongList'
import MyDataSource from '../model/SongModule'
@Component
export default struct PlayList {
@StorageProp('currentBreakpoint') currentBreakpoint: string = 'sm'
@StorageProp('fontSize') fontSize: number = 0
@Consume coverHeight: number
@Builder
PlayAll() {
Row() {
Image($r("app.media.ic_play_all"))
.height(23)
.width(23)
Text($r('app.string.play_all'))
.maxLines(1)
.padding({ left: 10 })
.fontColor('#000000')
.fontSize(this.fontSize)
Blank()
Image($r('app.media.ic_order_play'))
.width(24)
.height(24)
.margin({ right: 16 })
Image($r('app.media.ic_sort_list'))
.height(24)
.width(24)
}
.height(60)
.width('100%')
.padding({ left: 12, right: 12 })
}
@Builder
SongItem(title: string, label: Resource, singer: string) {
Row() {
Column() {
Text(title)
.fontColor('#000000')
.fontSize(this.fontSize)
.margin({ bottom: 4 })
Row() {
Image(label)
.width(16)
.height(16)
.margin({ right: 4 })
Text(singer)
.opacity(0.38)
.fontColor('#000000')
.fontSize(this.fontSize - 4)
}
}
.alignItems(HorizontalAlign.Start)
Blank()
Image($r('app.media.ic_list_more'))
.height(24)
.width(24)
}
.height(60)
.width('100%')
}
build() {
Column() {
this.PlayAll()
Scroll() {
List() {
LazyForEach(new MyDataSource(songList), item = > {
ListItem() {
Column() {
this.SongItem(item.title, item.label, item.singer)
Divider()
.strokeWidth(0.5)
.color('#000')
.opacity(0.1)
}
.width('100%')
.height(50)
.padding({ left: 14, right: 14 })
}
}, item = > item.id.toString())
}
.width('100%')
.lanes(this.currentBreakpoint === 'lg' ? 2 : 1)
}
.height('100%')
.flexGrow(1)
.flexShrink(1)
}
.width('100%')
.height('100%')
.borderRadius({ topLeft: 20, topRight: 20 })
.backgroundColor(Color.White)
.padding({ bottom: this.currentBreakpoint === 'sm' ? this.coverHeight : 0 })
}
}
播放控制欄
通過Blank組件實現拉伸能力:在不同斷點下,播放控制欄顯示的內容完全一致,唯一的區別是歌曲信息與播放控制按鈕之間的間距有差異。
總體運行效果
通過在首頁Column()中引用上述各組件后,可實現首頁的組件整合渲染,即可完成整體頁面開發,[源碼參考]。
/*
* Copyright (c) 2022-2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { songList } from '../model/SongList'
import MyDataSource from '../model/SongModule'
@Component
export default struct PlayList {
@StorageProp('currentBreakpoint') currentBreakpoint: string = 'sm'
@StorageProp('fontSize') fontSize: number = 0
@Consume coverHeight: number
@Builder
PlayAll() {
Row() {
Image($r("app.media.ic_play_all"))
.height(23)
.width(23)
Text($r('app.string.play_all'))
.maxLines(1)
.padding({ left: 10 })
.fontColor('#000000')
.fontSize(this.fontSize)
Blank()
Image($r('app.media.ic_order_play'))
.width(24)
.height(24)
.margin({ right: 16 })
Image($r('app.media.ic_sort_list'))
.height(24)
.width(24)
}
.height(60)
.width('100%')
.padding({ left: 12, right: 12 })
}
@Builder
SongItem(title: string, label: Resource, singer: string) {
Row() {
Column() {
Text(title)
.fontColor('#000000')
.fontSize(this.fontSize)
.margin({ bottom: 4 })
Row() {
Image(label)
.width(16)
.height(16)
.margin({ right: 4 })
Text(singer)
.opacity(0.38)
.fontColor('#000000')
.fontSize(this.fontSize - 4)
}
}
.alignItems(HorizontalAlign.Start)
Blank()
Image($r('app.media.ic_list_more'))
.height(24)
.width(24)
}
.height(60)
.width('100%')
}
build() {
Column() {
this.PlayAll()
Scroll() {
List() {
LazyForEach(new MyDataSource(songList), item = > {
ListItem() {
Column() {
this.SongItem(item.title, item.label, item.singer)
Divider()
.strokeWidth(0.5)
.color('#000')
.opacity(0.1)
}
.width('100%')
.height(50)
.padding({ left: 14, right: 14 })
}
}, item = > item.id.toString())
}
.width('100%')
.lanes(this.currentBreakpoint === 'lg' ? 2 : 1)
}
.height('100%')
.flexGrow(1)
.flexShrink(1)
}
.width('100%')
.height('100%')
.borderRadius({ topLeft: 20, topRight: 20 })
.backgroundColor(Color.White)
.padding({ bottom: this.currentBreakpoint === 'sm' ? this.coverHeight : 0 })
}
}
審核編輯 黃宇
-
鴻蒙
+關注
關注
57文章
2321瀏覽量
42749 -
鴻蒙OS
+關注
關注
0文章
188瀏覽量
4371
發布評論請先 登錄
相關推薦
評論