163.[HarmonyOS NEXT 实战案例三:Grid] 不规则网格布局基础篇:打造新闻应用首页
2025-06-30 22:43:09
104次阅读
0个评论
[HarmonyOS NEXT 实战案例三:Grid] 不规则网格布局基础篇:打造新闻应用首页
项目已开源,开源地址: https://gitcode.com/nutpi/HarmonyosNextCaseStudyTutorial , 欢迎fork & star
效果演示
1. Grid组件与不规则网格布局概述
在HarmonyOS NEXT的ArkUI框架中,Grid组件是一种强大的布局容器,它允许开发者创建灵活多变的网格布局。不规则网格布局是Grid组件的一种高级应用方式,通过设置GridItem的行列起止位置,可以让单个网格项占据多行多列,从而实现更加丰富的界面效果。
1.1 不规则网格布局的特点
不规则网格布局具有以下特点:
特点 | 描述 |
---|---|
灵活的空间分配 | 可以让重要内容占据更大的空间,次要内容占据较小空间 |
视觉层次分明 | 通过不同大小的网格项,自然形成视觉焦点和层次 |
信息密度优化 | 在有限空间内高效展示不同重要程度的信息 |
适应不同内容类型 | 可根据内容类型(图片、文本等)分配合适的空间 |
响应式布局支持 | 可以根据屏幕尺寸调整网格项的大小和位置 |
1.2 Grid与GridItem的关系
在使用Grid组件创建不规则网格布局时,需要理解Grid与GridItem的关系:
- Grid是容器组件,用于设置整体网格布局的参数
- GridItem是子组件,必须作为Grid的直接子组件使用
- Grid通过rowsTemplate和columnsTemplate属性定义网格的行列结构
- GridItem通过rowStart、rowEnd、columnStart、columnEnd属性定义其在网格中的位置和跨度
2. 新闻应用首页实战
在本案例中,我们将使用Grid和GridItem组件创建一个新闻应用的首页,展示不同类型和重要程度的新闻内容。
2.1 页面结构概览
我们的新闻应用首页包含以下几个部分:
- 顶部导航栏:显示当前位置和操作按钮
- 分类标签栏:显示新闻分类选项
- 新闻网格布局:使用不规则网格展示新闻内容
- 底部导航栏:提供应用的主要导航选项
2.2 数据模型定义
首先,我们定义新闻数据的接口:
interface NewsData{
id: number,
title: string,
summary?: string,
image: Resource,
category: string,
time: string,
isHot?: boolean,
isTop?: boolean,
readCount?: number
}
这个接口定义了新闻的基本属性,包括:
- 唯一标识(id)
- 标题(title)
- 摘要(summary,可选)
- 图片资源(image)
- 分类(category)
- 发布时间(time)
- 是否热门(isHot,可选)
- 是否置顶(isTop,可选)
- 阅读数(readCount,可选)
2.3 顶部导航栏实现
// 顶部导航栏
Row() {
Row() {
Image($r('app.media.location_icon'))
.width(16)
.height(16)
.fillColor('#FF6B35')
Text('北京')
.fontSize(16)
.fontColor('#333333')
.margin({ left: 4 })
Image($r('app.media.arrowright'))
.width(12)
.height(12)
.fillColor('#666666')
.margin({ left: 4 })
}
Blank()
Row() {
Button() {
Image($r('app.media.search_icon'))
.width(20)
.height(20)
.fillColor('#666666')
}
.width(36)
.height(36)
.borderRadius(18)
.backgroundColor('#F5F5F5')
Button() {
Image($r('app.media.home_icon'))
.width(20)
.height(20)
.fillColor('#666666')
}
.width(36)
.height(36)
.borderRadius(18)
.backgroundColor('#F5F5F5')
.margin({ left: 8 })
}
}
.width('100%')
.padding({ left: 16, right: 16, top: 8, bottom: 8 })
.backgroundColor('#FFFFFF')
顶部导航栏包含:
- 左侧位置信息:显示当前城市和下拉箭头
- 右侧操作按钮:搜索按钮和主页按钮
2.4 分类标签栏实现
// 分类标签栏
Row() {
Text('推荐')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#FF6B35')
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.borderRadius(16)
.backgroundColor('rgba(255, 107, 53, 0.1)')
Text('科技')
.fontSize(14)
.fontColor('#666666')
.margin({ left: 16 })
Text('财经')
.fontSize(14)
.fontColor('#666666')
.margin({ left: 16 })
Text('体育')
.fontSize(14)
.fontColor('#666666')
.margin({ left: 16 })
Text('娱乐')
.fontSize(14)
.fontColor('#666666')
.margin({ left: 16 })
Blank()
Image($r('app.media.more_icon'))
.width(16)
.height(16)
.fillColor('#666666')
}
.width('100%')
.padding({ left: 16, right: 16, top: 12, bottom: 12 })
.backgroundColor('#FFFFFF')
分类标签栏包含:
- 当前选中的分类(推荐):使用特殊样式突出显示
- 其他分类选项:科技、财经、体育、娱乐
- 右侧更多按钮:用于显示更多分类
3. 不规则网格布局实现
3.1 网格容器配置
// 新闻网格布局
Grid() {
// 子项内容...
}
.rowsTemplate('200px 200px 100px 100px 100px') // 定义行高
.columnsTemplate('1fr 1fr 1fr') // 3列布局
.rowsGap(12)
.columnsGap(12)
.width('100%')
.layoutWeight(1)
.padding({ left: 16, right: 16, top: 16, bottom: 16 })
.backgroundColor('#F8F8F8')
网格容器配置:
- 行高定义:前两行高度为200px,后三行高度为100px
- 列宽定义:三列等宽布局(1fr 1fr 1fr)
- 行列间距:均为12px
- 外边距:四周均为16px
3.2 头条新闻实现(占据2行2列)
// 头条新闻 - 占据2行2列
GridItem() {
Stack({ alignContent: Alignment.BottomStart }) {
Image(this.newsData[0].image)
.width('100%')
.height('100%')
.objectFit(ImageFit.Cover)
.borderRadius(12)
// 渐变遮罩
Column()
.width('100%')
.height('100%')
.borderRadius(12)
.linearGradient({
direction: GradientDirection.Bottom,
colors: [['rgba(0, 0, 0, 0)', 0.0], ['rgba(0, 0, 0, 0.7)', 1.0]]
})
// 新闻信息
Column() {
Row() {
if (this.newsData[0].isTop) {
Text('置顶')
.fontSize(10)
.fontColor('#FFFFFF')
.backgroundColor('#FF6B35')
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
.borderRadius(4)
}
Text(this.newsData[0].category)
.fontSize(10)
.fontColor('#FFFFFF')
.backgroundColor('rgba(255, 255, 255, 0.3)')
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
.borderRadius(4)
.margin({ left: this.newsData[0].isTop ? 6 : 0 })
Blank()
}
.width('100%')
Text(this.newsData[0].title)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.margin({ top: 8 })
if (this.newsData[0].summary) {
Text(this.newsData[0].summary)
.fontSize(12)
.fontColor('rgba(255, 255, 255, 0.8)')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.margin({ top: 4 })
}
Row() {
Text(this.newsData[0].time)
.fontSize(10)
.fontColor('rgba(255, 255, 255, 0.7)')
Blank()
Row() {
Image($r('app.media.cart_icon'))
.width(10)
.height(10)
.fillColor('rgba(255, 255, 255, 0.7)')
Text(`${this.newsData[0].readCount}`)
.fontSize(10)
.fontColor('rgba(255, 255, 255, 0.7)')
.margin({ left: 2 })
}
}
.width('100%')
.margin({ top: 8 })
}
.alignItems(HorizontalAlign.Start)
.width('100%')
.padding(16)
}
.width('100%')
.height('100%')
}
.rowStart(0)
.rowEnd(1)
.columnStart(0)
.columnEnd(1)
.onClick(() => {
console.log(`点击头条新闻: ${this.newsData[0].title}`)
})
头条新闻特点:
- 位置定义:从第0行开始到第1行结束,从第0列开始到第1列结束(占据2行2列)
- 布局结构:使用Stack布局,底层是图片,中间是渐变遮罩,顶层是新闻信息
- 渐变效果:从透明到半透明黑色的渐变,增强文字可读性
- 信息展示:包含置顶标签、分类标签、标题、摘要、时间和阅读数
3.3 右侧小新闻实现(各占1格)
// 右侧小新闻1
GridItem() {
Column() {
Image(this.newsData[1].image)
.width('100%')
.height(80)
.objectFit(ImageFit.Cover)
.borderRadius(8)
Column() {
Row() {
if (this.newsData[1].isHot) {
Text('热')
.fontSize(10)
.fontColor('#FFFFFF')
.backgroundColor('#FF3B30')
.padding({ left: 4, right: 4, top: 1, bottom: 1 })
.borderRadius(2)
}
Text(this.newsData[1].category)
.fontSize(10)
.fontColor('#FF6B35')
.margin({ left: this.newsData[1].isHot ? 4 : 0 })
Blank()
}
.width('100%')
Text(this.newsData[1].title)
.fontSize(14)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.margin({ top: 4 })
Row() {
Text(this.newsData[1].time)
.fontSize(10)
.fontColor('#999999')
Blank()
Text(`${this.newsData[1].readCount}`)
.fontSize(10)
.fontColor('#999999')
}
.width('100%')
.margin({ top: 6 })
}
.alignItems(HorizontalAlign.Start)
.width('100%')
.margin({ top: 8 })
}
.width('100%')
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(12)
.shadow({
radius: 4,
color: 'rgba(0, 0, 0, 0.1)',
offsetX: 0,
offsetY: 1
})
}
.rowStart(0)
.columnStart(2)
.onClick(() => {
console.log(`点击新闻: ${this.newsData[1].title}`)
})
右侧小新闻特点:
- 位置定义:位于第0行,第2列(占据1格)
- 布局结构:使用Column布局,上方是图片,下方是新闻信息
- 卡片样式:白色背景、圆角和阴影效果
- 信息展示:包含热门标签、分类、标题、时间和阅读数
3.4 底部横向新闻列表实现(跨列)
// 底部横向新闻列表
ForEach(this.newsData.slice(3), (news:NewsData, index) => {
GridItem() {
Row() {
Image(news.image)
.width(80)
.height(60)
.objectFit(ImageFit.Cover)
.borderRadius(8)
Column() {
Text(news.category)
.fontSize(10)
.fontColor('#FF6B35')
.alignSelf(ItemAlign.Start)
Text(news.title)
.fontSize(14)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.margin({ top: 4 })
Row() {
Text(news.time)
.fontSize(10)
.fontColor('#999999')
Blank()
Text(`${news.readCount}`)
.fontSize(10)
.fontColor('#999999')
}
.width('100%')
.margin({ top: 6 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
.margin({ left: 12 })
}
.width('100%')
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(12)
.shadow({
radius: 4,
color: 'rgba(0, 0, 0, 0.1)',
offsetX: 0,
offsetY: 1
})
}
.columnStart(0)
.columnEnd(2)
.onClick(() => {
console.log(`点击新闻: ${news.title}`)
})
})
底部横向新闻列表特点:
- 位置定义:从第0列开始到第2列结束(跨越3列)
- 布局结构:使用Row布局,左侧是图片,右侧是新闻信息
- 循环渲染:使用ForEach循环渲染多条新闻
- 信息展示:包含分类、标题、时间和阅读数
4. Grid组件关键属性解析
4.1 Grid容器属性
属性 | 说明 | 示例 |
---|---|---|
rowsTemplate | 设置行高和行数 | '200px 200px 100px 100px 100px' |
columnsTemplate | 设置列宽和列数 | '1fr 1fr 1fr' |
rowsGap | 行间距 | 12 |
columnsGap | 列间距 | 12 |
layoutDirection | 布局方向 | GridDirection.Row |
4.2 GridItem定位属性
属性 | 说明 | 示例 |
---|---|---|
rowStart | 起始行索引 | 0 |
rowEnd | 结束行索引 | 1 |
columnStart | 起始列索引 | 0 |
columnEnd | 结束列索引 | 1 |
5. 布局技巧与最佳实践
5.1 不规则网格布局设计原则
- 重要内容优先:将重要内容放在视觉焦点位置,并分配更大的空间
- 层次分明:通过不同大小的网格项创建视觉层次
- 内容适配:根据内容类型选择合适的网格大小和布局
- 一致性:保持设计语言的一致性,包括间距、圆角、阴影等
- 响应式考虑:设计时考虑不同屏幕尺寸下的布局效果
5.2 网格定位技巧
在不规则网格布局中,正确设置GridItem的位置非常重要:
// 示例1:占据第0行到第1行,第0列到第1列(2行2列)
GridItem() {
// 内容...
}
.rowStart(0)
.rowEnd(1)
.columnStart(0)
.columnEnd(1)
// 示例2:仅占据第0行,第2列(1格)
GridItem() {
// 内容...
}
.rowStart(0)
.columnStart(2)
// 示例3:跨列显示(从第0列到第2列)
GridItem() {
// 内容...
}
.columnStart(0)
.columnEnd(2)
5.3 卡片设计技巧
在新闻应用中,卡片设计对用户体验至关重要:
- 一致的圆角:所有卡片使用一致的圆角值(如12px)
- 适当的阴影:添加轻微阴影增强层次感
- 内边距统一:保持卡片内边距一致(如12px)
- 图文比例:保持合适的图文比例,增强可读性
- 信息层级:通过字体大小、颜色和间距区分信息层级
6. 总结
在本教程中,我们学习了如何使用HarmonyOS NEXT中的Grid和GridItem组件创建不规则网格布局,并实现了一个新闻应用首页。通过合理设置Grid容器的行列结构和GridItem的位置属性,我们成功创建了一个层次分明、重点突出的界面。
00
- 0回答
- 4粉丝
- 0关注
相关话题
- 164.[HarmonyOS NEXT 实战案例三:Grid] 不规则网格布局进阶篇:新闻应用高级布局与交互
- 165.[HarmonyOS NEXT 实战案例三:Grid] 不规则网格布局高级篇:复杂布局与高级技巧
- 160.[HarmonyOS NEXT 实战案例二:Grid] 照片相册网格布局:基础篇
- 172.[HarmonyOS NEXT 实战案例六:Grid] 响应式网格布局 - 基础篇
- 166.[HarmonyOS NEXT 实战案例四:Grid] 可滚动网格布局基础篇
- 169.[HarmonyOS NEXT 实战案例五:Grid] 动态网格布局基础篇
- 178.[HarmonyOS NEXT 实战案例八:Grid] 瀑布流网格布局基础篇
- 184.[HarmonyOS NEXT 实战案例十:Grid] 仪表板网格布局基础篇
- 181.[HarmonyOS NEXT 实战案例九:Grid] 电商网格布局基础篇:打造精美商品展示页面
- 157.[HarmonyOS NEXT 实战案例一:Grid] 基础网格布局:打造精美电商商品列表
- 175.[HarmonyOS NEXT 实战案例七:Grid] 嵌套网格布局基础篇:打造企业级仪表板界面
- 171.[HarmonyOS NEXT 实战案例五:Grid] 动态网格布局高级篇
- 162.[HarmonyOS NEXT 实战案例二:Grid] 照片相册网格布局:高级篇
- 168.[HarmonyOS NEXT 实战案例四:Grid] 可滚动网格布局高级篇
- 174.[HarmonyOS NEXT 实战案例六:Grid] 响应式网格布局 - 高级篇