Skip to content
ComPDF
Guides

自定义UI

除核心功能模块外,ComPDF SDK 还提供了 ComPDFKit_Tools 模块。该模块内置完整的 UI 组件与交互逻辑,可在核心能力基础上快速集成,并支持灵活扩展与定制。

此外,我们还提供了示例应用 PDFViewer,用于演示 ComPDFKit_Tools 的集成与使用方式,供开发者参考。

本章节将从以下几个方面介绍 ComPDFKit_Tools

  1. 概述:模块功能与 UI 示例说明
  2. 集成:如何快速集成并使用 ComPDFKit_Tools
  3. 自定义:如何进行 UI 与功能定制

概述

ComPDFKit_Tools 模块集成了常见的 PDF 浏览与编辑能力,包括但不限于:

  • 查看器(Viewer)
  • 注释(Annotations)
  • 表单(Forms)
  • 内容编辑(Content Editor)
  • 页面编辑(Page Editor)
  • 数字签名(Signatures)
  • 安全与水印(Security & Watermark)

通过 CPDFDocumentActivityCPDFDocumentFragment,即可快速在项目中启动完整的 PDF 文档界面。 UI 行为及功能模块可通过配置文件进行控制;如需更深度的定制,也可直接使用 ComPDFKit_Tools 模块源码进行二次开发。

集成 SDK

SDK 支持多种集成方式,您可根据项目需求选择合适的方案。

版本与兼容性(建议)

  • 与核心模块保持一致:compdfkit-tools 版本需与 compdfkitcompdfkit-ui 保持一致。
  • Android 版本:请根据 SDK 发布说明选择合适的 minSdk/compileSdk/targetSdk 与 AGP 版本。
  • R8/ProGuard:若开启混淆,请参考 SDK 随附的混淆配置。

Gradle 集成

  1. 打开位于项目根目录中的 "settings.gradle" 文件,然后添加 mavenCentral 存储库:
diff
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
+       mavenCentral()
    }
}
  1. 在应用程序模块目录中打开 "build.gradle" 文件:
Gradle 配置示意

编辑它并添加 ComPDFKit_Tools 模块依赖项:

groovy
def compdfVersion = "2.6.0" // 与 compdfkit / compdfkit-ui 版本保持一致

dependencies {
  implementation "com.compdf:compdfkit-tools:compdfVersion"
  // 或分别依赖核心与 UI 模块(版本需一致):
  // implementation "com.compdf:compdfkit:compdfVersion"
  // implementation "com.compdf:compdfkit-ui:compdfVersion"
}

手动集成

  1. "ComPDFKit-Tools.aar" 复制到 app 模块的 "libs" 目录中。
AAR 放置于 app/libs 目录
  1. 将以下代码添加到 app 目录下的 "build.gradle" 文件中:
groovy
...
dependencies {
  /* ComPDF SDK */
  implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
  ...
}
...

请务必按照手动集成方式同时依赖 ComPDFKit.aar、ComPDFKit-UI.aar,确保正常运行。

源码集成

可通过 SDK 安装包或 GitHub 示例项目 获取 ComPDFKit_Tools 模块源码。

  1. 下载或解压 SDK 源码
  2. 在 Android Studio 中通过 File → New → Import Module 导入模块
Import Module 示意
  1. app/build.gradle 中添加:
groovy
dependencies {
    implementation project(path: ':ComPDFKit_Tools')
}

使用

完成模块集成后,可通过 CPDFDocumentActivityCPDFDocumentFragment 启动文档界面。 UI 与功能配置通过 CPDFConfiguration 控制,默认配置文件为 tools_default_configuration.json

提示(Android 10/11+ 存储访问)

  • 推荐优先通过 Uri(SAF/MediaStore)方式打开与保存文档;直接使用文件路径在新系统上可能受分区存储(Scoped Storage)限制。
  • tools_default_configuration.json 默认已打包在 Tools 模块 assets 中,可直接使用或复制到应用 assets 目录覆盖默认配置。

最小可用示例(通过 Uri 打开,Activity 方式):

java
Intent intent = new Intent(context, CPDFDocumentActivity.class);
intent.setData(uri); // 可为 SAF 选择器返回的 Uri
intent.putExtra(CPDFDocumentActivity.EXTRA_FILE_PASSWORD, password); // 可为空
CPDFConfiguration configuration = CPDFConfigurationUtils.normalConfig(context, "tools_default_configuration.json");
intent.putExtra(CPDFDocumentActivity.EXTRA_CONFIGURATION, configuration);
intent.putExtra(CPDFDocumentActivity.EXTRA_PAGE_INDEX, 0);
context.startActivity(intent);
kotlin
val intent = Intent(context, CPDFDocumentActivity::class.java).apply {
    data = uri // 可为 SAF 选择器返回的 Uri
    putExtra(CPDFDocumentActivity.EXTRA_FILE_PASSWORD, password) // 可为空
    val configuration = CPDFConfigurationUtils.normalConfig(context, "tools_default_configuration.json")
    putExtra(CPDFDocumentActivity.EXTRA_CONFIGURATION, configuration)
    putExtra(CPDFDocumentActivity.EXTRA_PAGE_INDEX, 0)
}
context.startActivity(intent)
  1. 打开 Uri 路径 PDF 文件
java
// CPDFDocumentActivity
Intent intent = new Intent(context, CPDFDocumentActivity.class);
intent.setData(uri);
// 密码可为空
intent.putExtra(CPDFDocumentActivity.EXTRA_FILE_PASSWORD, password);
// tools_default_configuration.json文件已包含在Tools模块assets目录中
CPDFConfiguration configuration = CPDFConfigurationUtils.normalConfig(context, "tools_default_configuration.json");
intent.putExtra(CPDFDocumentActivity.EXTRA_CONFIGURATION, configuration);
// 打开界面时默认展示的页码
intent.putExtra(CPDFDocumentActivity.EXTRA_PAGE_INDEX, 0);
context.startActivity(intent);

// CPDFDocumentFragment
CPDFConfiguration configuration = CPDFConfigurationUtils.normalConfig(context, "tools_default_configuration.json");
CPDFDocumentFragment documentFragment = CPDFDocumentFragment.newInstance(uri, "password", configuration);
getSupportFragmentManager()
  .beginTransaction()
  .replace(R.id.fragment_container_view, documentFragment, "documentFragment")
  .commit();
kotlin
// CPDFDocumentActivity
val intent = Intent(context, CPDFDocumentActivity::class.java).apply {
    data = uri // 可为 SAF 选择器返回的 Uri
    putExtra(CPDFDocumentActivity.EXTRA_FILE_PASSWORD, password) // 密码可为空
    // tools_default_configuration.json 文件已包含在 Tools 模块 assets 目录中
    val configuration = CPDFConfigurationUtils.normalConfig(context, "tools_default_configuration.json")
    putExtra(CPDFDocumentActivity.EXTRA_CONFIGURATION, configuration)
    putExtra(CPDFDocumentActivity.EXTRA_PAGE_INDEX, 0) // 打开界面时默认展示的页码
}
context.startActivity(intent)

// CPDFDocumentFragment
val configuration = CPDFConfigurationUtils.normalConfig(context, "tools_default_configuration.json")
val documentFragment = CPDFDocumentFragment.newInstance(uri, "password", configuration)
supportFragmentManager
    .beginTransaction()
    .replace(R.id.fragment_container_view, documentFragment, "documentFragment")
    .commit()
  1. 打开 File 文件路径 PDF 文件(注意范围存储限制)
java
// CPDFDocumentActivity
Intent intent = new Intent(context, CPDFDocumentActivity.class);
intent.putExtra(CPDFDocumentActivity.EXTRA_FILE_PATH, filePath);
// 密码可为空
intent.putExtra(CPDFDocumentActivity.EXTRA_FILE_PASSWORD, password);
// tools_default_configuration.json文件已包含在Tools模块assets目录中
CPDFConfiguration configuration = CPDFConfigurationUtils.normalConfig(context, "tools_default_configuration.json");
intent.putExtra(CPDFDocumentActivity.EXTRA_CONFIGURATION, configuration);
// 打开界面时默认展示的页码
intent.putExtra(CPDFDocumentActivity.EXTRA_PAGE_INDEX, 0);
context.startActivity(intent);

// CPDFDocumentFragment
CPDFConfiguration configuration = CPDFConfigurationUtils.normalConfig(context, "tools_default_configuration.json");
CPDFDocumentFragment documentFragment = CPDFDocumentFragment.newInstance(filePath, password, configuration);
getSupportFragmentManager()
  .beginTransaction()
  .replace(R.id.fragment_container_view, documentFragment, "documentFragment")
  .commit();
kotlin
// CPDFDocumentActivity
val intent = Intent(context, CPDFDocumentActivity::class.java).apply {
    putExtra(CPDFDocumentActivity.EXTRA_FILE_PATH, filePath) // 文件路径
    putExtra(CPDFDocumentActivity.EXTRA_FILE_PASSWORD, password) // 密码可为空
    // tools_default_configuration.json 文件已包含在 Tools 模块 assets 目录中
    val configuration = CPDFConfigurationUtils.normalConfig(context, "tools_default_configuration.json")
    putExtra(CPDFDocumentActivity.EXTRA_CONFIGURATION, configuration)
    putExtra(CPDFDocumentActivity.EXTRA_PAGE_INDEX, 0) // 打开界面时默认展示的页码
}
context.startActivity(intent)

// CPDFDocumentFragment
val configuration = CPDFConfigurationUtils.normalConfig(context, "tools_default_configuration.json")
val documentFragment = CPDFDocumentFragment.newInstance(filePath, password, configuration)
supportFragmentManager
    .beginTransaction()
    .replace(R.id.fragment_container_view, documentFragment, "documentFragment")
    .commit()

运行后效果如下:

2.6.2_3

自定义

本节介绍如何通过配置文件对 ComPDFKit_Tools 进行功能与 UI 定制,包括:

  • 启用 / 禁用功能模块
  • 设置默认属性
  • 自定义工具栏与菜单
  • 自定义上下文菜单
  • 添加事件回调

示例均基于 tools_default_configuration.json,您可将该文件复制至项目 assets 目录进行修改和测试。

自定义功能模块

可通过 modeConfig 配置默认模式、可用模式及 UI 展示方式。

  • 设置默认模式(片段示意)
json
// tools_default_configuration.json
{
  "modeConfig": {
    // viewer, annotations, contentEditor, forms, signatures
    "initialViewMode": "viewer"
  }
}
  • 设置启用的模式列表(片段示意)
json
// tools_default_configuration.json
{
  "modeConfig": {
    "initialViewMode": "viewer",
    "availableViewModes": [
      "viewer",
      "annotations",
      "contentEditor",
      "forms",
      "signatures"
    ]
  }
}
  • UI 呈现方式(片段示意)
json
// tools_default_configuration.json
{
  "modeConfig": {
    // automatic : 自动模式,点击PDF页面切换全屏状态
    // always : 一直显示顶部、底部工具栏,不会进行隐藏
    // never: 隐藏顶部、底部工具栏,不会显示
    "uiVisibilityMode": "automatic"
  }
}

在创建CPDFDocumentFragment实例时进行使用:

java
CPDFConfiguration configuration = CPDFConfigurationUtils.normalConfig(context, "tools_default_configuration.json");
CPDFDocumentFragment documentFragment = CPDFDocumentFragment.newInstance(uri, "password", configuration);

CPDFDocumentActivity使用方法一致,请参考 2.6.3 章节。

设置默认属性

可在配置文件中统一设置注释、内容编辑、表单的默认样式,例如颜色、透明度、字体等。

json
// tools_default_configuration.json
{ 
  "annotationsConfig": {
    "initAttribute": {
      "note": {										// 便签注释属性
        "color": "#1460F3",
        "alpha": 255							// 取值范围:0-255
      },
      "highlight": {							// 高亮注释属性
        "color": "#1460F3",
        "alpha": 77
      },
      "freeText": {
        "fontColor": "#000000",
        "fontColorAlpha": 255,
        "fontSize": 30,
        "alignment": "left",
        "familyName": "Helvetica", // 字体名称, 详细请查看字体管理章节!
        "styleName": "Regular"		 // 字体样式 
      }
    }
  }
}

仅截取部分进行示例说明,完整内容请查看tools_default_configuration.json

  • 设置表单属性
json
// tools_default_configuration.json
{ 
  "formsConfig": {
    "initAttribute": {							
      "textField": {								// 文本域表单属性
        "fillColor": "#DDE9FF",
        "borderColor": "#1460F3",
        "borderWidth": 2,
        "fontColor": "#000000",
        "fontSize": 20,
        "alignment": "left",
        "multiline": true,
        "familyName": "Helvetica",
        "styleName": "Regular"
      },
      "checkBox": {									// 复选框属性
        "fillColor": "#DDE9FF",
        "borderColor": "#1460F3",
        "borderWidth": 2,
        "checkedColor": "#43474D",
        "isChecked": false,
        "checkedStyle": "check"			// check, circle, cross, diamond, square, star
      },
      "listBox": {									// 列表框属性
        "fillColor": "#DDE9FF",
        "borderColor": "#1460F3",
        "borderWidth": 2,
        "fontColor": "#000000",
        "fontSize": 20,
        "familyName": "Helvetica",
        "styleName": "Regular"
      }
    }
  }
}

仅截取部分进行示例说明,完整内容请查看tools_default_configuration.json

自定义工具栏

可通过 toolbarConfig 控制各模式下工具栏的显示状态,并自定义顶部菜单项。

  • 显示/隐藏工具栏
json
// tools_default_configuration.json
{
  "toolbarConfig": {
    "mainToolbarVisible": true,						// 显示/隐藏顶部工具栏
    "contentEditorToolbarVisible": true,	// 显示/隐藏内容编辑底部工具栏
    "annotationToolbarVisible": true,			// 显示/隐藏注释模式底部工具栏
    "formToolbarVisible": true,						// 显示/隐藏表单模式底部工具栏
    "signatureToolbarVisible": true				// 显示/隐藏签名模式底部工具栏
  }
}
  • 自定义注释工具栏
json
// tools_default_configuration.json
{
  "annotationsConfig": {
    "annotationAuthor": "Guest",	// 创建注释的作者名称
    "availableTypes": [						// 启用的注释类型
      "note",
      "highlight",
      "underline",
      "squiggly",
      "strikeout",
      "ink",
      "ink_eraser",
      "circle",
      "square",
      "arrow",
      "line",
      "freetext",
      "signature",
      "stamp",
      "pictures",
      "link",
      "sound"
    ],
    "availableTools": [						// 可用的工具
      "setting",
      "undo",
      "redo"
    ]
  }
}
  • 自定义表单工具栏
json
// tools_default_configuration.json
{ 
  "formsConfig": {
    "showCreateListBoxOptionsDialog": true,		// 创建列表框后是否弹出选项弹窗
    "showCreateComboBoxOptionsDialog": true,	// 创建下拉框后是否弹出选项弹窗
    "showCreatePushButtonOptionsDialog": true,// 创建按钮后是否弹出选项弹窗
    "availableTypes": [												// 启用的表单类型
      "textField",
      "checkBox",
      "radioButton",
      "listBox",
      "comboBox",
      "signaturesFields",
      "pushButton"
    ],
    "availableTools": [												// 启用的表单工具类型
      "undo",
      "redo"
    ]
  }
}
  • 自定义顶部工具栏菜单

默认配置提供了固定的菜单选项列表,支持隐藏或调整顺序。配置示例如下:

json
// tools_default_configuration.json
{
  "toolbarConfig": {
    "toolbarLeftItems": [
      "back"
    ],
    "toolbarRightItems": [
      "thumbnail",
      "search",
      "bota",
      "menu"
    ],
    "availableMenus": [
      "viewSettings",
      "documentEditor",
      "documentInfo",
      "save",
      "watermark",
      "security",
      "flattened",
      "share",
      "openDocument",
      "snip"
    ]
  }
}

此外,还支持配置自定义菜单选项以满足特定需求:

  1. 修改菜单选项图标与文本

若需修改默认菜单按钮的图标或文本,可通过自定义菜单配置快速实现。以下示例展示如何修改工具栏 More 菜单项的图标与文本:

json
// tools_default_configuration.json
{
  "toolbarConfig": { 
    "customToolbarLeftItems": [],		// 左侧菜单
    "customToolbarRightItems": [],  // 右侧菜单
    "customMoreMenuItems": [				// More菜单
      {
        "action": "viewSettings", 	// 阅读设置对应的id
        "icon": "ic_test_settings", // 按钮的图标, 图片资源必须放置在res/drawable目录中
        "title": "Settings" 				// 菜单的文本
      }
    ]
  }
}

请注意如果配置了对应的自定义菜单内容,对应的 toolbarLeftItems, toolbarRightItems, availableMenus 配置将不可用。

  1. 添加自定义菜单选项

您还可以根据需求添加自定义按钮。以下示例展示了如何在左侧菜单中添加一个“下载”按钮,点击后保存文档并获取文件路径:

json
// tools_default_configuration.json
{
  "toolbarConfig": { 
    // 左侧菜单
    "customToolbarLeftItems": [
      {
        "action": "custom", 				// 自定义事件的按钮 action 值必须为 custom.
        "identifier": "custom_download_action", // 自定义事件的唯一id值,点击按钮时将在回调中返回该id进行区分
        "icon": "ic_test_download", 	// 按钮的图标, 图片资源必须放置在res/drawable目录中
        "title": "Download" 				// 仅在customMoreMenuItems中生效
      }
    ],
    // 右侧菜单
    "customToolbarRightItems": [],
    // More菜单
    "customMoreMenuItems": []
  }
}

在使用CPDFDocumentFragment时添加事件回调:

java
CPDFDocumentFragment documentFragment = CPDFDocumentFragment.newInstance(uri, "password", configuration);
getSupportFragmentManager()
  .beginTransaction()
  .replace(R.id.fragment_container_view, documentFragment, "documentFragment")
  .commit();

// 添加回调
CPDFCustomEventCallbackHelper.getInstance().addCustomEventCallback(extraMap -> {
  // 先获取自定义事件分类, 这里存在多种类型,例如顶部工具栏事件,上下文菜单事件
  String customEventType = extraMap.get(CPDFCustomEventField.CUSTOM_EVENT_TYPE).toString();
  if (CPDFCustomEventType.TOOLBAR_ITEM_TAPPED.equals(customEventType)){
    // 顶部工具栏按钮点击事件
    String action = extraMap.get("identifier").toString();
    switch (action){
      case "custom_download_action":

        CPDFViewCtrl pdfView = documentFragment.pdfView;
        boolean saveIncremental = false;
        boolean fontSubset = true;

        pdfView.savePDF(saveIncremental, fontSubset, (filePath, pdfUri) -> {
          // 获取到uri 或 file
          CToastUtil.showToast(getApplicationContext(), "保存成功");
        }, e -> {

        });

        break;
    }
  }
});

// 请在CPDFDocumentFragment 使用完后移除注册的回调
CPDFCustomEventCallbackHelper.getInstance().removeCustomEventCallback(this);

通过以上步骤即可添加并响应一个完全自定义事件的菜单按钮。

自定义菜单按钮示意

自定义上下文菜单

选中注释、文本或表单时,会弹出上下文菜单以支持对象操作。该菜单同样支持自定义。若仅需移除或调整选项顺序,直接修改 JSON 文件即可。以下示例展示了如何在选中高亮注释时添加自定义按钮,用于获取高亮文本内容:

  1. 编辑 JSON 文件,添加一个自定义按钮
json
// tools_default_configuration.json
{
  "contextMenuConfig": {
    "fontSize": 14,
    "padding": [0,0,0,0],
    "iconSize": 36,
    "annotationMode": {
      "markupContent":  [				// markup注释包括: highlight, underline, strikeout, squiggly
        {
          "key": "custom",						// 自定义按钮 key 必须为 custom
          "identifier": "custom_markup_action_get_text",	// 自定义事件的id, 点击后回调此id进行区分
          "title": "Get Text",				// 选项显示的文本
          "icon": "ic_test_get_text",	// 显示的Icon, 图标需放置在 res/drawable目录下
          "showType": "text"					// 显示方式, text, icon
        }
      ]
    }
  }
}
  1. 添加点击事件回调
java
CPDFDocumentFragment documentFragment = CPDFDocumentFragment.newInstance(uri, "password", configuration);   
getSupportFragmentManager()
  .beginTransaction()
  .replace(R.id.fragment_container_view, documentFragment, "documentFragment")
  .commit();

CPDFCustomEventCallbackHelper.getInstance().addCustomEventCallback(extraMap -> {
  String customEventType = extraMap.get(CPDFCustomEventField.CUSTOM_EVENT_TYPE).toString();
  if (CPDFCustomEventType.CONTEXT_MENU_ITEM_TAPPED.equals(customEventType)){
    String identifier = extraMap.get(CPDFCustomEventField.IDENTIFIER).toString();
    switch (identifier){
      case "custom_markup_action_get_text":
        CPDFAnnotation annotation = (CPDFAnnotation) extraMap.get(CPDFCustomEventField.ANNOTATION);
        if (annotation != null){
          if (annotation instanceof CPDFMarkupAnnotation){
            CToastUtil.showToast(getApplicationContext(), ((CPDFMarkupAnnotation) annotation).getMarkedText());
          }
        }
        break;
    }
  }
});

运行效果如下:

2.6.3_5

根据不同的上下文菜单在extraMap中将返回不同的数据,请参考以下说明:

KeyValue说明
identifiercustom_markup_action_get_text自定义上下文菜单中定义的选项值,唯一id
annotationCPDFAnnotation选中注释时返回该对象
widgetCPDFWidget选中表单时返回该表单数据
editAreaCPDFEditArea内容编辑选中文本、图片返回对应的数据
textxxx长按选中文本返回选取的文本字符串
pageIndex长按选中文本所在的页码
rect长按选中文本,文本的矩形位置
imagebitmap截屏的上下文菜单返回截取的矩形区域Bitmap图片
point内容编辑模式长按PDF页面空白区域返回点击的位置坐标

添加回调事件

ComPDFKit_Tools 模块在 CPDFDocumentFragment 中提供了一系列事件回调,支持更多自定义功能的实现。以下展示部分常用回调:

  • 界面初始化完成回调
java
// CPDFDocumentFragment正确打开文档并加载完毕
documentFragment.setInitListener(pdfView -> {

});
  • 保存文档回调
java
documentFragment.setInitListener(pdfView -> {
  pdfView.setSaveCallback((filePath, pdfUri) -> {
    // 保存成功
  }, e -> {
    // 保存失败
  });
});
  • 创建注释回调
java
documentFragment.setAddAnnotCallback(new CPDFAddAnnotCallback() {
  @Override
  public void onAddAnnotation(CPDFPageView cpdfPageView, CPDFBaseAnnotImpl<CPDFAnnotation> cpdfBaseAnnot) {

  }
});
  • 选中注释、表单回调
java
documentFragment.setInitListener(pdfView -> {
  pdfView.getCPdfReaderView().setSelectAnnotCallback(new CPDFSelectAnnotCallback() {
    @Override
    public void onAnnotationSelected(CPDFPageView cpdfPageView, CPDFBaseAnnotImpl<CPDFAnnotation> cpdfBaseAnnot) {

    }

    @Override
    public void onAnnotationDeselected(CPDFPageView cpdfPageView, @Nullable CPDFBaseAnnotImpl<CPDFAnnotation> cpdfBaseAnnot) {

    }
  });
});
  • 选中内容编辑内容回调
java
documentFragment.setInitListener(pdfView -> {
  CPDFReaderView readerView = pdfView.getCPdfReaderView();
  pdfView.addSelectEditAreaChangeListener(new OnSelectEditAreaChangeListener() {
    @Override
    public void onSelectEditAreaChange(int type) {
      if (type == CEditToolbar.SELECT_AREA_NONE) {
        return;
      }
      CPDFEditArea editArea = readerView.getSelectEditArea();
    }
  });
});
  • 切换全屏回调
java
documentFragment.setFillScreenChangeListener(fillScreen -> {

});

更多回调接口请查看CPDFDocumentFragment, CPDFViewCtrl, CPDFReaderView