鸿蒙原生应用实战(三):诗词详情与作者天地页面——复杂数据展示与交互设计
文章摘要 本文介绍了鸿蒙原生应用中两个核心页面的开发实践: 诗词详情页:设计了包含12套完整诗词数据的结构,实现了注释、背景、赏析等复杂内容的展示。通过折叠面板、路由参数接收和状态管理等技术,构建了具有交互性的详情页面。 作者天地页:整理了8位诗人信息,包括代表作标签和风格特点。页面采用卡片式布局展示诗人画像、简介和代表作等丰富信息。 两个页面展示了ArkTS中复杂数据组织、组件化开发、交互设计等
鸿蒙原生应用实战(三):诗词详情与作者天地页面——复杂数据展示与交互设计
前言
前两章我们完成了项目架构和首页/诗词库页面的开发。本章将聚焦于应用中数据最密集的两个页面:
- 诗词详情页(PoemDetailPage.ets) —— 12 套完整诗词数据、注释、背景、赏析
- 作者天地页(AuthorPage.ets) —— 8 位诗人信息、代表作标签
这两个页面展示了 ArkTS 中复杂数据对象的组织、折叠面板交互、计时器等高级功能。
一、诗词详情页(PoemDetailPage.ets)
1.1 数据结构设计
详情页最核心的是数据结构设计。我们定义了一个包含完整信息的接口:
interface PoemAnnotation {
term: string; // 词语
meaning: string; // 释义
}
interface PoemFullData {
id: number;
title: string;
author: string;
dynasty: string;
fullContent: string[]; // 全文诗句数组
annotations: PoemAnnotation[]; // 逐词注释
appreciation: string; // 名家赏析
background: string; // 创作背景
type: string; // 诗词类型
}
1.2 12 套完整数据嵌入
在 App 内部直接嵌入 12 套诗词数据,避免了网络请求的依赖。数据以 Record<string, PoemFullData> 的形式组织,键为诗词 ID:
const poemsData: Record<string, PoemFullData> = {
'1': {
id: 1,
title: '静夜思',
author: '李白',
dynasty: '唐',
fullContent: ['床前明月光', '疑是地上霜', '举头望明月', '低头思故乡'],
annotations: [
{ term: '静夜思', meaning: '安静的夜晚产生的思绪。' },
{ term: '疑', meaning: '好像,仿佛。' },
{ term: '举头', meaning: '抬头。' }
],
appreciation: '这首诗描绘了秋日夜晚旅居在外的游子对故乡的深切思念...',
background: '此诗作于唐玄宗开元十四年(726年),李白在扬州旅舍...',
type: '五言绝句'
},
'2': { /* 春晓——孟浩然 */ },
'3': { /* 水调歌头——苏轼 */ },
// ... 共 12 套完整数据
};
1.3 路由参数接收
详情页通过 aboutToAppear 生命周期方法接收路由参数:
@State @Watch('onPoemIdChange') poemId: number = 1;
@State poemData: PoemFullData = poemsData['1'];
aboutToAppear(): void {
const params = router.getParams() as Record<string, Object>;
if (params && params['poemId'] !== undefined) {
this.poemId = params['poemId'] as number;
}
}
onPoemIdChange(): void {
this.poemData = poemsData[this.poemId.toString()] || poemsData['1'];
}
要点:
@Watch('onPoemIdChange')确保当poemId变化时,poemData自动更新。
1.4 页面结构
详情页从上到下包含:
┌──────────────────────────┐
│ ← 返回 ⭐ 收藏 ··· │ ← 顶部操作栏
├──────────────────────────┤
│ 静 夜 思 │ ← 标题(大字号+字间距)
│ 唐 · 李白 │
│ 五言绝句 [标签] │
├──────────────────────────┤
│ 床前明月光 │
│ 疑是地上霜 │ ← 全文展示(居中+行间距)
│ 举头望明月 │
│ 低头思故乡 │
├──────────────────────────┤
│ ❤ 点赞 📖 朗读 📤 分享 │ ← 操作按钮组
├──────────────────────────┤
│ 📝 注释 │
│ 静夜思 │ 安静夜晚的思绪 │ ← 逐词注释
│ 疑 │ 好像,仿佛 │
│ 举头 │ 抬头 │
├──────────────────────────┤
│ 📜 创作背景 [展开 ▼] │ ← 折叠面板
│ (点击展开后显示内容) │
├──────────────────────────┤
│ 🎨 赏析 │ ← 底部
└──────────────────────────┘
1.5 全文展示
诗文正文居中展示,每句诗单独一行,设置合适的行高和字间距:
ForEach(this.poemData.fullContent, (line: string) => {
Text(line)
.fontSize(19)
.fontColor($r('app.color.text_primary'))
.lineHeight(36) // 行高,增加呼吸感
.letterSpacing(2) // 字间距,增强古典韵味
.textAlign(TextAlign.Center)
.width('100%')
.padding({ top: 6 })
}, (line: string) => line)
1.6 注释区域
注释区域使用 ForEach 遍历 annotations 数组,每行显示词语和释义:
ForEach(this.poemData.annotations, (ann: PoemAnnotation) => {
Row() {
Text(ann.term)
.fontSize(14)
.fontColor($r('app.color.accent_purple'))
.fontWeight(FontWeight.Medium)
Text(ann.meaning)
.fontSize(13)
.fontColor($r('app.color.text_secondary'))
.margin({ left: 8 })
.layoutWeight(1)
}
.width('100%')
.alignItems(VerticalAlign.Top)
.padding({ top: 4, bottom: 4 })
}, (ann: PoemAnnotation) => ann.term)
1.7 折叠面板(创作背景)
创作背景默认折叠,点击"展开"按钮后显示。状态由 isShowFull 控制:
@State isShowFull: boolean = false;
Column() {
Row() {
Text('📜 创作背景')
.fontSize(16)
.fontWeight(FontWeight.Bold)
Blank()
Text(this.isShowFull ? '收起 ▲' : '展开 ▼')
.fontSize(12)
.fontColor($r('app.color.accent_purple'))
}
.width('100%')
.onClick(() => { this.isShowFull = !this.isShowFull; })
// 折叠内容——只有展开时渲染
if (this.isShowFull) {
Text(this.poemData.background)
.fontSize(14)
.fontColor($r('app.color.text_primary'))
.lineHeight(22)
.padding({ top: 10 })
}
}
技巧:在 ArkTS 中,
if条件包裹的组件只有条件为 true 时才会被创建,天然实现了懒加载。
1.8 交互操作
页面提供了多种交互:
// 收藏切换
Text(this.isFav ? '⭐' : '☆')
.fontSize(22)
.onClick(() => { this.isFav = !this.isFav; })
// 点赞切换
Row() {
Text(this.isLiked ? '❤' : '🤍').fontSize(20)
Text('点赞').fontSize(13).margin({ left: 4 })
}.onClick(() => { this.isLiked = !this.isLiked; })
二、作者天地页(AuthorPage.ets)
2.1 诗人数据
我们整理了 8 位最具代表性的诗人,每位包含简介、生卒年、风格标签和代表作:
const authors: AuthorInfo[] = [
{
id: 1,
name: '李白',
dynasty: '唐',
avatar: '🪶',
bio: '字太白,号青莲居士,唐代伟大的浪漫主义诗人,被后人誉为"诗仙"。',
birthDeath: '701年—762年',
poemCount: 1010,
style: '飘逸奔放',
masterpieces: ['静夜思', '将进酒', '望庐山瀑布', '蜀道难', '行路难']
},
// 杜甫、苏轼、李清照、白居易、李商隐、王维、李煜
];
2.2 诗人卡片设计
每个诗人卡片展示丰富的信息层次:
@Builder
createAuthorCard(author: AuthorInfo) {
Column() {
Row() {
// Emoji 头像
Text(author.avatar).fontSize(40)
Column() {
Row() {
Text(author.name).fontSize(20).fontWeight(FontWeight.Bold)
Text(author.dynasty + '代').fontSize(12)
.fontColor($r('app.color.text_secondary'))
}
Text(author.birthDeath).fontSize(12)
.fontColor($r('app.color.text_secondary'))
Row() {
Text('风格: ' + author.style)
.fontSize(11).fontColor($r('app.color.accent_purple'))
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
.backgroundColor($r('app.color.accent_purple') + '15')
.borderRadius(4)
Text('📝 ' + author.poemCount.toString() + '首')
.fontSize(11).margin({ left: 8 })
}
}
.layoutWeight(1).padding({ left: 14 })
Text('>').fontSize(18).fontColor($r('app.color.text_secondary'))
}
// 人物简介(最多两行)
Text(author.bio)
.fontSize(13).lineHeight(20)
.maxLines(2).textOverflow({ overflow: TextOverflow.Ellipsis })
// 代表作标签
Row() {
Text('代表作: ').fontSize(12)
ForEach(author.masterpieces.slice(0, 3), (title: string) => {
Text(title)
.fontSize(12).fontColor($r('app.color.accent_purple'))
.padding({ left: 8, right: 8, top: 2, bottom: 2 })
.backgroundColor($r('app.color.accent_purple') + '10')
.borderRadius(4).margin({ right: 6 })
}, (title: string) => title)
}
}
.width('100%').padding(16)
.backgroundColor($r('app.color.bg_card'))
.borderRadius(16)
.onClick(() => {
// 点击跳转到诗词库并自动搜索该作者
router.pushUrl({
url: 'pages/PoemListPage',
params: { searchAuthor: author.name }
});
})
}
2.3 数据过滤
作者页同样使用 @State + @Watch 模式处理搜索和朝代筛选:
@State @Watch('onAuthorFilterChange') searchText: string = '';
@State @Watch('onAuthorFilterChange') activeDynasty: string = 'all';
@State filteredAuthorsList: AuthorInfo[] = authors;
onAuthorFilterChange(): void {
let result: AuthorInfo[] = authors;
if (this.searchText.length > 0) {
const kw: string = this.searchText.toLowerCase();
result = result.filter((a: AuthorInfo) => a.name.includes(kw));
}
if (this.activeDynasty !== 'all') {
result = result.filter((a: AuthorInfo) => a.dynasty === this.activeDynasty);
}
this.filteredAuthorsList = result;
}
2.4 @Builder 参数化筛选标签
因为 @Builder 内不能声明变量,我们通过参数传递来避免此限制:
// 在 build() 中直接调用 @Builder 方法
Row() {
this.dynastyFilter('all', '全部', 0)
this.dynastyFilter('唐', '唐代', 1)
this.dynastyFilter('宋', '宋代', 2)
this.dynastyFilter('五代', '五代', 3)
}
@Builder
dynastyFilter(opt: string, label: string, idx: number) {
Text(label)
.fontSize(13)
.fontColor(opt === this.activeDynasty ?
Color.White : $r('app.color.text_secondary'))
.padding({ left: 16, right: 16, top: 6, bottom: 6 })
.backgroundColor(opt === this.activeDynasty ?
$r('app.color.accent_purple') : $r('app.color.bg_card'))
.borderRadius(16)
.margin({ right: 8 })
.onClick(() => { this.activeDynasty = opt; })
}
三、组件复用最佳实践
3.1 底部导航栏的复用
所有页面共享同一套底部导航栏。为了避免重复代码,每个页面都定义了自己的 createBottomNav @Builder,通过参数 activePage 标识当前高亮项:
@Builder
createBottomNav(activePage: string) {
this.renderNavBar(activePage)
}
@Builder
renderNavBar(activePage: string): void {
Row() {
this.navItem('🏠', '首页', 'home', activePage, 'pages/Index')
this.navItem('📚', '诗词库', 'list', activePage, 'pages/PoemListPage')
this.navItem('👤', '作者', 'author', activePage, 'pages/AuthorPage')
this.navItem('⭐', '收藏', 'collection', activePage, 'pages/CollectionPage')
}
.width('100%').height(60)
.backgroundColor($r('app.color.bg_card'))
.shadow({ radius: 8, color: '#15000000', offsetX: 0, offsetY: -2 })
}
@Builder
navItem(icon: string, label: string, page: string,
activePage: string, route: string) {
Column() {
Text(icon).fontSize(22)
Text(label).fontSize(10)
.fontColor(page === activePage ?
$r('app.color.accent_purple') : $r('app.color.text_secondary'))
.margin({ top: 2 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Center)
.onClick(() => {
if (page !== activePage) { router.pushUrl({ url: route }); }
})
}
小结
本章完成了诗词详情页和作者天地页的开发,核心内容包括:
- 12 套完整诗词数据的组织和访问
- 折叠面板交互实现
- 诗人卡片的多层次信息展示
- @Builder 参数化设计避免变量声明限制
- 跨页面跳转时自动搜索作者
下一章我们将完成收藏页面和全局底部导航的整合,并确保所有页面之间的无缝跳转。
【系列目录】
- (一)项目初始化与架构设计
- (二)首页与诗词库页面开发
- (三)诗词详情与作者天地页面开发 ← 本文
- (四)收藏页面与底部导航实现
- (五)编译调试与问题修复经验
更多推荐




所有评论(0)