HarmonyOS NEXT 诗词元服务项目开发上架全流程实战(一、项目介绍及实现效果)

在当今数字化时代,如何让传统文化与现代科技相结合,成为了一个值得思考的问题。诗词作为中国传统文化的重要组成部分,承载着丰富的历史信息和文化内涵。为了让更多人了解和欣赏诗词的魅力,我们决定开发一款基于HarmonyOS NEXT的诗词元服务应用。这款应用不仅能够提供诗词的欣赏功能,还能通过现代化的交互方式,让传统诗词焕发出新的生命力。

项目目标

目标是打造一个精神的栖息地,为文人墨客和诗词爱好者提供一个互动交流的平台。同时,我们希望通过这个项目,让更多人能快速上述鸿蒙应用开发,不要说你不会,更不要说难。直接从实战上手,直接看到学习成果,获得成就感。完整展示从开发到打包,从签名上架的全套流程。同时能够欣赏到中国传统文化的精髓,体验诗词带来的美好感受。在开发过程中,我们将注重用户体验,确保用户能够便捷地翻阅诗词,以及获取诗词的详细信息。

在这里插入图片描述

功能特性

  1. 诗词展示:展示诗词的标题、作者、内容,支持翻页操作和查看诗词注释。
  2. 对话框交互:包含诗词解说对话框和建议与反馈对话框,提升用户交互体验。
  3. 页面导航:可通过 AtomicServiceTabs 组件在首页和关于页面间切换。
  4. 个人中心:展示作者及个人信息。

诗词展示

诗词展示功能是应用的核心之一,将展示诗词的标题、作者和正文内容。用户可以通过翻页操作来浏览不同的诗词。此外,我们还为每首诗词提供了详细的注释,包括词句的解释、背景介绍和作者的注解,帮助用户更好地理解诗词。

对话框交互

我们加入了诗词解说对话框和建议与反馈对话框,以提升用户的交互体验。诗词解说对话框可以显示诗词的详细解释、背景和注解,而建议与反馈对话框则让用户可以向我们提出建议或反馈意见,帮助我们不断改进应用。
在这里插入图片描述

页面导航

我们使用了AtomicServiceTabs组件,通过底部导航栏实现了首页和关于页面的切换。首页主要展示诗词内容,而关于页面则介绍项目背景和开发团队的信息。

数据内容

诗词数据以本地JSON格式存储,示例如下:

{"poet": "伟人","title": "致彭德怀同志","content": "山高路远坑深,大军纵横驰奔。\r\n谁敢横刀立马?唯我彭大将军。","intro": "1935年10月21日,红军到达吴起镇,毛泽东亲自督战迎敌,部署好战斗后,把指挥事宜交给彭德怀处理,战斗结束后,他当场赋此诗赠予彭德怀。这首诗中字里行间跳动着凯歌的欢快音符,更跳动着革命者那颗“爱将”之心。","comment": "","trans": "吴起镇山险路长沟深,中央红军骑马奔驰杀敌。有谁敢横刀立马,只有我彭德怀大将军。","annot": "坑深:地势险峻。\r\n唯我彭大将军:毛泽东写诗赠于,首句即用电文句,但改“沟深”为“坑深”。彭德怀收到这首诗后,将诗的末句“唯我彭大将军”改为“唯我英勇红军”,然后将原诗还给毛泽东。"
}

实现概述

读取JSON文件

为了实现诗词数据的展示,我们首先需要读取本地的JSON文件。以下是读取JSON文件的代码实现:

import { common } from "@kit.AbilityKit";
import { util } from "@kit.ArkTS";/*** @author: * @date: 2024/12/7 13:59* @description: 读取JSON文件* @version:*/
export function ReadJsonFile(fileStr: string): Promise<Array<Record<string, Object>>>{return new Promise<Array<Record<string, Object>>>(async (success, error) => {try {let context: common.UIAbilityContext = getContext() as common.UIAbilityContext;let jsonFile = await context.resourceManager.getRawFileContent(fileStr);let jsonString: string = util.TextDecoder.create('utf-8', {ignoreBOM: true}).decodeToString(jsonFile, { stream: false});success(JSON.parse(jsonString))} catch (e) {error([])}})
}

首页实现

首页通过AtomicServiceTabs组件实现页签切换,并展示诗词内容。以下是首页的实现代码:

import { AboutView } from '../view/AboutView';
import { HomeView } from '../view/HomeView';
import { AtomicServiceTabs, TabBarOptions, TabBarPosition } from '@ohos.atomicservice.AtomicServiceTabs';
import { CustomConstants } from '../common/constants/CustomConstants';
import { AtomicServiceNavigation } from '@ohos.atomicservice.AtomicServiceNavigation';
import { ViewData } from '../common/entity/ViewData';
import PersistentStorage from '@ohos.data.PersistentStorage';PersistentStorage.persistProp<ViewData[]>(CustomConstants.PRELOAD_DATA_KEY, []);
PersistentStorage.persistProp<string>(CustomConstants.PRELOAD_STATUS_KEY, "succeed");@Entry
@Component
struct Index {@State currentIndex: number = 0;@State title: ResourceStr = $r('app.string.app_name');childNavStack: NavPathStack = new NavPathStack();@BuildertabHomeContent() {HomeView()}@BuildertabAboutContent() {AboutView()}@BuildernavigationContent() {AtomicServiceTabs({tabContents: [() => {this.tabHomeContent()},() => {this.tabAboutContent()}],tabBarOptionsArray: [new TabBarOptions($r('app.media.house'), '', $r('app.color.grayscale_color'), $r('app.color.title_font_color')),new TabBarOptions($r('app.media.person'), '', $r('app.color.grayscale_color'), $r('app.color.title_font_color'))],tabBarPosition: TabBarPosition.BOTTOM,barBackgroundColor: $r('app.color.primary_color'),barOverlap: true,onTabBarClick: (index: number) => {if (index === 0) {this.title = $r('app.string.app_name');} else {this.title = "关于我们";}}})}@BuilderpageMap(name: string) {}build() {Column() {AtomicServiceNavigation({navigationContent: () => {this.navigationContent();},title: this.title,titleOptions: {backgroundColor: $r('app.color.primary_color'),isBlurEnabled: true},navDestinationBuilder: this.pageMap,navPathStack: this.childNavStack,mode: NavigationMode.Stack})}.width(CustomConstants.PAGE_FULL).height(CustomConstants.PAGE_FULL)}
}

诗词展示实现

HomeView中,我们实现了诗词的展示和翻页功能。以下是诗词展示的实现代码:

import { curves } from "@kit.ArkUI";
import { CustomConstants } from "../common/constants/CustomConstants";
import { IntroDialogView } from "../common/dialog/IntroDialogView";
import { ReadJsonFile } from "../common/utils/ReadJsonFile";
import AlertDialog from '@ohos.promptDialog.AlertDialog';
import { DialogAlignment, BlurStyle } from '@ohos.promptDialog';@Preview
@Component
export struct HomeView {@State viewData: Array<Record<string, any>> = [];@State currentIndex: number = 0;re: RegExp = new RegExp("[。!?]");async aboutToAppear(): Promise<void> {await ReadJsonFile("data.json5").then((data) => {this.viewData = data as Array<Record<string, any>>;});}build() {Column() {Stack() {Scroll() {Flex({ direction: FlexDirection.Row, wrap: FlexWrap.NoWrap }) {Column({ space: CustomConstants.ROW_COLUMN_SPACES[3] }) {Text(this.viewData[this.currentIndex]?.title ?? "").width($r('app.float.size_20')).fontSize($r('app.float.font_size_18')).fontWeight(FontWeight.Bolder).fontColor($r('app.color.title_font_color'))Text(this.viewData[this.currentIndex]?.poet ?? "").width($r('app.float.size_20')).fontSize($r('app.float.font_size_14')).fontWeight(FontWeight.Normal).textAlign(TextAlign.Center).fontColor($r('app.color.start_window_background')).backgroundColor($r('app.color.tag_color')).backgroundBlurStyle(BlurStyle.COMPONENT_ULTRA_THIN).borderRadius($r('app.float.size_16')).padding({top: $r('app.float.size_16'),bottom: $r('app.float.size_16')})}.justifyContent(FlexAlign.Center).padding({left: $r('app.float.size_8'),right: $r('app.float.size_8')})Scroll() {Scroll() {Row({ space: 10 }) {ForEach(this.viewData[this.currentIndex]?.content.split(this.re), (item: string) => {if (item.trim() != "") {Column({ space: CustomConstants.ROW_COLUMN_SPACES[3] }) {Text(item.trim().replace("\n", "").replace("\r", "") + "。").width($r('app.float.size_20')).fontSize($r('app.float.font_size_18')).fontColor($r('app.color.title_font_color')).fontWeight(FontWeight.Medium)}.justifyContent(FlexAlign.Start).padding({left: $r('app.float.size_8'),right: $r('app.float.size_8')})}})}.justifyContent(FlexAlign.Start).alignItems(VerticalAlign.Top).padding({left: $r('app.float.size_8'),right: $r('app.float.size_8')}).transition(this.effect)}.layoutWeight(1).scrollBar(BarState.Off).scrollable(ScrollDirection.Horizontal).edgeEffect(EdgeEffect.Spring)}.layoutWeight(1).scrollBar(BarState.Off).scrollable(ScrollDirection.Vertical).edgeEffect(EdgeEffect.Spring)}.padding($r('app.float.size_20'))}.scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off).width(CustomConstants.PAGE_FULL).height(CustomConstants.PAGE_FULL).borderRadius($r('app.float.size_16')).shadow({radius: $r('app.float.size_10'),color: $r('app.color.primary_color')})Row() {Button() {// 上一页Text() {SymbolSpan($r('sys.symbol.chevron_left')).fontSize($r('app.float.font_size_24')).fontColor([$r('app.color.start_window_background')])}.width($r('app.float.size_81')).height($r('app.float.size_32')).textAlign(TextAlign.Center)}.type(ButtonType.Capsule).backgroundColor($r('app.color.secondary_color')).onClick(() => {this.currentIndex--;if (this.currentIndex < 0) {this.currentIndex = 0;}})// 注释Button() {Text() {SymbolSpan($r('sys.symbol.keyboard_badge_zhuyin')).fontSize($r('app.float.font_size_24')).fontColor([$r('app.color.start_window_background')])}.width($r('app.float.size_48')).height($r('app.float.size_48')).textAlign(TextAlign.Center)}.type(ButtonType.Circle).backgroundColor($r('app.color.primary_color')).onClick(() => {AlertDialog.show({title: this.viewData[this.currentIndex].title + '【释义】',message: '解释:\n' + this.viewData[this.currentIndex].trans + '\n\n赏析:\n' + this.viewData[this.currentIndex].intro + '\n\n注解:\n' + this.viewData[this.currentIndex].annot + '\n\n评价:\n' + this.viewData[this.currentIndex].comment,autoCancel: true,alignment: DialogAlignment.Center,backgroundColor: $r('app.color.primary_color'),backgroundBlurStyle: BlurStyle.NONE})})// 下一页Button() {Text() {SymbolSpan($r('sys.symbol.chevron_right')).fontSize($r('app.float.font_size_24')).fontColor([$r('app.color.start_window_background')])}.width($r('app.float.size_81')).height($r('app.float.size_32')).textAlign(TextAlign.Center)}.type(ButtonType.Capsule).backgroundColor($r('app.color.secondary_color')).onClick(() => {this.currentIndex++;if (this.currentIndex >= this.viewData.length) {this.currentIndex = 0;}})}.width(CustomConstants.PAGE_FULL).height($r('app.float.size_48')).justifyContent(FlexAlign.SpaceEvenly)}.width(CustomConstants.PAGE_FULL).height(CustomConstants.PAGE_FULL).alignContent(Alignment.Bottom)}.width(CustomConstants.PAGE_FULL).height(CustomConstants.PAGE_FULL).backgroundColor($r('app.color.mask_color')).padding({top: $r('app.float.size_12'),right: $r('app.float.size_16'),bottom: $r('app.float.size_64'),left: $r('app.float.size_16')})}// 创建TransitionEffectprivate effect: TransitionEffect =// 创建默认透明度转场效果,并指指定springMotion(0.6, 0.8)曲线TransitionEffect.OPACITY.animation({ curve: curves.springMotion(0.6, 0.8) })// 通过combine方法,这里的动画参数会跟随上面的TransitionEffect,也就是springMotion(0.6, 0.8).combine(TransitionEffect.scale({ x: 0, y: 0 }))// 添加旋转转场效果,这里的动画参数会跟随上面带animation的TransitionEffect,也就是springMotion().combine(TransitionEffect.rotate({ angle: 90 }))// 添加平移转场效果,这里的动画参数使用指定的springMotion().combine(TransitionEffect.translate({ y: 150 }).animation({ curve: curves.springMotion() }))// 添加move转场效果,这里的动画参数会跟随上面的TransitionEffect,也就是springMotion().combine(TransitionEffect.move(TransitionEdge.END));
}

总结

通过上述代码,我们实现了从读取JSON文件到展示诗词内容的全流程。接下来的开发中,我们将进一步优化用户体验,增强应用功能,确保用户能够流畅地欣赏到中国传统文化的瑰宝。

作者介绍

作者:csdn猫哥

原文链接:https://blog.csdn.net/yyz_1987

团队介绍

坚果派团队由坚果等人创建,团队拥有12个华为HDE带领热爱HarmonyOS/OpenHarmony的开发者,以及若干其他领域的三十余位万粉博主运营。专注于分享HarmonyOS/OpenHarmony、ArkUI-X、元服务、仓颉等相关内容,团队成员聚集在北京、上海、南京、深圳、广州、宁夏等地,目前已开发鸿蒙原生应用和三方库60+,欢迎交流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/81482.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux jounery 日志相关问题

/var/log 目录 是 Linux 系统中存放各种日志文件的标准位置。 这些日志文件记录了系统及其服务的运行状态。 日志文件来源 系统日志 由 syslog 或 systemd-journald&#xff08;如果使用 systemd 的话&#xff09;等日志服务生成。记录内核消息和各种系统事件&#xff0c;例如…

JavaWeb学习打卡-Day7-正向代理、反向代理、Nginx

正向代理 概念&#xff1a;正向代理是一个位于客户端和目标服务器之间的代理服务器&#xff08;中间服务器&#xff09;。为了从目标服务器取得内容&#xff0c;客户端向代理服务器发送一个请求&#xff0c;并且指定目标服务器&#xff0c;之后代理向目标服务器转发请求&#…

AI算法可视化:如何用Matplotlib与Seaborn解释模型?

AI算法可视化&#xff1a;如何用Matplotlib与Seaborn解释模型&#xff1f; 系统化学习人工智能网站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目录 AI算法可视化&#xff1a;如何用Matplotlib与Seaborn解释模型&#xff1f;摘要引言基础可…

GoogleTest:TEST_F

GoogleTest:简单示例及ASSERT/EXPECT说明-CSDN博客 介绍了写一个简单的测试用例 如果某些测试用例在开始测试前需要先做一些准备工作,那么如果每次都需要先准备,那么会比较的麻烦,基于这种情况可以使用GoogleTest的TEST_F方法。 简单点说,就是需要先定义一个继承于testin…

【云备份】配置文件加载模块

目录 一.为什么要配置文件 二.配置文件的实现 三.单例文件配置类设计 四.源码 一.为什么要配置文件 我们将服务端程序运行中用到的一些关键信息保存到配置文件中&#xff0c;这样可以使程序的运行更加灵活。 这样做的好处是&#xff0c;未来如果我们想要修改一些关键信息&…

文号验证-同时对两个输入框验证

文号验证-同时对两个输入框验证 效果&#xff1a; 一、如果有多个文号&#xff1a; <div v-for"(item, index) in approvalForm.productApprovalTypeEvents" :key"index"> <el-form-itemlabel"文号":prop"productApprovalTypeEv…

高翔视觉slam中常见的OpenCV和Eigen的几种数据类型的内存布局及分配方式详解

vector<Eigen::Vector2d, Eigen::aligned_allocator<Eigen::Vector2d>> 内存布局及分配方式详解 1. 内存对齐的必要性 Eigen 的固定大小类型(如 Eigen::Vector2d、Eigen::Matrix4d 等)需要 16 字节内存对齐,以支持 SIMD 指令(如 SSE/AVX)的并行计算。若未对…

5G育种技术之植物性状订制

行业展望 我国农作物种业市场规模逐年增长&#xff0c;其中以粮食作物种子市场规模较大。目前我国育种产业发展仍处于初级阶段&#xff0c;存在龙头企业市场占有率和行业集中度不高、企业育种技术和水平落后于发达国家、种企研发投入不足等问题。虽然基因编辑技术的出现有望改…

用户隐私与社交媒体:评估Facebook的保护成效

在这个数字化时代&#xff0c;社交媒体平台&#xff0c;尤其是Facebook&#xff0c;已经成为我们生活中不可或缺的一部分。然而&#xff0c;随着用户隐私问题日益受到关注&#xff0c;社交媒体平台如何处理和保护用户数据成为了公众讨论的焦点。本文将探讨Facebook在用户隐私保…

python实现基于Windows系统计算器程序

Python实现Windows系统计算器程序&#xff08;含高级功能&#xff09; 下面我将介绍如何使用Python创建一个功能完整的Windows风格计算器程序&#xff0c;包含基本运算和高级数学功能。 1. 使用Tkinter实现基础计算器 import tkinter as tk from tkinter import ttk import …

Vue 3 响应式 API 详解与实战案例

Vue 3 引入了全新的响应式系统&#xff0c;主要通过 ref 和 reactive 这两个 API 来实现。下面我将通过具体代码示例详细讲解它们的用法和区别。 1. ref - 基础响应式 API ref 用于创建响应式的基本类型数据&#xff08;如字符串、数字、布尔值&#xff09;&#xff0c;也可以…

软件第三方测试:关键部分、意义、流程及方法全解析?

软件第三方测试是保障软件质量的关键部分&#xff0c;它由专业的机构来开展&#xff0c;这个机构不隶属于开发方和使用方&#xff0c;能以客观公正的视角找出软件问题。 测试意义 软件第三方测试意义重大&#xff0c;它依靠专业技术&#xff0c;依照严格流程&#xff0c;对软…

WPF TextBlock控件性能优化指南

WPF TextBlock控件性能优化指南 1. 引言 TextBlock作为WPF中最基础且使用最广泛的文本显示控件&#xff0c;其性能优化对整个应用程序的响应速度和资源占用有着重要影响。尽管TextBlock是一个轻量级控件&#xff0c;但在大型应用或需要显示大量文本的场景中&#xff0c;不恰当…

【Linux】关于虚拟机

一些在Linux驱动开发中使用虚拟机的经验。 部分图片和经验来源于网络&#xff0c;若有侵权麻烦联系我删除&#xff0c;主要是做笔记的时候忘记写来源了&#xff0c;做完笔记很久才写博客。 专栏目录&#xff1a;记录自己的嵌入式学习之路-CSDN博客 目录 1 VirtualBox使用技…

AimRT从入门到精通 - 04RPC客户端和服务器

一、ROS中的service通信机制 服务通信也是ROS中一种极其常用的通信模式&#xff0c;服务通信是基于请求响应模式的&#xff0c;是一种应答机制。也即&#xff1a;一个节点A向另一个节点B发送请求&#xff0c;B接收处理请求并产生响应结果返回给A。比如如下场景&#xff1a; 机器…

普通IT的股票交易成长史--20250502 突破(1)

声明&#xff1a;本文章的内容只是自己学习的总结&#xff0c;不构成投资建议。文中观点基本来自yt站方方土priceaction&#xff0c;综合自己的观点得出。感谢他们的无私分享。 送给自己的话&#xff1a; 仓位就是生命&#xff0c;绝对不能满仓&#xff01;&#xff01;&#…

《操作系统真象还原》调试总结篇

文章目录 前言第11章调试我们操作系统目前的内存管理现状 前言 上一章结尾调试还没有完成&#xff0c;本章开始前需要先完成上一章代码的调试。 总的来说&#xff0c;我们的操作系统目前有三大块内容&#xff1a;线程-进程内容、内存管理内容、中断内容。当然这三部分肯定不可…

【Machine Learning Q and AI 读书笔记】- 01 嵌入、潜空间和表征

Machine Learning Q and AI 中文译名 大模型技术30讲&#xff0c;主要总结了大模型相关的技术要点&#xff0c;结合学术和工程化&#xff0c;对LLM从业者来说&#xff0c;是一份非常好的学习实践技术地图. 本文是Machine Learning Q and AI 读书笔记的第1篇&#xff0c;对应原…

Flutter 学习之旅 之 Flutter 和 Android 原生 实现数据交互的MethodChanel和EventChannel方式的简单整理

Flutter 学习之旅 之 Flutter 和 Android 原生 实现数据交互的MethodChanel和EventChannel方式的简单整理 目录 Flutter 学习之旅 之 Flutter 和 Android 原生 实现数据交互的MethodChanel和EventChannel方式的简单整理 一、简单介绍 二、Flutter 和 Android 原生之间的数据…

outlook for mac本地邮件存放在哪儿?

尽管 PST 格式通常与 Microsoft Outlook 联系在一起&#xff0c;但认为它也在 Mac OS 上存储邮箱数据是一种误解。实际上&#xff0c;Outlook for Mac 不会将邮件存储为 PST 文件。无法在 Outlook for Mac 中找到 PST 文件位置&#xff0c;因为它不使用 PST 文件来存储邮箱数据…