147.[HarmonyOS NEXT 实战案例八 :List系列] 粘性头部列表基础篇 原创
[HarmonyOS NEXT 实战案例八 :List系列] 粘性头部列表基础篇
项目已开源,开源地址: https://gitcode.com/nutpi/HarmonyosNextCaseStudyTutorial , 欢迎fork & star
效果演示
![147.[HarmonyOS NEXT 实战案例八 :List系列] 粘性头部列表基础篇-鸿蒙开发者社区 147.[HarmonyOS NEXT 实战案例八 :List系列] 粘性头部列表基础篇-鸿蒙开发者社区](https://dl-harmonyos.51cto.com/images/202506/922f10985e2b9bebfb9190fae6a1338b1e779f.png?x-oss-process=image/resize,w_373,h_597)
粘性头部列表是移动应用中常见的UI模式,它允许列表的分组标题在滚动时保持可见,提供更好的导航体验。本教程将详细讲解如何在HarmonyOS NEXT中实现一个功能完善的粘性头部列表,以音乐播放器应用为例,展示如何创建按专辑分组的音乐列表。
一、粘性头部列表概述
粘性头部列表是List组件与ListItemGroup组件结合使用的一种应用场景,它具有以下特点:
- 分组显示:内容按照一定规则分组显示
 - 粘性头部:当滚动时,当前分组的头部会固定在屏幕顶部
 - 层级结构:通过视觉设计清晰地表达内容的层级关系
 - 导航便捷:用户可以快速了解当前浏览的内容类别
 
在HarmonyOS NEXT中,粘性头部列表主要通过以下组件实现:
- List:基础列表容器
 - ListItemGroup:列表项分组容器
 - ListItem:列表项
 - sticky属性:控制粘性效果
 
二、数据模型设计
首先,我们需要定义音乐数据的接口:
// 歌曲接口
interface SongsType {
    title: string,
    duration: string,
    isLiked: boolean
}
// 专辑接口
interface MusicType {
    album: string,
    artist: string,
    cover: Resource,
    year: string,
    songs: SongsType[]
}
// 当前播放歌曲接口
interface CurrentSongType {
    album: string, 
    title: string
}
这些接口定义了我们的数据结构:
SongsType:定义歌曲的基本属性(标题、时长、是否喜欢)MusicType:定义专辑的基本属性(专辑名、艺术家、封面、年份、歌曲列表)CurrentSongType:定义当前播放歌曲的信息(所属专辑、歌曲标题)
然后,我们创建示例音乐数据:
private musicData: MusicType[] = [
    {
        album: '星辰大海',
        artist: '周杰伦',
        cover: $r('app.media.big25'),
        year: '2023',
        songs: [
            { title: '星辰大海', duration: '4:25', isLiked: true },
            { title: '城市之光', duration: '3:48', isLiked: false },
            // 更多歌曲...
        ]
    },
    // 更多专辑...
]
三、状态管理
为了管理当前播放的歌曲状态,我们定义以下状态变量:
// 当前播放歌曲
@State currentSong: CurrentSongType | null = null
这个状态变量用于跟踪当前正在播放的歌曲,初始值为null表示没有歌曲在播放。
四、核心功能实现
1. 播放歌曲功能
// 播放歌曲
playSong(album: string, title: string) {
    this.currentSong = { album, title }
}
这个方法用于设置当前播放的歌曲,接收专辑名和歌曲标题作为参数。
2. 专辑头部构建器
// 构建专辑头部
@Builder
AlbumHeader(album: string, artist: string, cover: Resource, year: string) {
    Row() {
        // 专辑封面
        Image(cover)
            .width(60)
            .height(60)
            .borderRadius(8)
            .margin({ right: 16 })
        // 专辑信息
        Column() {
            Text(album)
                .fontSize(18)
                .fontWeight(FontWeight.Bold)
            Text(artist)
                .fontSize(16)
                .fontColor('#666666')
                .margin({ top: 4 })
            Text(year)
                .fontSize(14)
                .fontColor('#999999')
                .margin({ top: 2 })
        }
        .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .backgroundColor('#FFFFFF')
    .padding({ left: 16, right: 16, top: 12, bottom: 12 })
}
这个Builder用于构建专辑头部的UI,包括专辑封面和专辑信息(专辑名、艺术家、年份)。
五、UI构建
1. 整体布局
build() {
    Stack({ alignContent: Alignment.Bottom }) {
        Column() {
            // 标题栏
            Row() { /* ... */ }
            
            // 音乐列表
            List() { /* ... */ }
        }
        .width('100%')
        .height('100%')
        .backgroundColor('#FFFFFF')
        // 底部播放控制栏(当有歌曲播放时显示)
        if (this.currentSong) {
            Row() { /* ... */ }
        }
    }
    .width('100%')
    .height('100%')
}
整体布局使用Stack容器,包含两个主要部分:
- 主内容区(Column容器):包含标题栏和音乐列表
 - 底部播放控制栏:仅在有歌曲播放时显示
 
2. 标题栏实现
// 标题栏
Row() {
    Text('音乐')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
    Blank()
    Image($r('app.media.music_icon'))
        .width(24)
        .height(24)
        .margin({ right: 16 })
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor('#F1F3F5')
标题栏包含应用标题和一个音乐图标。
3. 音乐列表实现
// 音乐列表
List() {
    ForEach(this.musicData, (albumData: MusicType) => {
        ListItemGroup({
            header: this.AlbumHeader(albumData.album, albumData.artist, albumData.cover, albumData.year),
            space: 0
        }) {
            ForEach(albumData.songs, (song: SongsType) => {
                ListItem() {
                    Row() {
                        // 歌曲信息
                        Column() {
                            Text(song.title)
                                .fontSize(16)
                                .fontWeight(FontWeight.Medium)
                            Text(song.duration)
                                .fontSize(14)
                                .fontColor('#666666')
                                .margin({ top: 4 })
                        }
                        .alignItems(HorizontalAlign.Start)
                        .layoutWeight(1)
                        // 喜欢按钮
                        Image(song.isLiked ? $r('app.media.heart_filled') : $r('app.media.heart_outline'))
                            .width(24)
                            .height(24)
                            .margin({ right: 16 })
                        // 更多按钮
                        Image($r('app.media.dcc_health_icon'))
                            .width(24)
                            .height(24)
                    }
                    .width('100%')
                    .padding({ left: 16, right: 16, top: 12, bottom: 12 })
                    .onClick(() => this.playSong(albumData.album, song.title))
                    .backgroundColor(
                        this.currentSong?.album === albumData.album &&
                            this.currentSong?.title === song.title ?
                            '#F0F7FF' : '#FFFFFF'
                    )
                }
                .height(64)
            })
        }
    })
}
.sticky(StickyStyle.Header) // 设置粘性头部
.width('100%')
.layoutWeight(1)
.divider({ // 设置分割线
    strokeWidth: 1,
    color: '#E5E5E5',
    startMargin: 16,
    endMargin: 16
})
音乐列表的关键点:
- 使用
ForEach遍历专辑数据,为每个专辑创建一个ListItemGroup - 使用自定义的
AlbumHeader构建器作为ListItemGroup的头部 - 在每个
ListItemGroup中,再次使用ForEach遍历歌曲数据,为每首歌曲创建一个ListItem - 为
List组件设置sticky(StickyStyle.Header)属性,启用粘性头部功能 - 为歌曲项添加点击事件,点击时调用
playSong方法 - 根据当前播放歌曲状态,设置不同的背景色以提供视觉反馈
 
4. 底部播放控制栏实现
// 底部播放控制栏(当有歌曲播放时显示)
if (this.currentSong) {
    Row() {
        // 当前播放歌曲信息
        Column() {
            Text(this.currentSong.title)
                .fontSize(16)
                .fontWeight(FontWeight.Medium)
                .maxLines(1)
                .textOverflow({ overflow: TextOverflow.Ellipsis })
            Text(this.currentSong.album)
                .fontSize(14)
                .fontColor('#666666')
                .margin({ top: 2 })
                .maxLines(1)
                .textOverflow({ overflow: TextOverflow.Ellipsis })
        }
        .alignItems(HorizontalAlign.Start)
        .layoutWeight(1)
        // 播放控制按钮
        Row({ space: 16 }) {
            Image($r('app.media.active_weather_icon'))
                .width(24)
                .height(24)
            Image($r('app.media.mobile_calculator_ap'))
                .width(32)
                .height(32)
            Image($r('app.media.note_icon'))
                .width(24)
                .height(24)
        }
    }
    .width('100%')
    .height(72)
    .padding({ left: 16, right: 16 })
    .backgroundColor('#FFFFFF')
    .borderColor('#E5E5E5')
    .borderWidth({ top: 1 })
}
底部播放控制栏仅在有歌曲播放时显示,包含当前播放歌曲信息和播放控制按钮。
六、关键技术点分析
1. 粘性头部的实现
粘性头部功能主要通过List组件的sticky属性实现:
.sticky(StickyStyle.Header) // 设置粘性头部
这个属性使得ListItemGroup的头部在滚动时保持可见,直到下一个分组的头部出现。
2. ListItemGroup的使用
ListItemGroup组件用于创建分组列表,它接收一个header参数作为分组的头部:
ListItemGroup({
    header: this.AlbumHeader(albumData.album, albumData.artist, albumData.cover, albumData.year),
    space: 0
})
header参数可以是一个自定义的Builder,这里我们使用AlbumHeader构建器来创建专辑头部。
3. 条件渲染
我们使用条件渲染来控制底部播放控制栏的显示:
if (this.currentSong) {
    Row() { /* ... */ }
}
只有当currentSong不为null时,才会显示底部播放控制栏。
4. 状态反馈
为了提供良好的用户体验,我们为当前播放的歌曲提供视觉反馈:
.backgroundColor(
    this.currentSong?.album === albumData.album &&
        this.currentSong?.title === song.title ?
        '#F0F7FF' : '#FFFFFF'
)
当歌曲正在播放时,其背景色会变为浅蓝色,以便用户快速识别当前播放的歌曲。
七、代码结构与样式设置
| 组件部分 | 主要功能 | 样式设置 | 
|---|---|---|
| Stack | 整体容器 | 宽高100%,底部对齐 | 
| Column | 主内容区 | 宽高100%,白色背景 | 
| 标题栏Row | 显示应用标题和图标 | 高56px,左右内边距16px,浅灰背景 | 
| List | 显示音乐列表 | 宽100%,弹性布局权重1,粘性头部,带分割线 | 
| ListItemGroup | 专辑分组 | 自定义头部,间距0 | 
| ListItem | 单首歌曲 | 高64px,点击事件,条件背景色 | 
| 底部Row | 播放控制栏 | 高72px,左右内边距16px,白色背景,顶部边框 | 
八、粘性头部列表的应用场景
粘性头部列表适用于多种应用场景,包括但不限于:
- 音乐播放器:按专辑、艺术家或流派分组显示歌曲
 - 联系人列表:按字母或组织分组显示联系人
 - 设置页面:按功能类别分组显示设置项
 - 电商应用:按类别分组显示商品
 - 日历应用:按月份或周分组显示事件
 - 新闻应用:按日期或主题分组显示新闻
 
九、总结
本教程详细讲解了如何在HarmonyOS NEXT中实现一个功能完善的粘性头部列表,
通过本教程,你应该能够掌握HarmonyOS NEXT中粘性头部列表的基本实现方法,并能够应用到自己的项目中。在进阶篇中,我们将探讨如何增强粘性头部列表的功能,如自定义粘性效果、动画过渡、交互优化等高级特性。




















