【续】鸿蒙 5.0 快速入门
七、黑马云音乐【续】
7.5 发现组件
7.5.1 概述
- 功能结构:

❓ 梳理:前面我们实现跳转用到了
Navigation组件,然后对于广告页 Start&布局页 Layout都是作为子页存在,那么它们是用
NavDestination组件。那么又在布局页内部有四个组件,我们是通过条件渲染的方式来展示的。现在对于发现组件又需要跳转到一个新的页面
播放页 Play那么应该怎么做呢??难道还来
NavDestination??这是不行的 ..

解决方案 :使用
AppStorageV2(应用全局UI状态存储) 来存储到之前的控制跳转的对象pathStack,我们在其他页面拿到这个对象就可以用它来实现跳转了
7.5.2 准备工作
在这个部分,我们专注于做
跳转这个功能的实现,那么其他前面涉及到的基础代码就先可以忽略,直接复制下面给出的代码就好了
- 1). 在
src/main/ets下面创建一个models目录 , 然后新建一个music.ets文件,放入下面代码:
export interface SongItemType {
img: string
name: string
author: string
url: string
id:string
}说明:因为这个接口在发现页和播放页都要使用,那么直接抽取在文件里
export给外面导入就好了
- 2). 发现页:
src/main/ets/pages/Find.ets
📌 点击展开代码
import { SongItemType } from "../models/music"
@ComponentV2
export struct Find {
songs: SongItemType[] = [
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/0.jpg',
name: '直到世界的尽头',
author: 'WANDS',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/0.m4a',
id: '0000'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/1.jpg',
name: '画',
author: '赵磊',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/1.mp3',
id: '0001'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/2.jpg',
name: 'Sweet Dreams',
author: 'TPaul Sax / Eurythmics',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/2.mp3',
id: '0002'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/3.jpg',
name: '奢香夫人',
author: '凤凰传奇',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/3.m4a',
id: '0003'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/4.jpg',
name: '空心',
author: '光泽',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/4.mp3',
id: '0004'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/5.jpg',
name: '反转地球',
author: '潘玮柏',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/5.mp3',
id: '0005'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/6.jpg',
name: 'No.9',
author: 'T-ara',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/6.m4a',
id: '0006'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/7.jpg',
name: '孤独',
author: 'G.E.M.邓紫棋',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/7.m4a',
id: '0007'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/8.jpg',
name: 'Lose Control',
author: 'Hedley',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/8.m4a',
id: '0008'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/9.jpg',
name: '倩女幽魂',
author: '张国荣',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/9.m4a',
id: '0009'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/10.jpg',
name: '北京北京',
author: '汪峰',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/10.m4a',
id: '0010'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/11.jpg',
name: '苦笑',
author: '汪苏泷',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/11.mp3',
id: '0011'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/12.jpg',
name: '一生所爱',
author: '卢冠廷 / 莫文蔚',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/12.m4a',
id: '0012'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/13.jpg',
name: '月半小夜曲',
author: '李克勤',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/13.mp3',
id: '0013'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/14.jpg',
name: 'Rolling in the Deep',
author: 'Adele',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/14.m4a',
id: '0014'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/15.jpg',
name: '海阔天空',
author: 'Beyond',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/15.m4a',
id: '0015'
}
]
build() {
Column() {
Text('猜你喜欢')
.fontColor('#fff')
.width('100%')
.margin({ bottom: 10 })
List() {
ForEach(this.songs, (item: SongItemType, index: number) => {
ListItem() {
Row() {
// 图
Stack() {
Image(item.img)
.width(80)
.border({ radius: 8 })
.margin({ right: 10 })
Image($r('app.media.wave'))
.width(24)
}
// 字
Column() {
Text(item.name)
.fontColor('#F3F3F3')
.width('100%')
.fontWeight(700)
.margin({ bottom: 15 })
Row() {
Text('VIP')
.fontColor('#9A8E28')
.border({ width: 1, color: '#9A8E28', radius: 12 })
.padding({
left: 5,
right: 5,
top: 3,
bottom: 3
})
.margin({ right: 10 })
Text(item.author)
.fontColor('#696969')
}
.width('100%')
}
.layoutWeight(1)
// 更多
Image($r('app.media.ic_more'))
.width(24)
.fillColor('#FEFEFE')
}
.width('100%')
.height(80)
// .backgroundColor(Color.Pink)
.margin({ bottom: 10 })
}
})
}
.scrollBar(BarState.Off)
}
.width('100%')
.height('100%')
.backgroundColor('#131313')
.padding({ left: 10, right: 10 })
// 扩充组件安全区域
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
}
}- 2). 播放页:
src/main/ets/pages/Play.ets
📌 点击展开代码
import { SongItemType } from "../models/music"
// 跳转页面入口函数
@Builder
export function PlayBuilder() {
Play()
}
@ComponentV2
struct Play {
@Local panelHeight: string = '0%'
@Local panelOpacity: number = 0
pathStack : NavPathStack = new NavPathStack();
songs: SongItemType[] = [
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/0.jpg',
name: '直到世界的尽头',
author: 'WANDS',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/0.m4a',
id: '0000'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/1.jpg',
name: '画',
author: '赵磊',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/1.mp3',
id: '0001'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/2.jpg',
name: 'Sweet Dreams',
author: 'TPaul Sax / Eurythmics',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/2.mp3',
id: '0002'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/3.jpg',
name: '奢香夫人',
author: '凤凰传奇',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/3.m4a',
id: '0003'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/4.jpg',
name: '空心',
author: '光泽',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/4.mp3',
id: '0004'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/5.jpg',
name: '反转地球',
author: '潘玮柏',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/5.mp3',
id: '0005'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/6.jpg',
name: 'No.9',
author: 'T-ara',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/6.m4a',
id: '0006'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/7.jpg',
name: '孤独',
author: 'G.E.M.邓紫棋',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/7.m4a',
id: '0007'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/8.jpg',
name: 'Lose Control',
author: 'Hedley',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/8.m4a',
id: '0008'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/9.jpg',
name: '倩女幽魂',
author: '张国荣',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/9.m4a',
id: '0009'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/10.jpg',
name: '北京北京',
author: '汪峰',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/10.m4a',
id: '0010'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/11.jpg',
name: '苦笑',
author: '汪苏泷',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/11.mp3',
id: '0011'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/12.jpg',
name: '一生所爱',
author: '卢冠廷 / 莫文蔚',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/12.m4a',
id: '0012'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/13.jpg',
name: '月半小夜曲',
author: '李克勤',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/13.mp3',
id: '0013'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/14.jpg',
name: 'Rolling in the Deep',
author: 'Adele',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/14.m4a',
id: '0014'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/15.jpg',
name: '海阔天空',
author: 'Beyond',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/15.m4a',
id: '0015'
}
]
// 当前播放的歌曲
@Local
playState:SongItemType = this.songs[0]
@Builder
deleteButton(index: number) {
Button('删除')
.backgroundColor('#ec5c87')
.fontColor('#fff')
.width(80)
.height('100%')
.type(ButtonType.Normal)
}
number2time(number: number) {
// 毫秒 → 秒 → 分+秒; 先判断是否大于1分钟
if (number > 60 * 1000) {
const s = Math.floor(number/1000%60)
const m = Math.floor(number/1000/60)
const second = s.toString().padStart(2, '0')
const minute = m.toString().padStart(2, '0')
return minute + ':' + second
} else {
const s = Math.floor(number/1000%60)
const second = s.toString().padStart(2, '0')
return '00:' + second
}
}
build() {
NavDestination() {
Stack({ alignContent: Alignment.Bottom }) {
// 播放
Stack() {
// 变色背景
Image(this.playState.img)
.width('100%')
.height('100%')
.blur(1000)
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
// 内容
Column() {
// // 播放界面
Column() {
// 图片
Stack({ alignContent: Alignment.Top }) {
Row() {
Row() {
Image(this.playState.img)
.width('70%')
.borderRadius(400)
}
.backgroundImage($r('app.media.ic_cd'))
.backgroundImageSize(ImageSize.Cover)
.justifyContent(FlexAlign.Center)
.width('100%')
.borderRadius(400)
.clip(true)
.aspectRatio(1)
}
.margin({
top: 50
})
.width('90%')
.aspectRatio(1)
.justifyContent(FlexAlign.Center)
.padding(24)
// 唱针
Image($r('app.media.ic_stylus'))
.width(200)
.aspectRatio(1)
.rotate({
angle: -55,
centerX: 100,
centerY: 30
})
.animation({
duration: 500
})
}
// 歌曲信息
Stack() {
// 第一个
Column({ space: 8 }) {
Text(this.playState.name)
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#4bb0c4')
Text(this.playState.author)
.fontSize(18)
.fontColor('#4bb0c4')
}
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
.zIndex(1)
// 第二个
Column({ space: 8 }) {
Text(this.playState.name)
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#ec5c87')
Text(this.playState.author)
.fontSize(18)
.fontColor('#ec5c87')
}
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
.zIndex(2)
// 第三个
Column({ space: 8 }) {
Text(this.playState.name)
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
Text(this.playState.author)
.fontSize(18)
.fontColor(Color.White)
}
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
.zIndex(3)
}
.layoutWeight(1)
// 操作
Row() {
Badge({ value: '99+', style: { badgeSize: 12, badgeColor: '#45CCCCCC' } }) {
Image($r("app.media.ic_like"))
.fillColor(Color.White)
.width(24)
}
Badge({ value: '10W', style: { badgeSize: 12, badgeColor: '#45cccccc' } }) {
Image($r("app.media.ic_comment_o"))
.fillColor(Color.White)
.width(18)
}
Badge({ value: 'hot', style: { badgeSize: 12, badgeColor: '#a8ff3131' } }) {
Image($r("app.media.ic_bells_o"))
.fillColor(Color.White)
.width(24)
}
Badge({ value: 'vip', style: { badgeSize: 12, badgeColor: '#b7efd371' } }) {
Image($r("app.media.ic_download_o"))
.fillColor(Color.White)
.width(24)
}
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)
// 播放
Column() {
// 进度条
Row({ space: 4 }) {
Text("00:00")
.fontSize(12)
.fontColor(Color.White)
Slider({
value: 0,
min: 0,
max: 0
})
.layoutWeight(1)
.blockColor(Color.White)
.selectedColor(Color.White)
.trackColor('#ccc5c5c5')
.trackThickness(2)
Text("00:00")
.fontSize(12)
.fontColor(Color.White)
}
.width('100%')
.padding(24)
// 切换
Row() {
Image($r('app.media.ic_auto'))
.fillColor(Color.White)
.width(30)
Image($r("app.media.ic_prev"))
.fillColor(Color.White)
.width(30)
// 播放按钮
Image($r('app.media.ic_paused'))
.fillColor(Color.White)
.width(50)
// 下一首
Image($r('app.media.ic_next'))
.fillColor(Color.White)
.width(30)
// 播放列表
Image($r('app.media.ic_song_list'))
.fillColor(Color.White)
.width(30)
.onClick(() => {
this.panelHeight = '50%'
this.panelOpacity = 1
})
}
.width('100%')
.padding({
bottom: 24
})
.justifyContent(FlexAlign.SpaceAround)
}
.width('100%')
}
.layoutWeight(1)
.width('100%')
}
}
.width('100%')
.height('100%')
.backgroundColor(Color.Transparent)
// 列表
Column() {
Column() {
}
.width('100%')
.layoutWeight(1)
.onClick(() => {
this.panelHeight = '0%'
this.panelOpacity = 0
})
Column() {
Row() {
Row() {
Image($r("app.media.ic_play"))
.width(20)
.fillColor('#ff5186')
}
.width(50)
.aspectRatio(1)
.justifyContent(FlexAlign.Center)
Row({ space: 8 }) {
Text(`播放列表 (0)`)
.fontColor(Color.White)
.fontSize(14)
}
.layoutWeight(1)
Image($r('app.media.ic_close'))
.fillColor('#ffa49a9a')
.width(24)
.height(24)
.margin({ right: 16 })
.onClick(() => {
this.panelHeight = '0%'
this.panelOpacity = 0
})
}
.width('100%')
.backgroundColor('#ff353333')
.padding(8)
.border({
width: { bottom: 1 },
color: '#12ec5c87'
})
.borderRadius({
topLeft: 4,
topRight: 4
})
// 播放列表
List() {
ForEach(this.songs, (item: SongItemType, index: number) => {
ListItem() {
Row() {
Row() {
Text((index + 1).toString())
.fontColor('#ffa49a9a')
}
.width(50)
.aspectRatio(1)
.justifyContent(FlexAlign.Center)
// 列表
Row({ space: 10 }) {
Column() {
Text(item.name)
.fontSize(14)
.fontColor('#ffa49a9a')
Text(item.author)
.fontSize(12)
.fontColor( Color.Gray)
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
.justifyContent(FlexAlign.Center)
}
.layoutWeight(1)
Image($r('app.media.ic_more'))
.width(24)
.height(24)
.margin({ right: 16 })
.fillColor(Color.Gray)
}
.alignItems(VerticalAlign.Center)
}
.swipeAction({
end: this.deleteButton(index)
})
.border({
width: { bottom: 1 },
color: '#12ec5c87'
})
})
}
.layoutWeight(1)
.backgroundColor('#ff353333')
}
.height(400)
}
.height(this.panelHeight)
// .height('100%')
.animation({
duration: 300
})
.backgroundColor('#ff353333')
.opacity(this.panelOpacity)
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
}
.width('100%')
.height('100%')
.backgroundColor(Color.Transparent)
}
.onReady((context: NavDestinationContext) => {
//当组件准备好的时候,把自己放进控制跳转对象里,那么自己就是能跳转的对象之一
this.pathStack = context.pathStack
})
.hideTitleBar(true) // 隐藏标题栏
}
}7.5.3 跳转
- 1). 路由表
router_map.json增加播放页 Play

- 2). 使用
AppstorageV2共享控制跳转的对象pathStack:
📌 官方文档:
- 介绍:
为了增强状态管理框架对
应用全局UI状态变量存储的能力,开发者可以使用
AppStorageV2存储 应用全局UI状态变量 数据。AppStorageV2是提供状态变量在应用级全局共享的能力,开发者可以通过
connect绑定同一个key,进行跨ability的数据共享。
概述:
AppStorageV2是在应用 U I启动时 会被创建的单例。它的目的是为了提供应用状态数据的中心存储,这些状态数据在应用级别都是可访问的。
AppStorageV2将在应用运行过程保留其数据。数据通过唯一的键字符串值访问。需要注意的是,
AppStorage与AppStorageV2之间的数据互不共享。AppStorageV2可以修改connect的返回值,实现与UI组件的同步。AppStorageV2支持应用的 主线程 内多个UIAbility实例间的状态共享。
使用说明:
connect:创建或获取存储的数据。说明
若未指定 key,使用第二个参数作为默认构造器;
否则使用第三个参数作为默认构造器(第二个参数非法也使用第三个参数作为默认构造器)。
确保数据已经存储在 AppStorageV2 中,可省略默认构造器,获取存储的数据;
否则必须指定默认构造器,不指定将导致应用异常。
同一个 key,connect 不同类型的数据会导致应用异常,应用需要确保类型匹配。
key 建议使用有意义的值,可由字母、数字、下划线组成,长度不超过255,
使用非法字符或空字符的行为是未定义的。
关联 @Observed 对象时,由于该类型的 name 属性未定义,
需要指定key或者自定义name属性。
remove:删除指定 key 的存储数据。- 删除 AppStorageV2 中不存在的 key 会报警告。
keys:返回所有 AppStorageV2 中的 key。
以上接口详细描述请参考 状态管理API指南。
- 主要关于
connect:
static connect<T extends object>(
type: TypeConstructorWithArgs<T>, //指定的类型
keyOrDefaultCreator?: string | StorageDefaultCreator<T>, //指定一个key
defaultCreator?: StorageDefaultCreator<T> //初始值(构造器)
): T | undefined
index.ets
import { AppStorageV2 } from '@kit.ArkUI';
@Entry
@Component
struct Index {
//我们在子页的跳转对象叫 pathStack , 所以这里也注意修改成 pathStack
// pathStack : NavPathStack = new NavPathStack();
pathStack : NavPathStack = AppStorageV2.connect(NavPathStack,'navStack',() => new NavPathStack())!
build() {
Navigation(this.pathStack){
}.onAppear(() => {
//通过名称Name推路径(其实就是跳转页面)
//软件一打开应当打开的是广告页,所以跳转
this.pathStack.pushPathByName("Start", null, false);
})
.hideNavBar(true) //不会把自己放到控制跳转对象(本质是不会进Stack栈)里面 -> 点返回不会返回到这个导航页
}
}📌 说明:
- 我们经常会看到
?或者!, 比如上面的: (❓ 那么到底都是什么意思)
pathStack : NavPathStack = AppStorageV2.connect(NavPathStack,'navStack',() => new NavPathStack())!这句代码最后面的
!
- 可选属性 / 可选参数 :
?
interface Person {
name: string;
age?: number; // age 是可选属性
}
let person: Person = {
name: "Alice"
};
console.log(person.age); // 输出:undefined// 定义一个函数,其中第二个参数是可选的
function greet(name: string, greeting?: string) {
if (greeting) {
console.log(`${greeting}, ${name}!`);
} else {
console.log(`Hello, ${name}!`);
}
}
// 调用函数时,可以省略可选参数
greet("Alice"); // 输出:Hello, Alice!
greet("Bob", "Good morning"); // 输出:Good morning, Bob!可选链(
Optional Chaining)运算符?.它用于在访问对象的属性或调用对象的方法时,
确保对象不是
null或undefined,从而避免运行时错误。
type Person = {
name: string;
address?: {
city: string;
zipCode: string;
};
};📌 注意 :
type和interface的区别interface只能用于定义对象类型和函数类型。type可以用于定义任何类型,包括基本类型、联合类型、元组等。interface支持扩展和合并,而type不支持。
在上面的代码块中如果要访问对象的属性:
person.address?.city:如果person.address是null或undefined,则不会尝试访问
city属性,直接返回undefined。如果
person.address存在,则正常访问city属性。
非空断言:
!当你确信一个值不会是
null或undefined,但TypeScript编译器无法推断这一点时,可以使用
!来断言该值是非空的。使用
!时需要谨慎,因为如果值实际上是null或undefined,运行时会抛出错误。
- 3). 其它页面使用
AppstorageV2使用共享的控制跳转对象pathStack:
在其他页面,比如
Find.ets写上同样的下面的代码:
pathStack : NavPathStack = AppStorageV2.connect(NavPathStack,'navStack',() => new NavPathStack())!
- 4). 使用控制跳转对象实现
Find.ets跳转到Play.ets:

说明:现在播放页的页面是写死的,无论点哪个都是同一首歌的信息,后面再进行优化就好了
7.5.4 基础播放功能
- 播放功能:(使用
AVPlayer播放音频 (ArkTS) ) AVPlayer播放流程:AVPlayer 官方文档- 创建
AVPlayer - 设置播放资源
- 设置播放参数(音量/倍速/焦点模式)
- 播放控制(播放/暂停/跳转/停止)
- 重置
- 销毁资源
- 创建
.BVdlozA8.png)

说明:像上面这样,这种具有大量数据和功能的,
我们最好应该统一管理比较好,所以最好
封装播放器工具类

- 1). 创建一个
utils目录, 下面创建一个AvPlayerManager.ets:

- 2). 编写
AvPlayerManager.ets基本的代码如init()实现基础的播放功能:
- 会遇到如下问题:

- 说明:需要等待的话,那么我们就需要用到
await关键字,并且需要用async修饰用到await的方法
- 示例代码:
➡️ AvPlayerManager.ets
import { media } from '@kit.MediaKit'
import { SongItemType } from '../models/music'
class AvPlayerManager{
//属性 + 方法
//播放器
player : media.AVPlayer | null = null
//定义初始化方法:创建播放器 + 监听播放器状态
async init() {
if(!this.player){
this.player = await media.createAVPlayer()
}
//监听状态
this.player.on('stateChange',(state) => {
if(state === 'initialized') {
this.player?.prepare()
}else if(state === 'prepared'){
this.player?.play()
}
})
}
//播放歌曲 → 设置播放资源
singPlay (song:SongItemType){
this.player!.url = song.url
}
}
//new 出来一个对象导出给其它地方使用
export const playManager : AvPlayerManager = new AvPlayerManager()- 3). 下一步我们需要考虑的是打开软件的那一刻,把这个
init()给调用一下
➡️ EntryAbility.ets

- 4). 然后就是在发现页点击音乐
onClick()那一刻跳转到播放器并且播放对应音乐:

- 5). 接下来就是重新运行项目,打开模拟器点击一首歌就会发现已经可以播放音乐了
7.5.5 共享播放数据
7.5.5.1 方案
在
7.5.4我们做了基础的播放功能,可以发现,无论我们点哪个都是固定的一个界面。应该要实现的是点了哪首歌就显示哪首歌相关的信息,而且,这个项目后面还有播控中心,
这也需要用到播放的相关歌的信息,而且
AvPlayer是不是也需要用到相关歌的信息 ...

那么就会想到 .............

- 共享播放数据的使用思路:

7.5.5.2 共享音乐图片
- 1). 在
models下封装一个共享播放数据的类
➡️ models/globalMusic.ets
export class GlobalMusic {
img: string = "" // 音乐封面
name: string = "" // 音乐名称
author: string = "" // 作者
url: string = "" // 当前播放链接
time: number = 0 // 播放时间
duration: number = 0 // 歌曲总时长
}- 2). 在
AvplayerManager.ets里面使用AppstorageV2全局共享播放数据

//共享播放数据
currentSong : GlobalMusic = AppStorageV2.connect(GlobalMusic,'SONG_KEY',() => new GlobalMusic())!- 3). 先初步的在
SingPlay里面传递播放的img给全局的播放数据:

4). 接下来就是哪里需要这个共享数据就哪里取 → 播放页需要:
当我们打开复制的
Play页面的时候 → 破案了,之前写死的数据如下:

- 修改为
AppstorageV2共享出来的数据:

- 5). 重新运行项目,测试:

发现图片已经是我们点击的那个音乐对应的图片了,其他相关数据后续继续改就好了
7.5.5.3 共享歌曲时间
- 任务概述:

- 实现思路:

- 先尝试着能不能监听到歌曲的总时长(❗ 剧透:会出现意外):
➡️ AvPlayManager.ets

- 说明:之前复制的代码有一个函数
number2time作用是:把 毫秒(计算机看) 转化为 分+秒(人看)

➡️ Play.ets

- 修改后:

修改后总时长不会发生改变,这就是前面说的 "❗ 意外"
说明:简单的
@AppstorageV2无法深度地监测到类中属性值的变化。(官方文档:那么为了增强状态管理框架对类对象中属性的观测能力,
开发者可以使用
@ObservedV2装饰器和@Trace装饰器装饰类以及类中的属性。)
➡️ 修改
globalMusic.ets
@ObservedV2
export class GlobalMusic {
@Trace img: string = "" // 音乐封面
@Trace name: string = "" // 音乐名称
@Trace author: string = "" // 作者
@Trace url: string = "" // 当前播放链接
@Trace time: number = 0 // 播放时间
@Trace duration: number = 0 // 歌曲总时长
}- 重新运行项目,测试:

可以发现效果实现了!
- 接下来就是实现监听播放时长的变化:
➡️ AvPlayManager.ets

➡️ Play.ets

测试:

- 接下来就是修改一下
进度条的部分:

那么先在 AvPlayerManager.ets 实现一下跳转的方法,然后再后面在 Slider.onChange() 这里调用
➡️ AvPlayerManager.ets

➡️ Play.ets

重新运行项目,测试:

7.5.6 播控功能
7.5.6.1 歌曲列表
- 任务:点击歌曲,添加歌曲到播放列表并播放歌曲

- 1). 首先我们需要在共享歌曲数据
GlobalMusic这里添加两个属性:
import { SongItemType } from "./music"
@ObservedV2
export class GlobalMusic {
@Trace img: string = "" // 音乐封面
@Trace name: string = "" // 音乐名称
@Trace author: string = "" // 作者
@Trace url: string = "" // 当前播放链接
@Trace time: number = 0 // 播放时间
@Trace duration: number = 0 // 歌曲总时长
//歌曲列表
@Trace playIndex : number = 0 // 当前播放的音乐
@Trace playList : SongItemType[] = [] // 播放列表
}- 2). 在
AvPlayerManager.ets里面播放歌曲的方法singPlay()做修改:
singPlay (song:SongItemType){
// 是否在列表里 → some 检查数组里面的数据是否存在满足条件的 → 只要有一个满足 some 返回 true
const inList = this.currentSong.playList.some(item => item.id === song.id)
if (inList) {
//在列表里面 → 是否是正在播放的 → currentSong url === song.url
if (this.currentSong.url === song.url) {
this.player?.play()
} else {
//设置新的索引 → 切歌
this.currentSong.playIndex = this.currentSong.playList.findIndex(item => item.id === song.id)
//切歌 ?
}
} else {
//不在列表里面 → 添加到列表 + 切换歌曲
this.currentSong.playList.unshift(song) // 把要播放的歌添加到队首
this.currentSong.playIndex = 0
//切换歌曲 ?
}
}目前切歌的方法我们还没实现,暂时是这样的代码
- 3). 补充切换歌曲的方法
changeSong()
(说明:这里同时也补充了前面给共享音乐数据其他属性赋值 , 前面只赋值了
img)
//切换歌曲
async changeSong() {
await this.player?.reset()
this.currentSong.duration = 0
this.currentSong.time = 0
this.currentSong.img = this.currentSong.playList[this.currentSong.playIndex].img
this.currentSong.name = this.currentSong.playList[this.currentSong.playIndex].name
this.currentSong.author = this.currentSong.playList[this.currentSong.playIndex].author
this.currentSong.url = this.currentSong.playList[this.currentSong.playIndex].url
this.player!.url = this.currentSong.url
}- 4). 补充切换歌曲方法到
singPlay()
singPlay (song:SongItemType){
// 是否在列表里 → some 检查数组里面的数据是否存在满足条件的 → 只要有一个满足 some 返回 true
const inList = this.currentSong.playList.some(item => item.id === song.id)
if (inList) {
//在列表里面 → 是否是正在播放的 → currentSong url === song.url
if (this.currentSong.url === song.url) {
this.player?.play()
} else {
//设置新的索引 → 切歌
this.currentSong.playIndex = this.currentSong.playList.findIndex(item => item.id === song.id)
//切歌
this.changeSong()
}
} else {
//不在列表里面 → 添加到列表 + 切换歌曲
this.currentSong.playList.unshift(song) // 把要播放的歌添加到队首
this.currentSong.playIndex = 0
//切换歌曲
this.changeSong()
}
}- 5). 重新运行项目,在模拟器测试,目前已经可以完成简单的切歌了
7.5.6.2 播放&暂停

- 1). 先修改共享歌曲数据
GlobalMusic
import { SongItemType } from "./music"
@ObservedV2
export class GlobalMusic {
@Trace img: string = "" // 音乐封面
@Trace name: string = "" // 音乐名称
@Trace author: string = "" // 作者
@Trace url: string = "" // 当前播放链接
@Trace time: number = 0 // 播放时间
@Trace duration: number = 0 // 歌曲总时长
//歌曲列表
@Trace playIndex : number = 0 // 当前播放的音乐
@Trace playList : SongItemType[] = [] // 播放列表
//播放&暂停
@Trace isPlay : boolean = false // 播放的状态
}- 2). 修改
AvPlayerManager.ets需要更新播放状态的地方,并且添加暂停的方法paused()
import { media } from '@kit.MediaKit'
import { GlobalMusic } from '../models/globalMusic'
import { SongItemType } from '../models/music'
import { AppStorageV2 } from '@kit.ArkUI'
class AvPlayerManager{
//属性 + 方法
//播放器
player : media.AVPlayer | null = null
//共享播放数据
currentSong : GlobalMusic = AppStorageV2.connect(GlobalMusic,'SONG_KEY',() => new GlobalMusic())!
//定义初始化方法:创建播放器 + 监听播放器状态
async init() {
if(!this.player){
this.player = await media.createAVPlayer()
}
//监听状态
this.player.on('stateChange',(state) => {
if(state === 'initialized') {
this.player?.prepare()
}else if(state === 'prepared'){
this.player?.play()
this.currentSong.isPlay = true
}
})
//监听歌曲总时长
this.player.on('durationUpdate',(duration) => {
//总时长有变化更新到全局共享数据
this.currentSong.duration = duration
})
//监听播放时长的变化
this.player.on('timeUpdate',(time) => {
this.currentSong.time = time
})
}
//播放歌曲 → 设置播放资源
// singPlay (song:SongItemType){
// this.player!.url = song.url
// this.currentSong.img = song.img
// }
singPlay (song:SongItemType){
// 是否在列表里 → some 检查数组里面的数据是否存在满足条件的 → 只要有一个满足 some 返回 true
const inList = this.currentSong.playList.some(item => item.id === song.id)
if (inList) {
//在列表里面 → 是否是正在播放的 → currentSong url === song.url
if (this.currentSong.url === song.url) {
this.player?.play()
this.currentSong.isPlay = true
} else {
//设置新的索引 → 切歌
this.currentSong.playIndex = this.currentSong.playList.findIndex(item => item.id === song.id)
//切歌
this.changeSong()
}
} else {
//不在列表里面 → 添加到列表 + 切换歌曲
this.currentSong.playList.unshift(song) // 把要播放的歌添加到队首
this.currentSong.playIndex = 0
//切换歌曲
this.changeSong()
}
}
//切换歌曲
async changeSong() {
await this.player?.reset()
this.currentSong.duration = 0
this.currentSong.time = 0
this.currentSong.img = this.currentSong.playList[this.currentSong.playIndex].img
this.currentSong.name = this.currentSong.playList[this.currentSong.playIndex].name
this.currentSong.author = this.currentSong.playList[this.currentSong.playIndex].author
this.currentSong.url = this.currentSong.playList[this.currentSong.playIndex].url
this.player!.url = this.currentSong.url
}
//跳转进度 seek
seekPlay(value : number){
this.player?.seek(value)
}
//暂停播放
paused() {
this.player?.pause() //播放器暂停
this.currentSong.isPlay = false //修改状态
}
}
//new 出来一个对象导出给其它地方使用
export const playManager : AvPlayerManager = new AvPlayerManager()- 3). 在
Play.ets做更改,比如暂停播放图片的更改,还有点击事件:

- 5). 重新运行项目,在模拟器测试,目前已经可以暂停&播放了
- 6). 接着还有一个问题就是:所有歌都有
wave.gif, 我们需要像之前一样添加一个条件渲染

- 6.1). 把全局共享音乐数据拿到发现页:

- 6.2). 条件渲染
wave.gif:

- 7). 重新运行项目,在模拟器测试,就可以实现播放的歌曲才有渲染效果:

7.5.6.3 上一首&下一首

- 1). 在
AvPlayerManager.ets增加 上一首 & 下一首 的方法:

- 2). 在播放页
Play.ets对应位置调用方法:

- 3). 重新运行项目,打开模拟器测试,记得要先选几首歌(之前的逻辑是选歌才能进数组,视觉数组是假的)
发现切歌是实现了,但是还是会有瑕疵, 这里先暂时不管,后面再根据实际的歌曲软件进行优化...
7.5.6.4 切换播放模式
- 思路:

- 1). 先修改共享歌曲数据
GlobalMusic:
import { SongItemType } from "./music"
@ObservedV2
export class GlobalMusic {
@Trace img: string = "" // 音乐封面
@Trace name: string = "" // 音乐名称
@Trace author: string = "" // 作者
@Trace url: string = "" // 当前播放链接
@Trace time: number = 0 // 播放时间
@Trace duration: number = 0 // 歌曲总时长
//歌曲列表
@Trace playIndex : number = 0 // 当前播放的音乐
@Trace playList : SongItemType[] = [] // 播放列表
//播放&暂停
@Trace isPlay : boolean = false // 播放的状态
//播放模式
@Trace playMode : 'auto' | 'random' | 'repeat' = 'auto'
}- 2). 有了对应数据,那么可以在
Play页面实现一下 "视觉上" 的播放模式切换 (条件渲染):

- . 重新运行项目, 在模拟器上测试,就发现实现了 "视觉上" 的播放模式切换
4). 那么现在播放模式还没有真正的改变,只是视觉上实现了。接下来就来实现播放模式的功能,
本质上播放模式影响上一首下一首,我们只需要完成
下一首对应的功能,那么上一首就也完成了 ...


5). 修改
Play页面播放列表的展示,之前一直是this.songs展示全部歌曲,现在我们改成真实在列表里面的歌曲才渲染出来,方便我们调试 ...

- 6). 重新运行项目,使用模拟器,加入一定量的歌曲测试一下刚刚的下一首功能能不能实现
- 7). 然后补充
上一首的逻辑,其实是更简单,因为没有自动播放下一首 → 重复播放的情况:

7.5.6.5 播放列表
- 任务:
- 渲染列表内容(长度,列表歌单内容)
- 点击播放歌曲
- 滑动移除歌曲
- 1). 播放列表长度:

- 2). 播放列表点击切歌:

- 3). 重新运行项目,打开模拟器测试
- 4). 分析删除歌曲:

- 5). 在
globalMusic添加一个重置数据的方法:

- 6). 在
AvplayerManager添加删除歌曲的方法:

记得回退页面要在前面补充上:
pathStack : NavPathStack = AppStorageV2.connect(NavPathStack,'navStack',() => new NavPathStack())!- 7). 在播放页
Play调用方法:


- 8). 重新运行项目,打开模拟器测试功能
7.5.7 播控中心
7.5.7.1 概述
- 功能结构:

- 官方文档:AVPlayer :

7.5.7.2 后台播放
1). 因为需要接入媒体会话
AvSession,所以最好我们像前面的AvPlayer那样,在
utils目录下创建一个工具类AvSessionManager.ets:
import { avSession } from '@kit.AVSessionKit'
class AvSessionManager {
session : avSession.AVSession | null = null
async init(content : Context) {
this.session = await avSession.createAVSession(content,'bgPlay','audio')
}
}
export const sessionManager : AvSessionManager = new AvSessionManager()- 2). 然后再软件启动的时候要进行初始化:

- 3). 申请 长时任务 :
- 3.1). 开通
ohos.permission.KEEP_BACKGROUND_RUNNING权限:

- 3.2). 声明后台模式类型 :

- 3.3). 在
AvSession工具类中增加一个申请长时任务的方法startBackgroundTask()

- 4). 在歌曲播放的时候调用申请长时任务的方法:

- 5). 重新运行项目,打开模拟器测试功能
7.5.7.3 同步播控中心
- 流程:

应用可以通过 setAVMetadata 把会话的一些元数据信息设置给系统,从而在播控中心界面进行展示
- 1). 在
AvSessionManager里面增加一个设置元数据的方法setAVMetadata():

需要获取全局共享歌曲数据:
playState : GlobalMusic = AppStorageV2.connect(GlobalMusic,'SONG_KEY', () => new GlobalMusic())!- 2). 把元数据同步给播控中心 (在换歌的时候需要同步):

- 3). 通过
setAVPlaybackState,把当前的播放状态设置给系统:

- 4). 接下来就要思考一下在哪里调用它:
- 播放
singPlay/ 暂停paused状态改变的时候 - 歌曲播放时间变化
timeUpdate的时候
- 播放



- 5). 注册控制命令(通过
on接口 ), 并激活session:


- 6). 注销会话,释放资源:
➡️ AvSessionManager

➡️ AvPlayerManager


➡️ EntryAbility

- 7). 重新运行项目,打开模拟器测试功能

现在播控中心的功能也是没问题的 ...
7.6 动态&我的
7.6.1 cursor

cursor官方网站:https://cursor.com/cn
- 下载插件:

- 汉化插件:

ArkTS插件:

- 会话规则:


# 基本规则
- 总是用中文回复
- Harmony 项目的 ForEach 循环必须添加类型,没用上的_也必须添加类型
- 对象类型必须用 interface 抽离
- 图片素材在 entry/src/main/resources/base/media,需通过 $r('app.media.') 访问图片
- 组件的必传属性需初始化
- 图片类型为 ResourceStr
- interface 写在最顶部
- build 的根标签如果填充颜色的话,需写上 .expandSafeArea()
- Row、Column 的子组件控制间距尽量用 space 参数,尽量少用 margin
- 白色和黑色用 Color.White 和 Color.Black 代替
# 布局模式
- 线性布局 (Row/Column)
- 层叠布局 (Stack)
- 弹性布局 (Flex)
- 相对布局 (RelativeContainer)
- 栅格布局 (GridRow/GridCol)
- 创建列表 (List)
- 创建网格 (Grid/GridItem)
- 创建轮播 (Swiper)
# 滚动相关
- 支持滚动和滑动的组件 List/ListItem/ListItemGroup、Grid/GridItem、Scroll、Swiper、WaterFlow/FlowItem、ScrollBar、Refresh、SwipeRefresher
- 嵌套滚动可使用 nestedScroll 属性,实现与父组件的滚动联动- 打开项目:

7.6.2 动态
- 效果图:

📌 AI 结构化提示词 = 目标 + 要求 + 注意事项 + 配套素材
📌 提示词
帮我把上面的界面用代码实现出来,
需求如下:
1. 界面为纵向布局
2. 上面是 互动广场 标题
3. 下面是 互动内容,内容根据 下方数据 ForEach 循环渲染
3.1 用户头像 和 描述内容为水平布局
3.2 用户头像 顶部对齐
注意:文字颜色和背景色必须根据上面图片设置,不能随意设置
数据如下:
songs: songItemType[] = [
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/0.jpg',
name: '直到世界的尽头',
author: 'WANDS',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/0.m4a',
id: '0000'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/1.jpg',
name: '画',
author: '赵磊',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/1.mp3',
id: '0001'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/2.jpg',
name: 'Sweet Dreams',
author: 'TPaul Sax / Eurythmics',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/2.mp3',
id: '0002'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/3.jpg',
name: '奢香夫人',
author: '凤凰传奇',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/3.m4a',
id: '0003'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/4.jpg',
name: '空心',
author: '光泽',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/4.mp3',
id: '0004'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/5.jpg',
name: '反转地球',
author: '潘玮柏',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/5.mp3',
id: '0005'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/6.jpg',
name: 'No.9',
author: 'T-ara',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/6.m4a',
id: '0006'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/7.jpg',
name: '孤独',
author: 'G.E.M.邓紫棋',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/7.m4a',
id: '0007'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/8.jpg',
name: 'Lose Control',
author: 'Hedley',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/8.m4a',
id: '0008'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/9.jpg',
name: '倩女幽魂',
author: '张国荣',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/9.m4a',
id: '0009'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/10.jpg',
name: '北京北京',
author: '汪峰',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/10.m4a',
id: '0010'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/11.jpg',
name: '苦笑',
author: '汪苏泷',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/11.mp3',
id: '0011'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/12.jpg',
name: '一生所爱',
author: '卢冠廷 / 莫文蔚',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/12.m4a',
id: '0012'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/13.jpg',
name: '月半小夜曲',
author: '李克勤',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/13.mp3',
id: '0013'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/14.jpg',
name: 'Rolling in the Deep',
author: 'Adele',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/14.m4a',
id: '0014'
},
{
img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/15.jpg',
name: '海阔天空',
author: 'Beyond',
url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/15.m4a',
id: '0015'
}
]
momentList: momentListType[] = [
{
author: 'Feast-aws',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h0.jpg',
content: '二十几岁的你,一无所有,也拥有一切!',
comment: 604,
like: 23382,
song: this.songs[0]
},
{
author: 'CeeYet',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h1.jpg',
content: '画一个姑娘陪着我,再画一个姑娘陪着我',
comment: 1237,
like: 5482,
song: this.songs[1]
},
{
author: 'Z_HOUSC',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h2.jpg',
content: '这个事还得从两个职业选手说起',
comment: 8425,
like: 4234,
song: this.songs[2]
},
{
author: '逆转大师',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h3.jpg',
content: '听奢香夫人,做上岸女人!',
comment: 7658,
like: 11545,
song: this.songs[3]
},
{
author: 'Moriaty',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h4.jpg',
content: '别一厢情愿的付出,他如果拒绝你了就别再纠缠了,别一次次满满热情得到的都是敷衍冷淡的回复,你不该这样的,你应该酷一点,别打扰真的是最好的选择。',
comment: 543,
like: 2234,
song: this.songs[4]
},
{
author: '丶你要去哪里',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h5.jpg',
content: '当年会一段潘帅的RAP是很拉风的事情',
comment: 14415,
like: 36297,
song: this.songs[5]
},
{
author: '一个人的某泥',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h6.jpg',
content: '抗韩二十年,死于朴智妍',
comment: 1237,
like: 5482,
song: this.songs[6]
},
{
author: '曾经那个少年1993',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h7.jpg',
content: '一个人去游泳,像投河;倒过来,一个人去投河,像游泳,太孤独。 ———-陈小三',
comment: 7628,
like: 36448,
song: this.songs[7]
},
{
author: '柳崽崽阿',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h8.jpg',
content: '哎呦,B站来的跟我一起摇💃',
comment: 1237,
like: 5482,
song: this.songs[8]
},
{
author: '李富贵他哥',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h9.jpg',
content: '我弟弟老是偷我的裙子丝袜穿,我担心他在女装的道路上越走越远,这让我这个当哥哥的很是担心啊',
comment: 7362,
like: 11482,
song: this.songs[9]
},
{
author: '一檀溪一',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h10.jpg',
content: '想起张雪峰说过的全世界最大的社区天通苑,住了400w人,光是地铁修了三站,去赶早上第一班天通苑的地铁,那才是真正的北京,真的是哭辽',
comment: 6496,
like: 8526,
song: this.songs[10]
},
{
author: '-_Dimple_',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h11.jpg',
content: '今天为了赶地铁在电梯上摔了一跤,手磕破了,马上起来进了地铁结果发现一着急坐反了。某一刻觉得自己长大了,摔倒了不是先难受,想哭觉得丢脸,而是想着快点赶上地铁。大概成年人的无奈就是没时间让我难过一会儿吧。',
comment: 1237,
like: 5482,
song: this.songs[11]
},
{
author: 'h奔放小青年',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h12.jpg',
content: '关于这部电影看到过一个评论:当年大学毕业以后我和女友各奔东西,曾经一个晚上,偶然又看了这部电影,当至尊宝拥吻紫霞的时候荡气回肠的歌声也让我潸然泪下。。。。。第二天我打起行囊踏上火车往她家赶,如今十年过去,我们已经有了一个可爱的女儿。 我想,这是对一部电影最好的肯定吧',
comment: 1237,
like: 5482,
song: this.songs[12]
},
{
author: '您的坑友已上线',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h13.jpg',
content: '李克勤说他唱了三十多年的歌,当接下面具的时候还很紧张,还很担心自己能不能被大家认出来,然后当《红日》想起来的时候,自己都被感动了。前天看张信哲在央视的开讲啦,坦言面对"过气“一次能够接受,撒贝宁说的很好,这批歌迷也许不会通过刷微博来支持,但是他在这些人的心里比微博上更重要。',
comment: 1237,
like: 5482,
song: this.songs[13]
},
{
author: '一个人的某泥',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h14.jpg',
content: '11年初三,无意中听到这首歌,推荐给同学,他们都说一般般。当我在家外放这首歌时,我奶奶对我说:“这什么歌,这么好听。',
comment: 1237,
like: 5482,
song: this.songs[14]
},
{
author: '不想早起',
avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h15.jpg',
content: '文不黑周树人 武不黑李小龙 音不黑黄家驹 影不黑周星驰',
comment: 1237,
like: 5482,
song: this.songs[15]
}
]
注意:是使用鸿蒙5.0 api16的语法,使用的语言是 ArkTS,然后组件都是使用V2版本的,而且颜色方案是 .fontColor('#颜色值') 这种形式- 把效果图&提示词交给
cursor然后等待结果出来后应用:

- 如果效果还是有瑕疵可以继续自己微调或者让
cursor调整:

- 最终效果:

📌 示例代码:
interface songItemType {
img: string;
name: string;
author: string;
url: string;
id: string;
}
interface momentListType {
author: string;
avatar: string;
content: string;
comment: number;
like: number;
song: songItemType;
}
@Component
export struct Moment {
private songs: songItemType[] = [
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/0.jpg', name: '直到世界的尽头', author: 'WANDS', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/0.m4a', id: '0000' },
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/1.jpg', name: '画', author: '赵磊', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/1.mp3', id: '0001' },
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/2.jpg', name: 'Sweet Dreams', author: 'TPaul Sax / Eurythmics', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/2.mp3', id: '0002' },
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/3.jpg', name: '奢香夫人', author: '凤凰传奇', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/3.m4a', id: '0003' },
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/4.jpg', name: '空心', author: '光泽', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/4.mp3', id: '0004' },
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/5.jpg', name: '反转地球', author: '潘玮柏', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/5.mp3', id: '0005' },
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/6.jpg', name: 'No.9', author: 'T-ara', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/6.m4a', id: '0006' },
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/7.jpg', name: '孤独', author: 'G.E.M.邓紫棋', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/7.m4a', id: '0007' },
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/8.jpg', name: 'Lose Control', author: 'Hedley', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/8.m4a', id: '0008' },
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/9.jpg', name: '倩女幽魂', author: '张国荣', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/9.m4a', id: '0009' },
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/10.jpg', name: '北京北京', author: '汪峰', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/10.m4a', id: '0010' },
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/11.jpg', name: '苦笑', author: '汪苏泷', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/11.mp3', id: '0011' },
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/12.jpg', name: '一生所爱', author: '卢冠廷 / 莫文蔚', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/12.m4a', id: '0012' },
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/13.jpg', name: '月半小夜曲', author: '李克勤', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/13.mp3', id: '0013' },
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/14.jpg', name: 'Rolling in the Deep', author: 'Adele', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/14.m4a', id: '0014' },
{ img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/15.jpg', name: '海阔天空', author: 'Beyond', url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/15.m4a', id: '0015' }
];
private momentList: momentListType[] = [
{ author: 'Feast-aws', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h0.jpg', content: '二十几岁的你,一无所有,也拥有一切!', comment: 604, like: 23382, song: this.songs[0] },
{ author: 'CeeYet', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h1.jpg', content: '画一个姑娘陪着我,再画一个姑娘陪着我', comment: 1237, like: 5482, song: this.songs[1] },
{ author: 'Z_HOUSC', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h2.jpg', content: '这个事还得从两个职业选手说起', comment: 8425, like: 4234, song: this.songs[2] },
{ author: '逆转大师', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h3.jpg', content: '听奢香夫人,做上岸女人!', comment: 7658, like: 11545, song: this.songs[3] },
{ author: 'Moriaty', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h4.jpg', content: '别一厢情愿的付出,他如果拒绝你了就别再纠缠了,别一次次满满热情得到的都是敷衍冷淡的回复,你不该这样的,你应该酷一点,别打扰真的是最好的选择。', comment: 543, like: 2234, song: this.songs[4] },
{ author: '丶你要去哪里', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h5.jpg', content: '当年会一段潘帅的RAP是很拉风的事情', comment: 14415, like: 36297, song: this.songs[5] },
{ author: '一个人的某泥', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h6.jpg', content: '抗韩二十年,死于朴智妍', comment: 1237, like: 5482, song: this.songs[6] },
{ author: '曾经那个少年1993', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h7.jpg', content: '一个人去游泳,像投河;倒过来,一个人去投河,像游泳,太孤独。 ———-陈小三', comment: 7628, like: 36448, song: this.songs[7] },
{ author: '柳崽崽阿', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h8.jpg', content: '哎呦,B站来的跟我一起摇💃', comment: 1237, like: 5482, song: this.songs[8] },
{ author: '李富贵他哥', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h9.jpg', content: '我弟弟老是偷我的裙子丝袜穿,我担心他在女装的道路上越走越远,这让我这个当哥哥的很是担心啊', comment: 7362, like: 11482, song: this.songs[9] },
{ author: '一檀溪一', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h10.jpg', content: '想起张雪峰说过的全世界最大的社区天通苑,住了400w人,光是地铁修了三站,去赶早上第一班天通苑的地铁,那才是真正的北京,真的是哭辽', comment: 6496, like: 8526, song: this.songs[10] },
{ author: '-_Dimple_', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h11.jpg', content: '今天为了赶地铁在电梯上摔了一跤,手磕破了,马上起来进了地铁结果发现一着急坐反了。某一刻觉得自己长大了,摔倒了不是先难受,想哭觉得丢脸,而是想着快点赶上地铁。大概成年人的无奈就是没时间让我难过一会儿吧。', comment: 1237, like: 5482, song: this.songs[11] },
{ author: 'h奔放小青年', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h12.jpg', content: '关于这部电影看到过一个评论:当年大学毕业以后我和女友各奔东西,曾经一个晚上,偶然又看了这部电影,当至尊宝拥吻紫霞的时候荡气回肠的歌声也让我潸然泪下。。。。。第二天我打起行囊踏上火车往她家赶,如今十年过去,我们已经有了一个可爱的女儿。 我想,这是对一部电影最好的肯定吧', comment: 1237, like: 5482, song: this.songs[12] },
{ author: '您的坑友已上线', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h13.jpg', content: '李克勤说他唱了三十多年的歌,当接下面具的时候还很紧张,还很担心自己能不能被大家认出来,然后当《红日》想起来的时候,自己都被感动了。前天看张信哲在央视的开讲啦,坦言面对"过气“一次能够接受,撒贝宁说的很好,这批歌迷也许不会通过刷微博来支持,但是他在这些人的心里比微博上更重要。', comment: 1237, like: 5482, song: this.songs[13] },
{ author: '一个人的某泥', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h14.jpg', content: '11年初三,无意中听到这首歌,推荐给同学,他们都说一般般。当我在家外放这首歌时,我奶奶对我说:“这什么歌,这么好听。', comment: 1237, like: 5482, song: this.songs[14] },
{ author: '不想早起', avatar: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/h15.jpg', content: '文不黑周树人 武不黑李小龙 音不黑黄家驹 影不黑周星驰', comment: 1237, like: 5482, song: this.songs[15] }
];
build() {
Column() {
// 标题
Text('互动广场')
.fontSize(18)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Medium)
.padding({ top: 16, bottom: 12 })
// 列表
List() {
ForEach(this.momentList, (item: momentListType, index: number) => {
ListItem() {
Column() {
// 头像 + 文本 行
Row({ space: 12 }) {
// 头像
Image(item.avatar)
.width(36)
.height(36)
.borderRadius(18)
// 文本区
Column({ space: 8 }) {
Text(item.author)
.fontSize(16)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Medium)
Text(item.content)
.fontSize(14)
.fontColor('#B3B3B3')
.maxLines(3)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.padding({ right: 50 })
// 歌曲卡片
Row({ space: 10 }) {
Image(item.song.img)
.width(64)
.height(64)
.borderRadius(6)
Column({ space: 6 }) {
Text(item.song.name)
.fontSize(15)
.fontColor('#FFFFFF')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(item.song.author)
.fontSize(12)
.fontColor('#9E9E9E')
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
Blank()
Image($r('app.media.ic_play'))
.width(18)
.height(18)
.opacity(0.9)
}
.height(64)
.backgroundColor('#2B2B2B')
.borderRadius(8)
.padding({ left: 8, right: 12 })
.justifyContent(FlexAlign.Start)
.alignItems(VerticalAlign.Center)
.width('100%')
// 操作区
Row({ space: 24 }) {
Row({ space: 6 }) {
Image($r('app.media.ic_share'))
.width(16)
.height(16)
.opacity(0.9)
.fillColor('#9E9E9E')
Text('分享')
.fontSize(12)
.fontColor('#9E9E9E')
}
Row({ space: 6 }) {
Image($r('app.media.ic_comment_o'))
.width(16)
.height(16)
.opacity(0.9)
.fillColor('#9E9E9E')
Text(`${item.comment}`)
.fontSize(12)
.fontColor('#9E9E9E')
}
Row({ space: 6 }) {
Image($r('app.media.ic_like'))
.width(16)
.height(16)
.opacity(0.9)
.fillColor('#9E9E9E')
Text(`${item.like}`)
.fontSize(12)
.fontColor('#9E9E9E')
}
}
}
.alignItems(HorizontalAlign.Start)
}
.alignItems(VerticalAlign.Top)
// 分割线
Divider()
.color('#2F2F2F')
.strokeWidth(1)
.margin({ top: 16 })
}
.width('100%')
.padding({ left: 16, right: 16, top: 14, bottom: 6 })
}
}, (item: momentListType, index: number) => item.song.id)
}
.edgeEffect(EdgeEffect.None)
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
.backgroundColor('#000000')
.expandSafeArea()
.padding({ top: 15 , bottom : 50})
}
}7.6.3 我的
- 效果图:
.Blo0kS4k.png)
- 提示词:
根据图片还原页面布局用代码实现出来
需求如下:
1. 页面分两块,纵向布局,上面是图片区域,占高度70%,下面是介绍区域,占高度30%
2. 图片区域是堆叠布局,大图片背景在下层,logo在上层,对齐方式底部居中
3. 介绍区域
3.1 第一行为软件名字(黑马云音乐);
3.2 第二行为用户基本信息
3.3 第三行是账户信息(关注、粉丝、点赞,数字和文字内容水平排列)
注意:文字颜色和背景色根据上面图片设置,不可以额外添加
注意是使用鸿蒙5.0 api16的语法,使用的语言是 ArkTS,然后组件都是使用V2版本的,而且颜色方案是 .fontColor('#颜色值') 这种形式
本地图片在resource/base/media 下面 , 背景大图是 bg_mine📌 示例代码:
// 页面:我的
// API16 ArkTS(V2 组件语法)
// 账户信息项接口
interface AccountItem {
label: string
value: string
}
@ComponentV2
export struct Mine {
// 账户信息数据
private accountItems: AccountItem[] = [
{ label: '关注', value: '1' },
{ label: '粉丝', value: '100万' },
{ label: '赞', value: '1.67亿' }
]
build() {
Column() {
// 顶部图片区域(70% 高度)
Stack({ alignContent: Alignment.Bottom }) {
// 背景大图
Image($r('app.media.bg_mine'))
.width('100%')
.height('100%')
// 底部居中 Logo
Image($r('app.media.logo'))
.width(100)
.backgroundColor('#000')
.border({radius : '50%'})
}
.width('100%')
.height('70%')
// 底部介绍区域(30% 高度)
Column({ space: 12 }) {
// 软件名称
Row() {
Text('黑马云音乐')
.fontSize(22)
.fontWeight(FontWeight.Medium)
.fontColor('#FFFFFF')
// VIP 标识
Text('VIP')
.fontSize(12)
.fontColor('#FF5A8A')
.margin({ left: 8 })
.border({ width: 1, color: '#FF5A8A' })
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
.borderRadius(10)
}
.margin({top : 25 , bottom : 25})
.alignItems(VerticalAlign.Center)
// 第二行:用户基本信息
Row(){
//图
Image($r('app.media.ic_boy'))
.width(15)
.fillColor(Color.Blue)
.margin({right : 10})
.opacity(0.8)
//文
Text('00后 双子座 北京 歌龄·18年')
.fontSize(14)
.fontColor('#9E9E9E')
}
.width('80%')
.justifyContent(FlexAlign.Center)
// 第三行:账户信息(水平排列)
Row({ space: 24 }) {
ForEach(this.accountItems, (item: AccountItem, _: number) => {
Row({ space: 5 }) {
Text(item.value)
.fontSize(14)
.fontWeight(FontWeight.Medium)
.fontColor('#9E9E9E')
Text(item.label)
.fontSize(12)
.fontColor('#9E9E9E')
}
})
}
.width('60%')
.justifyContent(FlexAlign.SpaceAround)
}
.width('100%')
.height('30%')
.padding({ left: 16, right: 16, top: 12})
.backgroundColor('#0F0F10')
}
.width('100%')
.height('100%')
.backgroundColor('#000000')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
}
}- 效果:

7.7 播放优化
之前对于
AvPlayerManager.ets里面的singPlay(song)方法的逻辑还可以进行优化:拿实际音乐软件
QQ音乐做测试会得到下图:

- 之前添加歌曲的逻辑是,如果歌曲不在列表里,一律添加到队首,而实际是添加到播放歌曲的下一位
- 重新设计后的逻辑如下:

- 对比之前的代码来看:

- 修改后代码:
singPlay (song:SongItemType){
//申请长时任务
sessionManager.startBackgroundTask()
// 是否在列表里 → some 检查数组里面的数据是否存在满足条件的 → 只要有一个满足 some 返回 true
const inList = this.currentSong.playList.some(item => item.id === song.id)
if (inList) {
//在列表里面 → 是否是正在播放的 → currentSong url === song.url
if (this.currentSong.url === song.url) {
this.player?.play()
this.currentSong.isPlay = true
} else {
//设置新的索引 → 切歌
this.currentSong.playIndex = this.currentSong.playList.findIndex(item => item.id === song.id)
//切歌
this.changeSong()
}
} else {
//不在列表里面
//如果列表为空,那么直接可以添加到队首就好了
if(this.currentSong.playList.length === 0){
this.currentSong.playList.unshift(song) // 把要播放的歌添加到队首
this.currentSong.playIndex = 0
} else {
//列表不为空的话,添加到 playIndex 的后一位
//lst.splice(start,deleteCount,...args)
this.currentSong.playList.splice(this.currentSong.playIndex + 1,0,song)
//说明(插入操作的位置,删除0个元素,插入的元素)
this.currentSong.playIndex++
}
//切换歌曲
this.changeSong()
}
sessionManager.setAVPlaybackState()
}- 重新运行项目,打开模拟器测试:
- 先添加一首 空心

- 添加一首 孤独看看插入位置

- 把当前播放歌曲改成第一位的空心

- 再插入一首苦笑

- 可以发现功能没问题 ✅
7.8 项目源码
- 项目源码托管在
gitee上面 : https://gitee.com/irai/hm_music
