鸿蒙 HarmonyOS 6 | PDFKit预览能力升级实战

前言

PDF 预览在办公、教育、阅读类应用里一直是高频需求。鸿蒙这条能力线本身分成两层,一层是 pdfService,负责文档加载、编辑、保存这类底层操作;另一层是 PdfView,负责预览、搜索、批注这类交互。到了 API 20,这条链路里更值得关注的点,集中在 PdfController 的加载回调、页数监听,以及 ArkWeb 里针对 PDF 预览补进来的两个事件上。 PdfView 从 5.0.0(12) 开始支持,registerPageCountChangedListener 是 6.0.0(20) Beta3 新增能力,ArkWeb 的 onPdfLoadEventonPdfScrollAtBottom 也在 6.0.0(20) Beta3 进入支持范围。

这次升级最实用的地方,不在功能堆得更多,在于预览链路终于有了更清晰的状态信号。文档什么时候开始进入可用状态,总页数什么时候拿到,Web 预览里的 PDF 有没有加载成功,用户是不是已经滚到最底部,这些都可以挂到明确回调上去做。

一、梳理 PdfView

PdfView 适合承接本地 PDF 预览、页码跳转、页面适配、滚动预览、关键字搜索和批注这类能力。项目里如果主要目标是做应用内 PDF 阅读器,优先走 PdfView 会更顺。页面这边通常先准备一个 PdfController,再把它交给 PdfView。文档预览和搜索、批注这些能力,也都是围绕 PdfController 展开的。

更好的接法,是在页面显示前先把文件准备好,再开始加载。loadDocument 的签名支持 path、可选密码、初始页码和进度回调,返回值是 Promise<ParseResult>。也就是说,文档能不能打开、是不是密码错误、是不是解析失败,最终都能通过 ParseResult 收敛。

下面这段代码适合作为本地 PDF 预览的最小可用骨架:

typescript 复制代码
import { pdfService, pdfViewManager } from '@kit.PDFKit'
import { fileIo } from '@kit.CoreFileKit'
import type common from '@ohos.app.ability.common'
import hilog from '@ohos.hilog'

@Entry
@Component
struct PdfPreviewPage {
  private controller: pdfViewManager.PdfController = new pdfViewManager.PdfController()
  @State pageCount: number = 0
  @State isLoading: boolean = true

  async aboutToAppear(): Promise<void> {
    const context = this.getUIContext().getHostContext() as common.UIAbilityContext
    const filePath = `${context.filesDir}/input.pdf`

    // 首次进入时,把 rawfile 里的 PDF 拷到应用沙箱
    if (!fileIo.accessSync(filePath)) {
      const content: Uint8Array = context.resourceManager.getRawFileContentSync('rawfile/input.pdf')
      const file = fileIo.openSync(filePath, fileIo.OpenMode.WRITE_ONLY |
        fileIo.OpenMode.CREATE | fileIo.OpenMode.TRUNC)
      fileIo.writeSync(file.fd, content.buffer)
      fileIo.closeSync(file.fd)
    }

    // API 20 新增,文档加载前就可以注册页数变化监听
    this.controller.registerPageCountChangedListener((count: number) => {
      this.pageCount = count
      hilog.info(0x0000, 'PdfPreview', `PDF总页数: ${count}`)
    })

    const result = await this.controller.loadDocument(filePath, '', 0, (progress: number) => {
      hilog.info(0x0000, 'PdfPreview', `加载进度: ${progress}`)
    })

    this.isLoading = false

    if (result !== pdfService.ParseResult.PARSE_SUCCESS) {
      hilog.error(0x0000, 'PdfPreview', `文档加载失败: ${result}`)
    }
  }

  aboutToDisappear(): void {
    this.controller.releaseDocument()
  }

  build() {
    Stack() {
      if (!this.isLoading) {
        PdfView({
          controller: this.controller,
          pageFit: pdfService.PageFit.FIT_WIDTH,
          showScroll: true
        })
        .width('100%')
        .height('100%')
      } else {
        LoadingProgress()
          .width(48)
          .height(48)
      }
    }
    .width('100%')
    .height('100%')
  }
}

这段代码里最重要的点有三个。第一,PDF 文件最好先落到应用沙箱里,再交给控制器加载。第二,页数监听放在 loadDocument 之前注册,这样总页数一出来就能接住。第三,页面退出时记得 releaseDocument(),文档实例不要一直挂在内存里。loadDocument 的参数形态和页数监听接口,已经都在 PDFKit 的控制器接口里补齐了。

二、PdfView 适合做阅读器,ArkWeb 更适合做在线预览

API 20 这一轮容易被混淆的一点,是有些回调属于 PdfView,有些回调属于 ArkWeb。registerPageCountChangedListenerPdfController 的能力,适合放在应用内阅读器。onPdfLoadEventonPdfScrollAtBottom 则是 ArkWeb 这条线补进来的 PDF 事件,适合放在 Web 组件加载 PDF 的场景里。两条链路都能做 PDF 预览,但适用场景不一样。

ArkWeb 这边的好处很直接。你如果本来就是基于 Web 组件去展示在线文档,或者 PDF 本身就来自远端地址,这两个事件会很好用。onPdfLoadEvent 可以直接拿到加载结果,onPdfScrollAtBottom 则特别适合做阅读完成、已读确认、加载更多推荐内容这类逻辑。ArkWeb 的事件定义页里已经把这两个事件列出来了,API 变更清单里也明确标出了它们是 6.0.0(20) Beta3 新增能力。

下面这段代码适合直接做 Web 侧 PDF 预览的起步版本:

typescript 复制代码
import { webview } from '@kit.ArkWeb'
import hilog from '@ohos.hilog'

@Entry
@Component
struct WebPdfPage {
  controller: webview.WebviewController = new webview.WebviewController()

  build() {
    Web({
      src: 'https://example.com/document.pdf#page=3&zoom=200&toolbar=0&pdfbackgroundcolor=ffffff',
      controller: this.controller
    })
      .domStorageAccess(true)
      .onPdfLoadEvent((eventInfo) => {
        hilog.info(
          0x0000,
          'WebPdfPage',
          `PDF加载事件, url=${eventInfo.url}, result=${eventInfo.result}`
        )
      })
      .onPdfScrollAtBottom((eventInfo) => {
        hilog.info(
          0x0000,
          'WebPdfPage',
          `PDF已滚动到底部, url=${eventInfo.url}`
        )
        // 这里可以做"已读完成"或推荐内容加载
      })
  }
}

这里有两个小点比较实用。第一,Web 组件支持通过 URL 参数直接控制 PDF 的初始状态,比如页码、缩放比例、工具栏显隐和背景色。第二,这条链路更适合在线预览、站内阅读页和混合内容页面,尤其是你本来已经在用 Web 承接内容展示时,这套回调会省很多事。

三、文档切换和加载反馈实践

PDF 预览这类页面有一个很常见的问题,文档一切换,界面就会闪一下。原因通常不复杂,旧文档刚释放,新文档还没真正加载出来,中间那段时间如果页面没有任何反馈,用户就会明显感知到断层。更稳的处理方式,是把加载态单独收出来,用状态变量控制 PdfView 的挂载时机。这样切文档时,页面至少会是一个稳定的 loading 过程。

这一类写法可以直接落成下面这样:

typescript 复制代码
@State isLoading: boolean = false
@State currentFilePath: string = ''

async switchDocument(filePath: string): Promise<void> {
  this.controller.releaseDocument()
  this.isLoading = true

  const result = await this.controller.loadDocument(filePath)

  this.isLoading = false

  if (result === pdfService.ParseResult.PARSE_SUCCESS) {
    this.currentFilePath = filePath
  } else {
    hilog.error(0x0000, 'PdfPreview', `切换文档失败: ${result}`)
  }
}

build() {
  Stack() {
    if (!this.isLoading) {
      PdfView({
        controller: this.controller,
        pageFit: pdfService.PageFit.FIT_WIDTH,
        showScroll: true
      })
      .width('100%')
      .height('100%')
    } else {
      LoadingProgress()
        .width(52)
        .height(52)
    }
  }
  .width('100%')
  .height('100%')
}

这类写法解决的是一个很实际的问题,用户不需要知道底层是不是在重新解析 PDF,只要看到页面始终是稳定反馈,体验就会顺很多。加载成功和失败也都有明确状态点,后面接 Toast、错误页或者重试逻辑都会方便。loadDocument 返回 ParseResult 这一点,本身就适合拿来做这层状态管理。

四、几个容易踩的坑,提前避开

第一个坑,是把 PdfView 和 ArkWeb 的 PDF 事件混成一套。registerPageCountChangedListener 属于 PdfControlleronPdfLoadEventonPdfScrollAtBottom 属于 Web 组件。两者可以都做 PDF 预览,但不该混着写。阅读器场景优先走 PdfView,在线文档或网页承载场景更适合走 Web。

第二个坑,是忽略加密文档。loadDocument 支持密码参数,密码错误和解析错误不属于一类结果。业务层最好把这两种情况分开处理,密码错了可以继续提示用户输入,解析失败就该走错误提示或回退逻辑。loadDocument(path, password?, initPageIndex?, onProgress?) 这套签名,已经把密码和进度回调都放进来了。

第三个坑,是页面退出时忘记释放文档。PDF 文档本身可能比较大,长时间挂着控制器实例不释放,内存占用会越来越难看。文档切换、页面关闭、阅读器退出这些时机,最好都把 releaseDocument() 放进去。这个问题看起来像细节,项目一大就很容易出事。

总结

鸿蒙 6 API 20 这一轮在 PDF 预览链路上做的升级,最有价值的地方就是把状态信号补全了。PdfController 侧有 loadDocument 的加载结果和页数监听,ArkWeb 侧补进了 onPdfLoadEventonPdfScrollAtBottom。只要把这几处回调接顺,文档加载反馈、总页数展示、在线预览、已读判断这些能力就都能挂到明确的节点上去做。

阅读器优先走 PdfView,在线 PDF 优先走 Web,文档切换时单独做 loading 状态,退出时及时释放资源。

相关推荐
花先锋队长3 小时前
鸿蒙6.1加持菜鸟App:地理围栏+实况窗,靠近驿站自动提醒,取件不再遗漏
华为·智能手机·harmonyos
nashane3 小时前
HarmonyOS 6学习:页面跳转弹窗状态保持全解析
学习·华为·harmonyos·harmonyos 5
maaath3 小时前
【maaath】Flutter for OpenHarmony 实战:电影榜单应用开发指南
flutter·华为·harmonyos
若兰幽竹5 小时前
【HarmonyOS 6.1 全场景实战】开篇词:打造消除“吃饭焦虑”的《灵犀厨房》
harmonyos·鸿蒙开发·华为鸿蒙系统
机构师5 小时前
<鸿蒙><APP><3D>鸿蒙3D开发,如何获取ktx格式的天空盒图?
华为·harmonyos
xmdy58666 小时前
Flutter+开源鸿蒙实战|智安盾电商溯源平台Day6 登录逻辑+积分体系+全局收尾优化
flutter·华为·harmonyos
前端不太难6 小时前
AISystem:鸿蒙游戏中的 AI 行为驱动
人工智能·游戏·harmonyos
xmdy58668 小时前
Flutter+开源鸿蒙实战|智联邻里Day1 项目搭建+环境适配+架构规划(十五五民生创新版)
flutter·开源·harmonyos
maaath8 小时前
【maaath】Flutter for OpenHarmony 音乐播放器应用实战开发
flutter·华为·harmonyos