135.[HarmonyOS NEXT 实战案例七:List系列] 水平列表组件实战:打造精美图片库 基础篇

2025-06-30 22:13:07
103次阅读
0个评论

[HarmonyOS NEXT 实战案例七:List系列] 水平列表组件实战:打造精美图片库 基础篇

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

效果演示

image.png

image.png

一、水平列表概述

在HarmonyOS NEXT应用开发中,除了常见的垂直列表外,水平列表也是一种重要的UI组件,特别适合展示图片、卡片等内容。水平列表允许用户通过左右滑动浏览内容,在有限的屏幕空间内展示更多信息。

1.1 水平列表的应用场景

应用场景 说明
图片库 展示照片集合,用户可以左右滑动浏览
商品推荐 在电商应用中展示推荐商品
视频列表 展示视频缩略图,用户可以选择感兴趣的视频
卡片集合 展示信用卡、会员卡等卡片信息
横向导航 作为应用内的横向导航菜单

1.2 水平列表与垂直列表的区别

特性 水平列表 垂直列表
滚动方向 左右滚动 上下滚动
适用场景 图片展示、横向导航 长列表数据、聊天记录
实现方式 设置listDirection为Axis.Horizontal 默认为Axis.Vertical
多行支持 可通过lanes属性设置多行 单列多行

二、水平列表实战案例

在本案例中,我们将实现一个水平方向的图片库,展示风景照片及其标题。

2.1 数据模型定义

首先,我们需要定义图片的数据模型:

interface Image {
    title: string
    image: ResourceStr
}

这个接口定义了图片的两个属性:标题和图片资源。

2.2 数据准备

接下来,我们准备图片数据:

private images: Image[]= [
    { title: '风景照1', image: $r('app.media.big28') },
    { title: '风景照2', image: $r('app.media.big27') },
    { title: '风景照3', image: $r('app.media.big26') },
    { title: '风景照4', image: $r('app.media.big25') },
    { title: '风景照5', image: $r('app.media.big24') },
    { title: '风景照6', image: $r('app.media.big23') },
    { title: '风景照7', image: $r('app.media.big22') },
    { title: '风景照8', image: $r('app.media.big21') },
]

我们创建了一个包含8张风景照片的数组,每张照片都有标题和图片资源。

2.3 页面结构设计

整个页面采用垂直布局,包含标题栏和两个水平图片列表:

build() {
    Column() {
        // 标题栏
        Row() {
            Text('照片库')
                .fontSize(24)
                .fontWeight(FontWeight.Bold)
        }
        .width('100%')
        .height(56)
        .padding({ left: 16 })
        .backgroundColor('#F1F3F5')

        // 水平图片列表
        List() {
            // 列表内容
        }
        .width('100%')
        .height(180)
        .margin({ top: 16 })
        .listDirection(Axis.Horizontal) // 设置为水平列表
        .padding({ left: 16 })

        // 标题栏
        Row() {
            Text('推荐照片')
                .fontSize(20)
                .fontWeight(FontWeight.Bold)
        }
        .width('100%')
        .height(56)
        .padding({ left: 16 })
        .margin({ top: 16 })

        // 多行水平图片列表
        List() {
            // 列表内容
        }
        .width('100%')
        .layoutWeight(1)
        .listDirection(Axis.Horizontal) // 设置为水平列表
        .lanes(2) // 设置为2行
        .padding({ left: 16 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#FFFFFF')
}

在这个结构中:

  • 使用Column作为根容器,占满整个屏幕
  • 顶部是一个Row容器,作为标题栏,包含标题文本
  • 中间是第一个List组件,用于显示单行水平图片列表
  • 接着是另一个标题栏
  • 底部是第二个List组件,用于显示多行水平图片列表

2.4 单行水平列表实现

接下来,我们实现第一个水平列表的内容:

List() {
    ForEach(this.images, (item:Image) => {
        ListItem() {
            Column() {
                // 图片
                Image(item.image)
                    .width(160)
                    .height(120)
                    .borderRadius(8)
                    .objectFit(ImageFit.Cover)

                // 图片标题
                Text(item.title)
                    .fontSize(14)
                    .margin({ top: 8 })
                    .maxLines(1)
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
            }
            .alignItems(HorizontalAlign.Center)
            .width(160)
        }
        .padding({ right: 12 })
    })
}
.width('100%')
.height(180)
.margin({ top: 16 })
.listDirection(Axis.Horizontal) // 设置为水平列表
.padding({ left: 16 })

在这段代码中:

  1. 使用ForEach遍历images数组,为每张图片创建一个ListItem
  2. 每个ListItem包含一个Column布局,用于垂直排列图片和标题
  3. 图片使用Image组件,设置宽高和圆角
  4. 标题使用Text组件,设置最大行数和文本溢出处理
  5. 设置ListItem的右侧内边距,使列表项之间有间隔
  6. 最重要的是,通过listDirection(Axis.Horizontal)将列表方向设置为水平

2.5 多行水平列表实现

接下来,我们实现第二个水平列表,这是一个多行水平列表:

List() {
    ForEach(this.images, (item:Image) => {
        ListItem() {
            Column() {
                // 图片
                Image(item.image)
                    .width(120)
                    .height(90)
                    .borderRadius(8)
                    .objectFit(ImageFit.Cover)

                // 图片标题
                Text(item.title)
                    .fontSize(14)
                    .margin({ top: 8 })
                    .maxLines(1)
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
            }
            .alignItems(HorizontalAlign.Center)
            .width(120)
        }
        .padding({ right: 12, bottom: 16 })
    })
}
.width('100%')
.layoutWeight(1)
.listDirection(Axis.Horizontal) // 设置为水平列表
.lanes(2) // 设置为2行
.padding({ left: 16 })

这个列表与第一个列表的主要区别在于:

  1. 图片和列表项的尺寸略小
  2. 添加了底部内边距,使行与行之间有间隔
  3. 最重要的是,通过lanes(2)属性将列表设置为2行

三、水平列表的关键属性解析

3.1 listDirection属性

listDirection属性用于设置列表的滚动方向,是实现水平列表的关键属性:

.listDirection(Axis.Horizontal) // 设置为水平列表

Axis枚举值说明:

说明
Axis.Horizontal 水平方向,列表项从左到右排列
Axis.Vertical 垂直方向,列表项从上到下排列(默认值)

3.2 lanes属性

lanes属性用于设置水平列表的行数或垂直列表的列数:

.lanes(2) // 设置为2行

当listDirection为Axis.Horizontal时,lanes表示行数;当listDirection为Axis.Vertical时,lanes表示列数。

3.3 列表项宽度设置

在水平列表中,列表项的宽度是一个重要的设置:

Column() {
    // 图片和标题
}
.alignItems(HorizontalAlign.Center)
.width(160) // 设置列表项内容的宽度

需要注意的是,这里设置的是列表项内容的宽度,而不是ListItem本身的宽度。ListItem的宽度会根据内容自动调整。

3.4 列表项间距设置

为了使列表项之间有适当的间距,我们可以设置ListItem的内边距:

ListItem() {
    // 列表项内容
}
.padding({ right: 12 }) // 设置右侧内边距

在水平列表中,通常设置右侧内边距来控制列表项之间的水平间距;在多行水平列表中,还需要设置底部内边距来控制行与行之间的垂直间距。

四、完整代码解析

让我们来看一下完整的代码实现:

@Component
export struct HorizontalList {
    // 图片数据
    private images: Image[]= [
        { title: '风景照1', image: $r('app.media.big28') },
        { title: '风景照2', image: $r('app.media.big27') },
        { title: '风景照3', image: $r('app.media.big26') },
        { title: '风景照4', image: $r('app.media.big25') },
        { title: '风景照5', image: $r('app.media.big24') },
        { title: '风景照6', image: $r('app.media.big23') },
        { title: '风景照7', image: $r('app.media.big22') },
        { title: '风景照8', image: $r('app.media.big21') },
    ]

    build() {
        Column() {
            // 标题栏
            Row() {
                Text('照片库')
                    .fontSize(24)
                    .fontWeight(FontWeight.Bold)
            }
            .width('100%')
            .height(56)
            .padding({ left: 16 })
            .backgroundColor('#F1F3F5')

            // 水平图片列表
            List() {
                ForEach(this.images, (item:Image) => {
                    ListItem() {
                        Column() {
                            // 图片
                            Image(item.image)
                                .width(160)
                                .height(120)
                                .borderRadius(8)
                                .objectFit(ImageFit.Cover)

                            // 图片标题
                            Text(item.title)
                                .fontSize(14)
                                .margin({ top: 8 })
                                .maxLines(1)
                                .textOverflow({ overflow: TextOverflow.Ellipsis })
                        }
                        .alignItems(HorizontalAlign.Center)
                        .width(160)
                    }
                    .padding({ right: 12 })
                })
            }
            .width('100%')
            .height(180)
            .margin({ top: 16 })
            .listDirection(Axis.Horizontal) // 设置为水平列表
            .padding({ left: 16 })

            // 标题栏
            Row() {
                Text('推荐照片')
                    .fontSize(20)
                    .fontWeight(FontWeight.Bold)
            }
            .width('100%')
            .height(56)
            .padding({ left: 16 })
            .margin({ top: 16 })

            // 多行水平图片列表
            List() {
                ForEach(this.images, (item:Image) => {
                    ListItem() {
                        Column() {
                            // 图片
                            Image(item.image)
                                .width(120)
                                .height(90)
                                .borderRadius(8)
                                .objectFit(ImageFit.Cover)

                            // 图片标题
                            Text(item.title)
                                .fontSize(14)
                                .margin({ top: 8 })
                                .maxLines(1)
                                .textOverflow({ overflow: TextOverflow.Ellipsis })
                        }
                        .alignItems(HorizontalAlign.Center)
                        .width(120)
                    }
                    .padding({ right: 12, bottom: 16 })
                })
            }
            .width('100%')
            .layoutWeight(1)
            .listDirection(Axis.Horizontal) // 设置为水平列表
            .lanes(2) // 设置为2行
            .padding({ left: 16 })
        }
        .width('100%')
        .height('100%')
        .backgroundColor('#FFFFFF')
    }
}

4.1 代码结构分析

部分 说明
@Component 组件装饰器,表明这是一个自定义组件
private images 私有属性,存储图片数据
build() 组件的构建函数,定义组件的UI结构
Column 根容器,垂直排列子元素
Row 标题栏容器,水平排列子元素
List 列表容器,用于展示图片列表
ForEach 循环构建器,遍历图片数据
ListItem 列表项容器,定义每个图片的布局

4.2 样式设置分析

在这个案例中,我们使用了多种样式设置来美化界面:

  1. 尺寸设置

    • 使用width('100%')和height('100%')使组件占满父容器
    • 使用layoutWeight(1)使第二个List组件占据剩余空间
    • 设置具体的像素值,如height(180)、width(160)等
  2. 边距和填充

    • 使用padding设置内边距
    • 使用margin设置外边距
  3. 颜色和背景

    • 设置背景颜色backgroundColor
  4. 文本样式

    • 设置字体大小fontSize
    • 设置字体粗细fontWeight
    • 设置最大行数maxLines和文本溢出处理textOverflow
  5. 特殊效果

    • 使用borderRadius设置图片圆角效果
    • 使用objectFit设置图片填充模式

总结

在本篇教程中,我们学习了如何使用HarmonyOS NEXT的List和ListItem组件创建水平方向的图片列表。我们从数据模型定义、页面结构设计到列表项实现,全面讲解了水平列表的实现过程。 水平列表是一种非常实用的UI组件,特别适合展示图片、卡片等内容。通过本教程的学习,你应该能够在自己的应用中灵活运用水平列表,创建出更加丰富和用户友好的界面。

收藏00

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

全栈若城

  • 0回答
  • 4粉丝
  • 0关注
相关话题