HarmonyOS NEXT 诗词元服务项目开发上架全流程实战(一、项目介绍及实现效果)
在当今数字化时代,如何让传统文化与现代科技相结合,成为了一个值得思考的问题。诗词作为中国传统文化的重要组成部分,承载着丰富的历史信息和文化内涵。为了让更多人了解和欣赏诗词的魅力,我们决定开发一款基于HarmonyOS NEXT的诗词元服务应用。这款应用不仅能够提供诗词的欣赏功能,还能通过现代化的交互方式,让传统诗词焕发出新的生命力。
项目目标
目标是打造一个精神的栖息地,为文人墨客和诗词爱好者提供一个互动交流的平台。同时,我们希望通过这个项目,让更多人能快速上述鸿蒙应用开发,不要说你不会,更不要说难。直接从实战上手,直接看到学习成果,获得成就感。完整展示从开发到打包,从签名上架的全套流程。同时能够欣赏到中国传统文化的精髓,体验诗词带来的美好感受。在开发过程中,我们将注重用户体验,确保用户能够便捷地翻阅诗词,以及获取诗词的详细信息。

功能特性
- 诗词展示:展示诗词的标题、作者、内容,支持翻页操作和查看诗词注释。
- 对话框交互:包含诗词解说对话框和建议与反馈对话框,提升用户交互体验。
- 页面导航:可通过 AtomicServiceTabs 组件在首页和关于页面间切换。
- 个人中心:展示作者及个人信息。
诗词展示
诗词展示功能是应用的核心之一,将展示诗词的标题、作者和正文内容。用户可以通过翻页操作来浏览不同的诗词。此外,我们还为每首诗词提供了详细的注释,包括词句的解释、背景介绍和作者的注解,帮助用户更好地理解诗词。
对话框交互
我们加入了诗词解说对话框和建议与反馈对话框,以提升用户的交互体验。诗词解说对话框可以显示诗词的详细解释、背景和注解,而建议与反馈对话框则让用户可以向我们提出建议或反馈意见,帮助我们不断改进应用。 
页面导航
我们使用了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();
  @Builder
  tabHomeContent() {
    HomeView()
  }
  @Builder
  tabAboutContent() {
    AboutView()
  }
  @Builder
  navigationContent() {
    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 = "关于我们";
        }
      }
    })
  }
  @Builder
  pageMap(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')
    })
  }
  // 创建TransitionEffect
  private 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+,欢迎交流。
- 4回答
- 7粉丝
- 5关注
- HarmonyOS NEXT 诗词元服务项目开发上架全流程实战(二、元服务与应用APP签名打包步骤详解)
- 从0到1上架一个元服务的全流程
- 鸿蒙元服务项目实战:备忘录实现列表展示
- 鸿蒙元服务项目实战:备忘录UI页面开发
- 鸿蒙元服务项目实战:备忘录内容编辑开发
- 元服务——介绍及环境搭建
- 鸿蒙元服务项目实战:终结篇之备忘录搜索功能实现
- HarmonyOS NEXT 实战之元服务:静态多案例效果(一)
- HarmonyOS 【诗韵悠然】AI古诗词赏析APP开发实战从零到一系列(一、开篇,项目介绍)
- 创建元服务项目
- HarmonyOS NEXT实战:元服务的创建、实现多个案例效果
- 鸿蒙开发:如何上架一个元服务应用
- 鸿蒙原生开发手记:03-元服务开发全流程(开发元服务,只需要看这一篇文章)
- HarmonyOS NEXT 实战之元服务:静态案例效果---教育培训服务
- HarmonyOS NEXT 实战之元服务:静态案例效果(二)

