HarmonyOS 5开发从入门到精通(十七):新闻阅读应用实战(上)
HarmonyOS 5开发从入门到精通(十七):新闻阅读应用实战(上)
本章将带领大家开发一个完整的新闻阅读应用,涵盖新闻列表展示、详情页跳转、网络数据获取等核心功能。通过本案例,你将掌握HarmonyOS应用开发的关键技能。
一、核心概念
1. 新闻数据模型与网络请求
新闻应用的核心是获取远程API数据并转换为本地数据模型。HarmonyOS提供了@ohos.net.http模块进行网络请求,配合JSON解析将服务器返回的数据转换为TypeScript对象,实现数据驱动UI。
2. 页面路由与导航
页面导航是应用的核心交互方式,通过@ohos.router模块实现页面跳转和参数传递。结合ArkUI的声明式开发范式,可以构建流畅的页面切换体验。
二、关键API详解
1. 网络请求模块
import http from '@ohos.net.http'
// 创建HTTP请求实例
const httpRequest = http.createHttp()
// 发起GET请求
httpRequest.request(url, {
method: http.RequestMethod.GET,
connectTimeout: 10000,
readTimeout: 10000
})
2. JSON解析
// 解析JSON字符串
const newsData = JSON.parse(response.result)
// 转换为JSON字符串
const jsonStr = JSON.stringify(newsData)
3. 新闻数据模型
class NewsItem {
id: string = ''
title: string = ''
content: string = ''
publishTime: string = ''
source: string = ''
}
4. 页面路由
import router from '@ohos.router'
// 跳转到详情页
router.pushUrl({
url: 'pages/NewsDetail',
params: { id: newsId }
})
// 接收参数
const params = router.getParams()
const newsId = params['id']
5. 列表组件渲染
List() {
ForEach(this.newsList, (news: NewsItem) => {
ListItem() {
Text(news.title)
}
})
}
6. 异步数据获取
async getNewsList(): Promise<NewsItem[]> {
try {
const response = await httpRequest.request(url)
return JSON.parse(response.result)
} catch (error) {
console.error('获取新闻数据失败')
}
}
7. 权限配置
{
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
8. 状态管理
@State newsList: NewsItem[] = []
@State isLoading: boolean = true
9. 页面生命周期
aboutToAppear() {
this.loadNewsData()
}
onPageShow() {
this.refreshData()
}
10. 错误处理
try {
// 网络请求
} catch (error) {
console.error('请求失败:', error)
}
11. 数据绑定
Text(this.newsList.length + '条新闻')
.fontSize(16)
.fontColor(Color.Gray)
12. 组件复用
@Component
struct NewsCard {
@Prop news: NewsItem
build() {
Column() {
Text(this.news.title)
Text(this.news.publishTime)
}
}
}
三、实战案例
完整代码实现
import http from '@ohos.net.http'
import router from '@ohos.router'
// 新闻数据模型
class NewsItem {
id: string = ''
title: string = ''
content: string = ''
publishTime: string = ''
source: string = ''
}
@Entry
@Component
struct NewsApp {
@State newsList: NewsItem[] = []
@State isLoading: boolean = true
@State errorMessage: string = ''
private httpRequest = http.createHttp()
aboutToAppear() {
this.loadNewsData()
}
// 加载新闻数据
private async loadNewsData() {
try {
this.isLoading = true
const url = 'https://api.example.com/news'
const response = await this.httpRequest.request(url, {
method: http.RequestMethod.GET
})
if (response.responseCode === 200) {
const data = JSON.parse(response.result)
this.newsList = data.map((item: any) => ({
id: item.id,
title: item.title,
content: item.content,
publishTime: item.publishTime,
source: item.source
}))
}
} catch (error) {
this.errorMessage = '网络请求失败,请检查网络连接'
console.error('获取新闻数据失败:', error)
} finally {
this.isLoading = false
}
}
// 跳转到详情页
private navigateToDetail(news: NewsItem) {
router.pushUrl({
url: 'pages/NewsDetail',
params: {
id: news.id,
title: news.title,
content: news.content,
publishTime: news.publishTime,
source: news.source
}
})
}
build() {
Column() {
// 标题栏
Row() {
Text('新闻头条')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ left: 20 })
Button('刷新')
.margin({ left: 20 })
.onClick(() => this.loadNewsData())
}
.width('100%')
.height(60)
.backgroundColor(Color.White)
.justifyContent(FlexAlign.SpaceBetween)
// 内容区域
if (this.isLoading) {
LoadingProgress()
.width(50)
.height(50)
.margin({ top: 100 })
} else if (this.errorMessage) {
Text(this.errorMessage)
.fontSize(16)
.fontColor(Color.Red)
.margin({ top: 100 })
} else {
List({ space: 10 }) {
ForEach(this.newsList, (news: NewsItem) => {
ListItem() {
NewsCard({ news: news })
.onClick(() => this.navigateToDetail(news))
}
})
}
.width('100%')
.height('100%')
}
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
}
// 新闻卡片组件
@Component
struct NewsCard {
@Prop news: NewsItem
build() {
Column() {
// 新闻标题
Text(this.news.title)
.fontSize(18)
.fontColor(Color.Black)
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.margin({ bottom: 8 })
// 新闻来源和时间
Row() {
Text(this.news.source)
.fontSize(14)
.fontColor(Color.Gray)
Text(this.news.publishTime)
.fontSize(14)
.fontColor(Color.Gray)
.margin({ left: 10 })
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.padding(16)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({ radius: 2, color: '#10000000', offsetX: 0, offsetY: 1 })
.margin({ left: 16, right: 16, top: 8, bottom: 8 })
}
}
// 新闻详情页
@Entry
@Component
struct NewsDetail {
@State title: string = ''
@State content: string = ''
@State publishTime: string = ''
@State source: string = ''
aboutToAppear() {
const params = router.getParams()
this.title = params['title'] || ''
this.content = params['content'] || ''
this.publishTime = params['publishTime'] || ''
this.source = params['source'] || ''
}
build() {
Column() {
// 返回按钮
Row() {
Image($r('app.media.back'))
.width(24)
.height(24)
.onClick(() => router.back())
Text('返回')
.fontSize(16)
.margin({ left: 8 })
}
.width('100%')
.padding(16)
.justifyContent(FlexAlign.Start)
// 新闻内容
Scroll() {
Column() {
Text(this.title)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 16 })
Row() {
Text(this.source)
.fontSize(14)
.fontColor(Color.Gray)
Text(this.publishTime)
.fontSize(14)
.fontColor(Color.Gray)
.margin({ left: 10 })
}
.margin({ bottom: 20 })
Text(this.content)
.fontSize(16)
.lineHeight(24)
}
.padding(16)
}
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
.backgroundColor(Color.White)
}
}
四、总结
✅ 关键知识点
- 网络请求的异步处理与错误捕获
- JSON数据的解析与模型转换
- 页面路由的参数传递与接收
- 列表组件的渲染与点击事件处理
- 组件复用的最佳实践
🔧 核心API列表
http.createHttp()- 创建HTTP请求实例http.RequestMethod.GET- GET请求方法JSON.parse()- JSON字符串解析router.pushUrl()- 页面跳转router.getParams()- 获取路由参数ForEach()- 列表数据循环渲染@State- 状态管理装饰器@Prop- 属性传递装饰器async/await- 异步编程语法糖
💡 应用建议
- 网络请求建议使用
try-catch包裹,处理网络异常 - 列表数据可添加分页加载,提升用户体验
- 建议使用真实的新闻API替换示例中的模拟接口
- 可以添加下拉刷新功能,方便用户更新数据
通过本章学习,你已经掌握了新闻阅读应用的核心开发技能。下一章我们将继续完善应用,添加更多实用功能。
浙公网安备 33010602011771号