【Android / Kotlin】ズーム対応のPDF表示サンプルアプリ（PdfRenderer + PhotoView）

# はじめに
今回 Android でPDFを表示するためにはどのように実装する必要があるかを調査、検討しました。

結論、Android でPDF表示をするのはかなり大変。
というかただ表示するだけであればそこまで難しくはないものの、使い勝手をよくするためにいろいろな機能を実装したりするとなると相当大変そうです。（というかそもそも可能か？ということもあります。）

iOS のようには行かないみたいです。
Android 大変や…

こんな意見もあって、参考にさせていただきました。
https://qiita.com/s_of_p/items/1c1a467d246ab7dc45e1

そこで今回は

- PDFを１枚だけ表示（標準SDKの[PdfRenderer](https://developer.android.com/reference/kotlin/android/graphics/pdf/PdfRenderer)を利用）
- ズームイン / ズームアウトに対応（ライブラリ[PhotoView](https://github.com/Baseflow/PhotoView)を利用）

この２点に対応した簡単なサンプルアプリを作成しました。

![20210525_115215.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621162/0c7cb7b7-86aa-6e95-4400-4f29eff75401.gif)


# 環境
- Android Studio 4.2.1
- Build #AI-202.7660.26.42.7351085, built on May 11, 2021
- Runtime version: 11.0.8+10-b944.6916264 x86_64
- VM: OpenJDK 64-Bit Server VM by N/A
- macOS 10.15.7

# セットアップ
以下を利用できるようにするため、``build.gradle``に記述を追加する

- データバインディング
- PhotoView

## build.gradle(Projectレベル)
```build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    ext.kotlin_version = "1.5.0"
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.2.1"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url "https://www.jitpack.io" } // ここに追加（PhotoView導入のため
        jcenter() // Warning: this repository is going to shut down soon
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
```

## build.gradle(Moduleレベル)

```build.gradle
plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.android.pdfrenderer_sample"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }

    // DataBinding 導入のため 以下３行を追加
    buildFeatures {
        dataBinding = true
    }
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    implementation 'com.github.chrisbanes:PhotoView:2.3.0' // ここに追加（PhotoView導入のため
}
```

## PDFファイルを assets にセット

``app/assets``ディレクトリを作成してその配下に表示対象のPDFファイルをセットします。

![スクリーンショット 2021-05-25 12.57.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621162/9a87ad43-d658-d842-c1d8-88f2c810bba7.png)


# 実装
## レイアウトファイル
```activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    >
    <androidx.constraintlayout.widget.ConstraintLayout
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

<!--        ここにPDFを表示させる-->
        <com.github.chrisbanes.photoview.PhotoView
            android:id="@+id/photo_view"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
```


## アクティビティ
```MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // レイアウト と PhotoView 要素を バインド
        val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        val photoView = binding.photoView

        // assets にある PDFファイル にアクセス
        val file = File(cacheDir.path, "sample_explain.pdf")
        val fileOutputStream = FileOutputStream(file)
        assets.open("sample_explain.pdf").copyTo(fileOutputStream)

        // PdfRenderer を インスタンス化
        val parcelFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
        val pdfRenderer = PdfRenderer(parcelFileDescriptor)

        // PdfRenderer を オープン（今回は最初のページのみ表示するため引数の index:0）
        val page = pdfRenderer.openPage(0)

        // Bitmap を生成して 表示する PhotoView にセット（幅、高さなどは適当に設定してます）
        val bitmap = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888)
        page.render(bitmap, Rect(0, 0, 500, 500), null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
        photoView.setImageBitmap(bitmap)

        // page と PdfRenderer を クローズ
        page.close()
        pdfRenderer.close()
    }
}
```

# 最後に
PDFを複数ページ表示させるためにはリサイクラービューを利用したりと工夫が必要そうです。
他にもいろいろと機能を実装するためにはかなり実装を工夫して頑張る必要がありそうでした。

参考にさせていただいた資料（ありがとうございました！！！）

- https://developer.android.com/reference/kotlin/android/graphics/pdf/PdfRenderer
- https://qiita.com/seekseep/items/e55a489c3dcb2e1c3fb8
- https://github.com/Baseflow/PhotoView
- https://proandroiddev.com/rendering-pdfs-on-android-the-easy-way-c05635b2c3a8
- https://qiita.com/s_of_p/items/1c1a467d246ab7dc45e1
- https://www.codota.com/code/java/classes/android.graphics.pdf.PdfRenderer?snippet=5ce69a437e03440004d0ad11