鸿蒙学习实战之路-Reader Kit获取目录列表最佳实践

最近好多朋友问我:“西兰花啊,我用Reader Kit做了个阅读器,能显示书籍信息了,可怎么才能显示目录让用户跳转章节啊?” 害,这问题可问对人了!咱们做阅读器,目录功能就像地图一样重要,没有它用户就像在迷宫里瞎转~

今天这篇,我就手把手带你用Reader Kit获取目录列表,教你怎么显示目录并实现章节跳转,全程不超过10分钟(不含下载时间)~


一、获取目录列表的基本流程

咱们先来看个流程图,了解一下整个过程是咋回事:

在这里插入图片描述

简单来说,整个流程就像咱们看地图找路:

  1. 先初始化书籍解析器(就像打开地图)
  2. 获取目录列表(就像查看地图上的各个地点)
  3. 展示目录(就像把地图摊开给用户看)
  4. 用户点击目录,获取跳转信息(就像找到目的地的路线)
  5. 跳转到指定章节(就像按照路线到达目的地)

二、需要用到的接口

咱们做这个功能,主要需要用到4个接口,就像旅行需要用到地图、指南针、导航仪一样:

接口名 描述
getDefaultHandler 获取书籍默认解析器,相当于打开地图的工具
getCatalogList 获取书籍目录列表,相当于地图上的各个地点标记
getDomPosByCatalogHref 获取阅读起始位置domPos,相当于目的地的具体坐标
getSpineList 获取书脊内容列表,相当于地图上的主要道路

三、具体实现步骤

1. 导入相关模块

首先,咱们得把需要的模块导入进来,就像旅行前先把所有装备都收拾好一样:

import { common } from "@kit.AbilityKit";
import { bookParser } from "@kit.ReaderKit";
import { hilog } from "@kit.PerformanceAnalysisKit";

2. 初始化书籍解析器

接下来,咱们需要初始化书籍解析器,就像旅行前先打开地图确认位置一样:

private defaultHandler: bookParser.BookParserHandler | null = null;
aboutToAppear(): void {
  this.init().then(() => {
  });
}
private async init() {
  let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
  let path: string = `${context.filesDir}/abc.epub`; // 这里的路径是书籍在沙箱中的位置
  try {
    this.defaultHandler = await bookParser.getDefaultHandler(path);
  } catch (error) {
    hilog.error(0x0000, "testTAG", `getDefaultHandler failed, Code: ${error.code}, message: ${error.message}`);
  }
}

3. 获取并展示目录列表

现在,咱们就可以获取目录列表并展示出来了,就像把地图摊开给用户看一样:

@State catalogItemList: bookParser.CatalogItem[] = [];
aboutToAppear(): void {
  this.init().then(() => {
    this.getCatalogList();
  });
}
private getCatalogList() {
  try {
    this.catalogItemList = this.defaultHandler?.getCatalogList() || [];
  } catch (error) {
    hilog.error(0x0000, "testTAG", `getCatalogList failed, Code: ${error.code}, message: ${error.message}`);
  }
}
build() {
  Column() {
    List() {
      ForEach(this.catalogItemList, (item: bookParser.CatalogItem) => {
        ListItem() {
          Column() {
            Row() {
              Row() {
                Text(' · ')
                  .fontSize(14)
                Text(item.catalogName)
                  .fontSize(14)
                  .textOverflow({ overflow: TextOverflow.Ellipsis })
                  .padding({ top: 8, bottom: 8 })
                  .maxLines(2)
                  .layoutWeight(1)
              }
            }
            .width('100%')
            .height(48)
            .justifyContent(FlexAlign.Center)
            .alignItems(VerticalAlign.Center)
            Divider()
          }
          .padding({
            left: item.catalogLevel ? item.catalogLevel * 26 : 10,
            right: item.catalogLevel ? item.catalogLevel * 26 : 10,
            top: 6,
            bottom: 6
          })
          .onClick(async () => {
            // 点击目录跳转到指定章节
            this.jumpToCatalogItem(item);
          })
        }
      })
    }
    .scrollBar(BarState.Off)
    .width('100%')
    .height('100%')
  }
  .width('100%')
  .height('100%')
}

4. 实现章节跳转功能

最后,咱们需要实现章节跳转功能,就像按照地图上的路线到达目的地一样:

private jumpToCatalogItem(catalogItem: bookParser.CatalogItem) {
  let domPos = this.getDomPos(catalogItem);
  let resourceIndex = this.getResourceItemByCatalog(catalogItem).index;
  // 通过domPos及resourceIndex信息,即可通过startPlay接口跳转到指定位置
  hilog.info(0x0000, "testTAG", `jumpToCatalogItem domPos: ${domPos}, resourceIndex: ${resourceIndex}`);
}
private getDomPos(catalogItem: bookParser.CatalogItem): string {
  try {
    let domPos: string = this.defaultHandler?.getDomPosByCatalogHref(catalogItem.href || '') || '';
    return domPos;
  } catch (error) {
    hilog.error(0x0000, "testTAG", `getDomPos failed, Code: ${error.code}, message: ${error.message}`);
  }
  return '';
}
/**
 * 获取书籍目录对应的资源条目
 *
 * @param catalogItem 目录条目
 */
private getResourceItemByCatalog(catalogItem: bookParser.CatalogItem): bookParser.SpineItem {
  let resourceFile = catalogItem.resourceFile || '';
  try {
    let spineList: bookParser.SpineItem[] = this.defaultHandler?.getSpineList() || []
    // 查找目录对应的资源条目
    let resourceItemArr = spineList.filter(item => item.href === resourceFile);
    if (resourceItemArr.length > 0) {
      hilog.info(0x0000, 'testTag', 'getResourceItemByCatalog get resource ', resourceItemArr[0]);
      let resourceItem = resourceItemArr[0];
      return resourceItem;
    } else if (spineList.length > 0) {
      // 如果查找不到,则默认返回第1个资源条目
      hilog.info(0x0000, 'testTag', 'getResourceItemByCatalog get resource in resourceList', spineList[0]);
      return spineList[0];
    }
  } catch (error) {
    hilog.error(0x0000, "testTAG", `getDomPos failed, Code: ${error.code}, message: ${error.message}`);
  }
  // 如果没有资源条目,则返回默认值
  hilog.info(0x0000, 'testTag', 'getResourceItemByCatalog get resource in escape');
  return {
    idRef: '',
    index: 0,
    href: '',
    properties: ''
  };
}

四、需要注意的地方

🥦 西兰花警告

  1. 书籍文件必须在沙箱目录:Reader Kit只能读取应用沙箱目录下的书籍文件,就像地图只能在特定区域使用一样,出了范围就不管用了~

  2. 要处理异常情况:不同格式的电子书可能会有不同的问题,比如有些书籍可能没有目录,咱们得做好异常捕获,就像旅行时要准备好应对突发情况一样~

  3. 目录层级的处理:代码中通过item.catalogLevel来设置缩进,这样可以让目录显示出层级关系,就像地图上的道路一样有主次之分~

🥦 西兰花小贴士

  • 跳转的小技巧:获取到domPosresourceIndex后,咱们可以用ReadPageComponentstartPlay接口来实现跳转,就像导航仪告诉咱们具体怎么走一样~

  • 支持的格式:Reader Kit支持txt、epub、mobi、azw、azw3格式的电子书,不过不同格式的目录结构可能会有差异,咱们在开发时要多测试几种格式~


五、下一步行动

现在咱们已经学会了如何获取目录列表并实现章节跳转,接下来可以试试:

  1. 实现更美观的目录界面,比如添加折叠/展开功能
  2. 实现阅读进度同步,让目录显示当前阅读位置
  3. 实现目录搜索功能,让用户可以快速找到想看的章节

📚 推荐资料


我是盐焗西兰花,
不教理论,只给你能跑的代码和避坑指南。
下期见!🥦

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐