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

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

项目目标

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

功能特性

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

诗词展示

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

对话框交互

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

页面导航

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

数据内容

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

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

实现概述

读取JSON文件

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

typescript 复制代码
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组件实现页签切换,并展示诗词内容。以下是首页的实现代码:

typescript 复制代码
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中,我们实现了诗词的展示和翻页功能。以下是诗词展示的实现代码:

typescript 复制代码
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+,欢迎交流。

相关推荐
云和数据.ChenGuang8 小时前
鸿蒙版电影app设计开发
华为·harmonyos·鸿蒙·鸿蒙系统
layneyao9 小时前
自动驾驶L4级技术落地:特斯拉、Waymo与华为的路线之争
人工智能·华为·自动驾驶
Bruce_Liuxiaowei10 小时前
HarmonyOS Next~鸿蒙系统UI创新实践:原生精致理念下的设计革命
ui·华为·harmonyos
SuperHeroWu715 小时前
【HarmonyOS 5】鸿蒙检测系统完整性
华为·harmonyos·模拟器·系统完整性·越狱设备
京东云开发者15 小时前
Taro on Harmony :助力业务高效开发纯血鸿蒙应用
harmonyos
前端付豪16 小时前
2、ArkTS 是什么?鸿蒙最强开发语言语法全讲解(附实操案例)
前端·后端·harmonyos
zhujiaming16 小时前
鸿蒙端应用适配使用开源flutter值得注意的一些问题
前端·flutter·harmonyos
前端付豪16 小时前
8、鸿蒙动画开发实战:做一个会跳舞的按钮!(附动效示意图)
前端·后端·harmonyos
前端付豪16 小时前
3、构建你的第一个鸿蒙组件化 UI 页面:实现可复用的卡片组件(附实战代码)
前端·后端·harmonyos