自定义UI
除核心功能模块外,ComPDF SDK 还提供了 ComPDFKit_Tools 模块。该模块内置完整的 UI 组件与交互逻辑,可在核心能力基础上快速集成,并支持灵活扩展与定制。
此外,我们还提供了示例应用 PDFViewer,用于演示 ComPDFKit_Tools 的集成与使用方式,供开发者参考。
本章节将从以下几个方面介绍 ComPDFKit_Tools:
- 概述:模块功能与 UI 示例说明
- 集成:如何快速集成并使用 ComPDFKit_Tools
- 自定义:如何进行 UI 与功能定制
概述
ComPDFKit_Tools 模块集成了常见的 PDF 浏览与编辑能力,包括但不限于:
- 查看器(Viewer)
- 注释(Annotations)
- 表单(Forms)
- 内容编辑(Content Editor)
- 页面编辑(Page Editor)
- 数字签名(Signatures)
- 安全与水印(Security & Watermark)
通过 CPDFDocumentActivity 或 CPDFDocumentFragment,即可快速在项目中启动完整的 PDF 文档界面。 UI 行为及功能模块可通过配置文件进行控制;如需更深度的定制,也可直接使用 ComPDFKit_Tools 模块源码进行二次开发。
集成 SDK
SDK 支持多种集成方式,您可根据项目需求选择合适的方案。
版本与兼容性(建议)
- 与核心模块保持一致:
compdfkit-tools版本需与compdfkit、compdfkit-ui保持一致。- Android 版本:请根据 SDK 发布说明选择合适的
minSdk/compileSdk/targetSdk与 AGP 版本。- R8/ProGuard:若开启混淆,请参考 SDK 随附的混淆配置。
Gradle 集成
- 打开位于项目根目录中的 "settings.gradle" 文件,然后添加 mavenCentral 存储库:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
+ mavenCentral()
}
}- 在应用程序模块目录中打开 "build.gradle" 文件:

编辑它并添加 ComPDFKit_Tools 模块依赖项:
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"
}手动集成
- 将 "ComPDFKit-Tools.aar" 复制到 app 模块的 "libs" 目录中。

- 将以下代码添加到 app 目录下的 "build.gradle" 文件中:
...
dependencies {
/* ComPDF SDK */
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
...
}
...请务必按照手动集成方式同时依赖 ComPDFKit.aar、ComPDFKit-UI.aar,确保正常运行。
源码集成
可通过 SDK 安装包或 GitHub 示例项目 获取 ComPDFKit_Tools 模块源码。
- 下载或解压 SDK 源码
- 在 Android Studio 中通过 File → New → Import Module 导入模块

- 在
app/build.gradle中添加:
dependencies {
implementation project(path: ':ComPDFKit_Tools')
}使用
完成模块集成后,可通过 CPDFDocumentActivity 或 CPDFDocumentFragment 启动文档界面。 UI 与功能配置通过 CPDFConfiguration 控制,默认配置文件为 tools_default_configuration.json。
提示(Android 10/11+ 存储访问)
- 推荐优先通过
Uri(SAF/MediaStore)方式打开与保存文档;直接使用文件路径在新系统上可能受分区存储(Scoped Storage)限制。tools_default_configuration.json默认已打包在 Tools 模块assets中,可直接使用或复制到应用assets目录覆盖默认配置。
最小可用示例(通过 Uri 打开,Activity 方式):
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);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)- 打开 Uri 路径 PDF 文件
// 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();// 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()- 打开 File 文件路径 PDF 文件(注意范围存储限制)
// 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();// 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()运行后效果如下:

自定义
本节介绍如何通过配置文件对 ComPDFKit_Tools 进行功能与 UI 定制,包括:
- 启用 / 禁用功能模块
- 设置默认属性
- 自定义工具栏与菜单
- 自定义上下文菜单
- 添加事件回调
示例均基于 tools_default_configuration.json,您可将该文件复制至项目 assets 目录进行修改和测试。
自定义功能模块
可通过 modeConfig 配置默认模式、可用模式及 UI 展示方式。
- 设置默认模式(片段示意)
// tools_default_configuration.json
{
"modeConfig": {
// viewer, annotations, contentEditor, forms, signatures
"initialViewMode": "viewer"
}
}- 设置启用的模式列表(片段示意)
// tools_default_configuration.json
{
"modeConfig": {
"initialViewMode": "viewer",
"availableViewModes": [
"viewer",
"annotations",
"contentEditor",
"forms",
"signatures"
]
}
}- UI 呈现方式(片段示意)
// tools_default_configuration.json
{
"modeConfig": {
// automatic : 自动模式,点击PDF页面切换全屏状态
// always : 一直显示顶部、底部工具栏,不会进行隐藏
// never: 隐藏顶部、底部工具栏,不会显示
"uiVisibilityMode": "automatic"
}
}在创建CPDFDocumentFragment实例时进行使用:
CPDFConfiguration configuration = CPDFConfigurationUtils.normalConfig(context, "tools_default_configuration.json");
CPDFDocumentFragment documentFragment = CPDFDocumentFragment.newInstance(uri, "password", configuration);CPDFDocumentActivity使用方法一致,请参考 2.6.3 章节。
设置默认属性
可在配置文件中统一设置注释、内容编辑、表单的默认样式,例如颜色、透明度、字体等。
// 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
- 设置表单属性
// 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 控制各模式下工具栏的显示状态,并自定义顶部菜单项。
- 显示/隐藏工具栏
// tools_default_configuration.json
{
"toolbarConfig": {
"mainToolbarVisible": true, // 显示/隐藏顶部工具栏
"contentEditorToolbarVisible": true, // 显示/隐藏内容编辑底部工具栏
"annotationToolbarVisible": true, // 显示/隐藏注释模式底部工具栏
"formToolbarVisible": true, // 显示/隐藏表单模式底部工具栏
"signatureToolbarVisible": true // 显示/隐藏签名模式底部工具栏
}
}- 自定义注释工具栏
// 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"
]
}
}- 自定义表单工具栏
// tools_default_configuration.json
{
"formsConfig": {
"showCreateListBoxOptionsDialog": true, // 创建列表框后是否弹出选项弹窗
"showCreateComboBoxOptionsDialog": true, // 创建下拉框后是否弹出选项弹窗
"showCreatePushButtonOptionsDialog": true,// 创建按钮后是否弹出选项弹窗
"availableTypes": [ // 启用的表单类型
"textField",
"checkBox",
"radioButton",
"listBox",
"comboBox",
"signaturesFields",
"pushButton"
],
"availableTools": [ // 启用的表单工具类型
"undo",
"redo"
]
}
}- 自定义顶部工具栏菜单
默认配置提供了固定的菜单选项列表,支持隐藏或调整顺序。配置示例如下:
// tools_default_configuration.json
{
"toolbarConfig": {
"toolbarLeftItems": [
"back"
],
"toolbarRightItems": [
"thumbnail",
"search",
"bota",
"menu"
],
"availableMenus": [
"viewSettings",
"documentEditor",
"documentInfo",
"save",
"watermark",
"security",
"flattened",
"share",
"openDocument",
"snip"
]
}
}此外,还支持配置自定义菜单选项以满足特定需求:
- 修改菜单选项图标与文本
若需修改默认菜单按钮的图标或文本,可通过自定义菜单配置快速实现。以下示例展示如何修改工具栏 More 菜单项的图标与文本:
// tools_default_configuration.json
{
"toolbarConfig": {
"customToolbarLeftItems": [], // 左侧菜单
"customToolbarRightItems": [], // 右侧菜单
"customMoreMenuItems": [ // More菜单
{
"action": "viewSettings", // 阅读设置对应的id
"icon": "ic_test_settings", // 按钮的图标, 图片资源必须放置在res/drawable目录中
"title": "Settings" // 菜单的文本
}
]
}
}请注意如果配置了对应的自定义菜单内容,对应的 toolbarLeftItems, toolbarRightItems, availableMenus 配置将不可用。
- 添加自定义菜单选项
您还可以根据需求添加自定义按钮。以下示例展示了如何在左侧菜单中添加一个“下载”按钮,点击后保存文档并获取文件路径:
// 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时添加事件回调:
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 文件即可。以下示例展示了如何在选中高亮注释时添加自定义按钮,用于获取高亮文本内容:
- 编辑 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
}
]
}
}
}- 添加点击事件回调
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;
}
}
});运行效果如下:

根据不同的上下文菜单在extraMap中将返回不同的数据,请参考以下说明:
| Key | Value | 说明 |
|---|---|---|
| identifier | custom_markup_action_get_text | 自定义上下文菜单中定义的选项值,唯一id |
| annotation | CPDFAnnotation | 选中注释时返回该对象 |
| widget | CPDFWidget | 选中表单时返回该表单数据 |
| editArea | CPDFEditArea | 内容编辑选中文本、图片返回对应的数据 |
| text | xxx | 长按选中文本返回选取的文本字符串 |
| pageIndex | 长按选中文本所在的页码 | |
| rect | 长按选中文本,文本的矩形位置 | |
| image | bitmap | 截屏的上下文菜单返回截取的矩形区域Bitmap图片 |
| point | 内容编辑模式长按PDF页面空白区域返回点击的位置坐标 |
添加回调事件
ComPDFKit_Tools 模块在 CPDFDocumentFragment 中提供了一系列事件回调,支持更多自定义功能的实现。以下展示部分常用回调:
- 界面初始化完成回调
// CPDFDocumentFragment正确打开文档并加载完毕
documentFragment.setInitListener(pdfView -> {
});- 保存文档回调
documentFragment.setInitListener(pdfView -> {
pdfView.setSaveCallback((filePath, pdfUri) -> {
// 保存成功
}, e -> {
// 保存失败
});
});- 创建注释回调
documentFragment.setAddAnnotCallback(new CPDFAddAnnotCallback() {
@Override
public void onAddAnnotation(CPDFPageView cpdfPageView, CPDFBaseAnnotImpl<CPDFAnnotation> cpdfBaseAnnot) {
}
});- 选中注释、表单回调
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) {
}
});
});- 选中内容编辑内容回调
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();
}
});
});- 切换全屏回调
documentFragment.setFillScreenChangeListener(fillScreen -> {
});更多回调接口请查看CPDFDocumentFragment, CPDFViewCtrl, CPDFReaderView。