构建一个功能完整的 PDF 阅读器(UI Extensions)
本文介绍如何基于 UI Extensions Component 快速构建一个功能完整的 PDF 阅读器(包含内置工具栏与常用功能模块 UI)。
说明 如需仅展示 PDF(不含内置 UI),请参考:构建一个功能基础的 PDF 阅读器(PDFViewCtrl)。
前置条件
- 完成 集成福昕 Android SDK(含工程创建、SDK 集成与授权初始化)。
- 确认已启用 UI Extensions 依赖(参考:启用 UI Extensions)。
步骤 1:接入 UI Extensions,构建“完整阅读器”
核心思路是:
- 创建
PDFViewCtrl作为渲染与交互容器。 - 创建
UIExtensionsManager并绑定到PDFViewCtrl。 - 通过
UIExtensionsManager.openDocument()打开文档,并将其ContentView作为界面内容。
1.1 主题与全屏配置(必需)
UI Extensions 会接管界面布局。为避免系统 ActionBar / 标题栏影响内置 UI 布局,建议:
- 主题使用 NoActionBar
- 例如 在
YourProjectName/app/src/main/res/values/themes.xml中将主题样式设置为Theme.MaterialComponents.Light.NoActionBar
- 例如 在
- Activity 设为全屏
示例(在 Activity#onCreate() 的 super.onCreate() 之后、初始化 UI 之前调用):
java
import android.view.Window;
import android.view.WindowManager;
// Turn off the title at the top of the screen.
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
// Set the window to Fullscreen.
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
1.2 初始化 UIExtensionsManager
java
import com.foxit.uiextensions.UIExtensionsManager;
private UIExtensionsManager uiExtensionsManager = null;
uiExtensionsManager = new UIExtensionsManager(this.getApplicationContext(), pdfViewCtrl);
uiExtensionsManager.setAttachedActivity(this);
uiExtensionsManager.onCreate(this, pdfViewCtrl, savedInstanceState);
pdfViewCtrl.setUIExtensionsManager(uiExtensionsManager);
1.3 打开文档
使用 UIExtensionsManager.openDocument() 打开文档(而不是 PDFViewCtrl.openDoc()):
java
import android.os.Environment;
String path = Environment.getExternalStorageDirectory().getPath() + "/FoxitSDK/Sample.pdf";
uiExtensionsManager.openDocument(path, null);
setContentView(uiExtensionsManager.getContentView());
1.4 转发生命周期(重要)
为确保部分功能正常工作,需要在 Activity 中转发生命周期与事件回调。示例代码如下:
java
@Override
public void onStart() {
if (uiExtensionsManager != null) {
uiExtensionsManager.onStart(this);
}
super.onStart();
}
@Override
public void onStop() {
if (uiExtensionsManager != null) {
uiExtensionsManager.onStop(this);
}
super.onStop();
}
@Override
public void onPause() {
if (uiExtensionsManager != null) {
uiExtensionsManager.onPause(this);
}
super.onPause();
}
@Override
public void onResume() {
if (uiExtensionsManager != null) {
uiExtensionsManager.onResume(this);
}
super.onResume();
}
@Override
protected void onDestroy() {
if (uiExtensionsManager != null) {
uiExtensionsManager.onDestroy(this);
}
super.onDestroy();
}
1.5 更新 AndroidManifest.xml(建议)
为保证部分内置功能可用且行为正确,建议同步更新 AndroidManifest.xml:
- 权限(按功能启用)
- 摄像头:用于扫描等需要调用相机的场景。
- 录音:用于音视频相关能力;缺失时相关功能可能不可用。
示例:
xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
- Activity 的
configChanges(重要)
为避免旋转屏幕等配置变化触发 Activity 重建,从而影响部分功能,建议在 MainActivity 增加:
xml
<activity
android:name=".MainActivity"
android:configChanges="keyboardHidden|orientation|locale|layoutDirection|screenSize" />
参考实现:MainActivity.java
以下是可直接对照的 MainActivity.java 完整示例(包含 UI Extensions 初始化、运行时权限处理、文档打开与生命周期转发):
[MainActivity.java]
java
package com.foxit.pdfreader;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.Settings;
import android.view.KeyEvent;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.foxit.sdk.PDFViewCtrl;
import com.foxit.sdk.common.Constants;
import com.foxit.sdk.common.Library;
import com.foxit.uiextensions.UIExtensionsManager;
public class MainActivity extends AppCompatActivity {
private PDFViewCtrl pdfViewCtrl = null;
private UIExtensionsManager uiExtensionsManager = null;
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static final int REQUEST_ALL_FILES_ACCESS_PERMISSION = 222;
private static final String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
// The value of "sn" can be found in the "rdk_sn.txt".
// The value of "key" can be found in the "rdk_key.txt".
private static String sn = " ";
private static String key = " ";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// initialize the library.
int errorCode = Library.initialize(sn, key);
if (errorCode != Constants.e_ErrSuccess)
return;
// Turn off the title at the top of the screen.
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
// Set the window to Fullscreen.
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
// Instantiate a PDFViewCtrl object.
pdfViewCtrl = new PDFViewCtrl(this);
// Set the associated activity for RMS UI operations.
pdfViewCtrl.setAttachedActivity(this);
// Initialize a UIExtensionManager object and set it to PDFViewCtrl.
uiExtensionsManager = new UIExtensionsManager(this.getApplicationContext(), pdfViewCtrl);
uiExtensionsManager.setAttachedActivity(this);
uiExtensionsManager.onCreate(this, pdfViewCtrl, savedInstanceState);
pdfViewCtrl.setUIExtensionsManager(uiExtensionsManager);
setContentView(uiExtensionsManager.getContentView());
// Require the authorization of runtime permissions.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (!Environment.isExternalStorageManager()) {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.setData(Uri.parse("package:" + getApplicationContext().getPackageName()));
startActivityForResult(intent, REQUEST_ALL_FILES_ACCESS_PERMISSION);
return;
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int permission = ContextCompat.checkSelfPermission(this.getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
return;
}
}
// Open and Render a PDF document.
String path = Environment.getExternalStorageDirectory().getPath() + "/FoxitSDK/Sample.pdf";
uiExtensionsManager.openDocument(path, null);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == REQUEST_EXTERNAL_STORAGE && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Open and Render a PDF document.
String path = Environment.getExternalStorageDirectory().getPath() + "/FoxitSDK/Sample.pdf";
uiExtensionsManager.openDocument(path, null);
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_ALL_FILES_ACCESS_PERMISSION) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (Environment.isExternalStorageManager()) {
String path = Environment.getExternalStorageDirectory().getPath() + "/FoxitSDK/Sample.pdf";
uiExtensionsManager.openDocument(path, null);
}
}
} else {
if (pdfViewCtrl != null) {
pdfViewCtrl.handleActivityResult(requestCode, resultCode, data);
}
}
}
@Override
public void onStart() {
if (uiExtensionsManager != null) {
uiExtensionsManager.onStart(this);
}
super.onStart();
}
@Override
public void onStop() {
if (uiExtensionsManager != null) {
uiExtensionsManager.onStop(this);
}
super.onStop();
}
@Override
public void onPause() {
if (uiExtensionsManager != null) {
uiExtensionsManager.onPause(this);
}
super.onPause();
}
@Override
public void onResume() {
if (uiExtensionsManager != null) {
uiExtensionsManager.onResume(this);
}
super.onResume();
}
@Override
protected void onDestroy() {
if (uiExtensionsManager != null) {
uiExtensionsManager.onDestroy(this);
}
super.onDestroy();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (uiExtensionsManager != null) {
uiExtensionsManager.onConfigurationChanged(this, newConfig);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (uiExtensionsManager != null && uiExtensionsManager.onKeyDown(this, keyCode, event))
return true;
return super.onKeyDown(keyCode, event);
}
}
步骤 2:运行与验证
- 将测试文件(例如
Sample.pdf)放到设备或模拟器的FoxitSDK目录(示例路径:/sdcard/FoxitSDK/Sample.pdf)。 - 运行应用后,按系统提示授予文件访问权限。
Android 10/11+ 存储权限说明(示例代码相关)
- Android 10(API 29):如使用旧外部存储访问方式,可能需要在
AndroidManifest.xml中加入android:requestLegacyExternalStorage="true"。- Android 11+(API 30+):若仍访问共享存储路径,可能需要引导用户授予“所有文件访问权限”(示例工程通常这样处理)。