回复
     163.[HarmonyOS NEXT 实战案例三:Grid] 不规则网格布局基础篇:打造新闻应用首页 原创
全栈若城
 发布于 2025-6-30 16:05
 浏览
 0收藏
[HarmonyOS NEXT 实战案例三:Grid] 不规则网格布局基础篇:打造新闻应用首页
项目已开源,开源地址: https://gitcode.com/nutpi/HarmonyosNextCaseStudyTutorial , 欢迎fork & star
效果演示
![163.[HarmonyOS NEXT 实战案例三:Grid] 不规则网格布局基础篇:打造新闻应用首页-鸿蒙开发者社区 163.[HarmonyOS NEXT 实战案例三:Grid] 不规则网格布局基础篇:打造新闻应用首页-鸿蒙开发者社区](https://dl-harmonyos.51cto.com/images/202506/13ba8f25466767fa94b730ba2efb2269ba3d62.png?x-oss-process=image/resize,w_373,h_597)
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的位置属性,我们成功创建了一个层次分明、重点突出的界面。
©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
     
        赞
        
 
        收藏 
      
 回复
  相关推荐
 



















