135.[HarmonyOS NEXT 实战案例七:List系列] 水平列表组件实战:打造精美图片库 基础篇 原创
[HarmonyOS NEXT 实战案例七:List系列] 水平列表组件实战:打造精美图片库 基础篇
项目已开源,开源地址: https://gitcode.com/nutpi/HarmonyosNextCaseStudyTutorial , 欢迎fork & star
效果演示
![135.[HarmonyOS NEXT 实战案例七:List系列] 水平列表组件实战:打造精美图片库 基础篇-鸿蒙开发者社区 135.[HarmonyOS NEXT 实战案例七:List系列] 水平列表组件实战:打造精美图片库 基础篇-鸿蒙开发者社区](https://dl-harmonyos.51cto.com/images/202506/19f8f4240f1573086a805031be472a1bdeb1a2.png?x-oss-process=image/resize,w_373,h_597)
![135.[HarmonyOS NEXT 实战案例七:List系列] 水平列表组件实战:打造精美图片库 基础篇-鸿蒙开发者社区 135.[HarmonyOS NEXT 实战案例七:List系列] 水平列表组件实战:打造精美图片库 基础篇-鸿蒙开发者社区](https://dl-harmonyos.51cto.com/images/202506/165772b15dc9b61f0143903cf6f8c5410e3cdb.png?x-oss-process=image/resize,w_373,h_597)
一、水平列表概述
在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 })
在这段代码中:
- 使用ForEach遍历images数组,为每张图片创建一个ListItem
 - 每个ListItem包含一个Column布局,用于垂直排列图片和标题
 - 图片使用Image组件,设置宽高和圆角
 - 标题使用Text组件,设置最大行数和文本溢出处理
 - 设置ListItem的右侧内边距,使列表项之间有间隔
 - 最重要的是,通过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 })
这个列表与第一个列表的主要区别在于:
- 图片和列表项的尺寸略小
 - 添加了底部内边距,使行与行之间有间隔
 - 最重要的是,通过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 样式设置分析
在这个案例中,我们使用了多种样式设置来美化界面:
- 
尺寸设置:
- 使用width(‘100%’)和height(‘100%’)使组件占满父容器
 - 使用layoutWeight(1)使第二个List组件占据剩余空间
 - 设置具体的像素值,如height(180)、width(160)等
 
 - 
边距和填充:
- 使用padding设置内边距
 - 使用margin设置外边距
 
 - 
颜色和背景:
- 设置背景颜色backgroundColor
 
 - 
文本样式:
- 设置字体大小fontSize
 - 设置字体粗细fontWeight
 - 设置最大行数maxLines和文本溢出处理textOverflow
 
 - 
特殊效果:
- 使用borderRadius设置图片圆角效果
 - 使用objectFit设置图片填充模式
 
 
总结
在本篇教程中,我们学习了如何使用HarmonyOS NEXT的List和ListItem组件创建水平方向的图片列表。我们从数据模型定义、页面结构设计到列表项实现,全面讲解了水平列表的实现过程。
水平列表是一种非常实用的UI组件,特别适合展示图片、卡片等内容。通过本教程的学习,你应该能够在自己的应用中灵活运用水平列表,创建出更加丰富和用户友好的界面。




















