Skip to content

如何用Java开发一个Android PDF阅读器

本节将帮助您快速开始使用 ComPDFKit PDF SDK,并通过分步说明使用 Java 制作 Android 应用程序。通过以下步骤,您将获得一个简单的应用程序,可以显示指定 PDF 文件的内容。

创建新项目

  1. 使用 Android Studio 创建一个 Phone and Table 项目。这里我们创建一个 No Activity 项目。

create_project.png

添加 ComPDFKit PDF SDK 包

首先,我们需要导入 ComPDFKit PDF SDK。您可以通过两种方式集成它:使用 Gradle 集成手动集成

Gradle 集成

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

编辑它并添加完整的 ComPDFKit SDK 依赖项:

groovy
dependencies {
  implementation 'com.compdf:compdfkit:2.2.0'
  implementation 'com.compdf:compdfkit-ui:2.2.0'
}
  1. AndroidManifest.xml中申请读写权限:
xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

注: 对于面向 Android 6.0 或更高版本的应用程序,请确保在运行时检查并请求外部存储的读写权限。

  1. 如果您使用了在线认证的许可证,请在AndroidManifest.xml中添加网络访问权限:
xml
<uses-permission android:name="android.permission.INTERNET"/>

手动集成

  1. "ComPDFKit.aar""ComPDFKit-UI.aar" 复制到 app"libs" 目录中。

libs.png

  1. 将以下代码添加到 app 目录下的 "build.gradle" 文件中:
groovy
...
dependencies {
    /*ComPDFKit SDK*/
    implementation(fileTree('libs'))
    ...
}
...
  1. 将 ComPDFKit PDF SDK for Android 作为项目的依赖项添加进去。在 app 目录下的 "build.gradle" 文件中,将 "ComPDFKit.aar""ComPDFKit-UI.aar" 以及相关的支持库添加到 dependencies 中。为了简化操作,您可以按照以下方式更新依赖项:
groovy
dependencies {
    ...
    //glide
    implementation 'com.github.bumptech.glide:glide:4.12.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'

    implementation 'androidx.documentfile:documentfile:1.0.1'
}
  1. AndroidManifest.xml中申请读写权限:
xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

注: 对于面向 Android 6.0 或更高版本的应用程序,请确保在运行时检查并请求外部存储的读写权限。

应用许可证密钥

具体步骤请参考应用许可证密钥

添加混淆

在 "proguard-rules.pro" 文件中,请添加 compdfkit 的混淆配置信息如下:

-keep class com.compdfkit.ui.** {*;}
-keep class com.compdfkit.core.** {*;}

显示PDF文档

  1. 将 PDF 文档复制到 Android 项目的 assets 目录中。例如,将文件 "Quick Start Guide.pdf" 导入到路径 src/main/assets 中。

structure.png

  1. 在您的包下创建一个新的 Empty Activity,并将该 Activity 的名称设置为 MainActivity

new_activity.png

Android Studio将自动生成一个名为 "MainActivity.java" 的 source 文件和一个名为 "activity_main.xml" 的 layout 文件。

Source文件:

activity_java.png

Layout文件:

layout.png

  1. "activity_main.xml" 中创建一个 CPDFReaderView 来显示 PDF 文档的内容:
xml
<!-- 你的 activity_main.xml 文件 -->

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <!-- 创建一个 CPDFReaderView -->
    <com.compdfkit.ui.reader.CPDFReaderView
        android:id="@+id/readerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

从布局中获取 CPDFReaderView 或者直接在对应的 MainActivity.java 文件中的代码中创建一个CPDFReaderView

Java
// 你的 MainActivity.java 文件。

package com.compdfkit.pdfviewer;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import com.compdfkit.ui.reader.CPDFReaderView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 从xml中获取CPDFReaderView
        CPDFReaderView readerView = findViewById(R.id.readerview);
        // 创建CPDFReaderView的代码。
        // CPDFDocument readerView = new CPDFReaderView(content);
    }
}
kotlin
// 你的 MainActivity.kt 文件。
package com.compdfkit.pdfviewer

import androidx.appcompat.app.AppCompatActivity

import android.os.Bundle
import com.compdfkit.ui.reader.CPDFReaderView

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 从xml中获取CPDFReaderView
        val readerView = findViewById<CPDFReaderView>(R.id.readerview)
        // 创建CPDFReaderView的代码。
        // CPDFDocument readerView = new CPDFReaderView(content);
    }
}
  1. 打开文档。这是一个耗时的过程,因此需要在子线程中执行。文档成功打开后,会初始化渲染 PDF 的 UI:
Java
// 你的 MainActivity.java 文件。

... //导入。

public class MainActivity extends AppCompatActivity {

    // 将PDF文件从资源文件夹复制到缓存文件夹。
    private void copyPdfFromAssetsToCache(String fileName) {
        try {
            InputStream inputStream = getAssets().open(fileName);
            File outputFile = new File(getCacheDir(), fileName);
            FileOutputStream outputStream = new FileOutputStream(outputFile);

            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }

            inputStream.close();
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        CPDFReaderView readerView = findViewById(R.id.readerview);
        // 创建CPDFReaderView的代码。
        // CPDFDocument readerView = new CPDFReaderView(content);

        // 创建文档对象。
        CPDFDocument document = new CPDFDocument(this);
        

        new Thread(() -> {
            String fileName = "Quick Start Guide.pdf";
            copyPdfFromAssetsToCache(fileName);

            File file = new File(getCacheDir(), fileName);
            String filePath = file.getAbsolutePath();

            // 打开文档。
            CPDFDocument.PDFDocumentError error = document.open(filePath);
            if (error == CPDFDocument.PDFDocumentError.PDFDocumentErrorPassword) {
                // 该文档已加密,需要密码才能打开。
                error = document.open(filePath, "password");
            }

            if (error == CPDFDocument.PDFDocumentError.PDFDocumentErrorSuccess) {
                // 文档已成功打开,并且可以对数据进行解析和操作。
            } else {
                //无法打开PDF文件。具体错误可以参考API文件。
            }
        }).start();
    }
}
kotlin
// 你的 MainActivity.java 文件。

... //导入。

class MainActivity : AppCompatActivity() {

  // 将PDF文件从资源文件夹复制到缓存文件夹。
  private fun copyPdfFromAssetsToCache(fileName: String) {
    try {
      val inputStream = assets.open(fileName)
      val outputFile = File(cacheDir, fileName)
      val outputStream = FileOutputStream(outputFile)

      val buffer = ByteArray(1024)
      var bytesRead: Int
      while (inputStream.read(buffer).also { bytesRead = it } != -1) {
        outputStream.write(buffer, 0, bytesRead)
      }
      inputStream.close()
      outputStream.flush()
      outputStream.close()
    } catch (e: IOException) {
      e.printStackTrace()
    }
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val readerView = findViewById<CPDFReaderView>(R.id.readerview)
    // 创建CPDFReaderView的代码。
    // CPDFDocument readerView = new CPDFReaderView(content);

    // 创建文档对象。
    val document = CPDFDocument(this)

    Thread {
      val fileName = "Quick Start Guide.pdf"
      copyPdfFromAssetsToCache(fileName)

      val file = File(cacheDir, fileName)
      val filePath = file.absolutePath

      // 打开文档。
      var error = document.open(filePath)
      if (error == CPDFDocument.PDFDocumentError.PDFDocumentErrorPassword) {
        // 该文档已加密,需要密码才能打开。
        error = document.open(filePath, "password")
      }
      if (error == CPDFDocument.PDFDocumentError.PDFDocumentErrorSuccess) {
        // 文档已成功打开,并且可以对数据进行解析和操作。
      } else {
        // 无法打开PDF文件。具体错误可以参考API文件。
      }
    }.start()
  }
}
  1. 设置CPDFReaderView的基本属性:
Java
// 你的 MainActivity.java 文件。

... // 导入。

public class MainActivity extends AppCompatActivity {
    // 创建一个处理程序以在主线程上运行代码。
    private Handler mainThreadHandler = new Handler(Looper.getMainLooper());
...
    if (error == CPDFDocument.PDFDocumentError.PDFDocumentErrorSuccess) {
        // 文档已成功打开,并且可以对数据进行解析和操作。
        mainThreadHandler.post(() -> {
            // 设置UI的文档内容。
            readerView.setPDFDocument(document);
        });
    } else {
        // 无法打开PDF文件。具体错误可以参考API文件。
    }
...
}
kotlin
// 你的 MainActivity.java 文件。

... // 导入。

public class MainActivity : AppCompatActivity() {
    // 创建一个处理程序以在主线程上运行代码。
    private var mainThreadHandler =  Handler(Looper.getMainLooper());
    ...
    if (error == CPDFDocument.PDFDocumentError.PDFDocumentErrorSuccess) {
         // 文档已成功打开,并且可以对数据进行解析和操作。
        mainThreadHandler.post {
            // 设置UI的文档内容。
            readerView.setPDFDocument(document);
        }
    } else {
        // 无法打开PDF文件。具体错误可以参考API文件。
    }
    ...
}
  1. 在这个阶段,您的代码会像下面展示的这样:
Java
// 你的 MainActivity.java 文件。

... // 导入。

public class MainActivity extends AppCompatActivity {
    // 创建一个处理程序,在主线程上运行代码。

    private Handler mainThreadHandler = new Handler(Looper.getMainLooper());
    // 将PDF文件从资源文件夹复制到缓存文件夹。
    private void copyPdfFromAssetsToCache(String fileName) {
        try {
            InputStream inputStream = getAssets().open(fileName);
            File outputFile = new File(getCacheDir(), fileName);
            FileOutputStream outputStream = new FileOutputStream(outputFile);

            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }

            inputStream.close();
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        CPDFReaderView readerView = findViewById(R.id.readerview);

        // 创建文档对象。
        CPDFDocument document = new CPDFDocument(this);

        new Thread(() -> {
            String fileName = "Quick Start Guide.pdf";
            copyPdfFromAssetsToCache(fileName);

            File file = new File(getCacheDir(), fileName);
            String filePath = file.getAbsolutePath();

            // 打开文档。
            CPDFDocument.PDFDocumentError error = document.open(filePath);
            if (error == CPDFDocument.PDFDocumentError.PDFDocumentErrorPassword) {
                // 该文档已加密,需要密码才能打开。
                error = document.open(filePath, "password");
            }

            if (error == CPDFDocument.PDFDocumentError.PDFDocumentErrorSuccess) {
                // 文档已成功打开,并且可以对数据进行解析和操作。
                mainThreadHandler.post(() -> {
                    // 将文档设置为阅读器视图。
                    readerView.setPDFDocument(document);
                });
            } else {
                // 无法打开PDF文件。具体错误可以参考API文件。
            }
        }).start();
    }
}
kotlin
// 你的 MainActivity.java 文件。

... // 导入。

class MainActivity : AppCompatActivity() {
    // 创建一个处理程序,在主线程上运行代码。
    private val mainThreadHandler = Handler(Looper.getMainLooper())

    // 将PDF文件从资源文件夹复制到缓存文件夹。
    private fun copyPdfFromAssetsToCache(fileName: String) {
        try {
            val inputStream = assets.open(fileName)
            val outputFile = File(cacheDir, fileName)
            val outputStream = FileOutputStream(outputFile)
            val buffer = ByteArray(1024)
            var bytesRead: Int
            while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                outputStream.write(buffer, 0, bytesRead)
            }
            inputStream.close()
            outputStream.flush()
            outputStream.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val readerView = findViewById<CPDFReaderView>(R.id.readerview)

        // 创建文档对象。
        val document = CPDFDocument(this)
        Thread {
            val fileName = "Quick Start Guide.pdf"
            copyPdfFromAssetsToCache(fileName)
            val file = File(cacheDir, fileName)
            val filePath = file.absolutePath

            // 打开文档。
            var error = document.open(filePath)
            if (error == PDFDocumentError.PDFDocumentErrorPassword) {
                // 该文档已加密,需要密码才能打开。
                error = document.open(filePath, "password")
            }
            if (error == PDFDocumentError.PDFDocumentErrorSuccess) {
                // 文档已成功打开,并且可以对数据进行解析和操作。
                mainThreadHandler.post {
                    // 将文档设置为阅读器视图。
                    readerView.pdfDocument = document
                }
            } else {
                // 无法打开PDF文件。具体错误可以参考API文件。
            }
        }.start()
    }
}
xml
<!-- 你的 activity_main.xml 文件 -->
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.compdfkit.ui.reader.CPDFReaderView
        android:id="@+id/readerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
  1. 运行应用程序。
2.4.2.1-1

现在,借助ComPDFKit的帮助,您获得了一个简单的应用程序来显示PDF文件。

问题排除

  1. 无法打开 PDF 文件

    我们向您提供的许可证是与您的应用程序 ID 绑定的,因此请确保所获取的许可证与您的应用程序 ID 匹配。

  2. 其它问题

    如果您在集成 ComPDFKit PDF SDK for Android 时遇到其他问题,请随时联系 ComPDFKit 团队。