147.[HarmonyOS NEXT 实战案例八 :List系列] 粘性头部列表基础篇

2025-06-30 22:23:05
103次阅读
0个评论

[HarmonyOS NEXT 实战案例八 :List系列] 粘性头部列表基础篇

项目已开源,开源地址: https://gitcode.com/nutpi/HarmonyosNextCaseStudyTutorial , 欢迎fork & star

效果演示

image.png

粘性头部列表是移动应用中常见的UI模式,它允许列表的分组标题在滚动时保持可见,提供更好的导航体验。本教程将详细讲解如何在HarmonyOS NEXT中实现一个功能完善的粘性头部列表,以音乐播放器应用为例,展示如何创建按专辑分组的音乐列表。

一、粘性头部列表概述

粘性头部列表是List组件与ListItemGroup组件结合使用的一种应用场景,它具有以下特点:

  1. 分组显示:内容按照一定规则分组显示
  2. 粘性头部:当滚动时,当前分组的头部会固定在屏幕顶部
  3. 层级结构:通过视觉设计清晰地表达内容的层级关系
  4. 导航便捷:用户可以快速了解当前浏览的内容类别

在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容器,包含两个主要部分:

  1. 主内容区(Column容器):包含标题栏和音乐列表
  2. 底部播放控制栏:仅在有歌曲播放时显示

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
})

音乐列表的关键点:

  1. 使用ForEach遍历专辑数据,为每个专辑创建一个ListItemGroup
  2. 使用自定义的AlbumHeader构建器作为ListItemGroup的头部
  3. 在每个ListItemGroup中,再次使用ForEach遍历歌曲数据,为每首歌曲创建一个ListItem
  4. List组件设置sticky(StickyStyle.Header)属性,启用粘性头部功能
  5. 为歌曲项添加点击事件,点击时调用playSong方法
  6. 根据当前播放歌曲状态,设置不同的背景色以提供视觉反馈

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,白色背景,顶部边框

八、粘性头部列表的应用场景

粘性头部列表适用于多种应用场景,包括但不限于:

  1. 音乐播放器:按专辑、艺术家或流派分组显示歌曲
  2. 联系人列表:按字母或组织分组显示联系人
  3. 设置页面:按功能类别分组显示设置项
  4. 电商应用:按类别分组显示商品
  5. 日历应用:按月份或周分组显示事件
  6. 新闻应用:按日期或主题分组显示新闻

九、总结

本教程详细讲解了如何在HarmonyOS NEXT中实现一个功能完善的粘性头部列表, 通过本教程,你应该能够掌握HarmonyOS NEXT中粘性头部列表的基本实现方法,并能够应用到自己的项目中。在进阶篇中,我们将探讨如何增强粘性头部列表的功能,如自定义粘性效果、动画过渡、交互优化等高级特性。

收藏00

登录 后评论。没有帐号? 注册 一个。

全栈若城

  • 0回答
  • 4粉丝
  • 0关注