PdfViewerFragment 是專用的 Fragment,可用於在 Android 應用程式中顯示 PDF 文件。PdfViewerFragment 可簡化 PDF 轉譯作業,讓您專注於應用程式功能的其他層面。
結果
版本相容性
如要使用 PdfViewerFragment,應用程式必須指定至少 Android S (API 級別 31) 和 SDK 擴充功能級別 13。如未滿足這些相容性要求,程式庫會擲回 UnsupportedOperationException。
您可以使用 SdkExtensions 模組,在執行階段檢查 SDK 擴充功能版本。這樣一來,只有在裝置符合必要條件時,您才能有條件地載入片段和 PDF 文件。
if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
// Load the fragment and document.
}
依附元件
如要將 PDF 檢視器併入應用程式,請在應用程式模組的 build.gradle 檔案中宣告 androidx.pdf 依附元件。您可以透過 Google Maven 存放區存取 PDF 程式庫。
dependencies {
val pdfVersion = "1.0.0-alpha0X"
implementation("androidx.pdf:pdf:pdf-viewer-fragment:$pdfVersion")
}
PdfViewerFragment 功能
PdfViewerFragment 會以分頁格式呈現 PDF 文件,方便瀏覽。為有效載入,這個片段採用兩階段的算繪策略,逐步載入網頁維度。
為最佳化記憶體用量,PdfViewerFragment 只會算繪目前顯示的頁面,並釋出螢幕外頁面的點陣圖。此外,PdfViewerFragment 還包含浮動動作按鈕 (FAB),可透過觸發包含文件 URI 的隱含 android.intent.action.ANNOTATE 意圖,支援註解功能。
實作
在 Android 應用程式中加入 PDF 檢視器需要多個步驟。
建立活動版面配置
首先,請為代管 PDF 檢視器的活動定義版面配置 XML。版面配置應包含 FrameLayout,其中包含 PdfViewerFragment 和使用者互動按鈕,例如在文件中搜尋。
<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:id="@+id/pdf_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<FrameLayout
android:id="@+id/fragment_container_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/search_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/search_string"
app:strokeWidth="1dp"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
設定活動
主機 PdfViewerFragment 的活動必須擴充 AppCompatActivity。在活動的 onCreate() 方法中,將內容檢視區塊設為您建立的版面配置,並初始化任何必要的使用者介面元素。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val getContentButton: MaterialButton = findViewById(R.id.launch_button)
val searchButton: MaterialButton = findViewById(R.id.search_button)
}
}
初始化 PdfViewerFragment
使用從 getSupportFragmentManager() 取得的片段管理工具,建立 PdfViewerFragment 的例項。建立新的片段執行個體前,請先檢查是否已存在片段執行個體,尤其是在設定變更期間。
在下列範例中,initializePdfViewerFragment() 函式會處理片段交易的建立和提交。這個函式會將容器中現有的片段,替換為 PdfViewerFragment 的例項。
class MainActivity : AppCompatActivity() {
@RequiresExtension(extension = Build.VERSION_CODES.S, version = 13)
private var pdfViewerFragment: PdfViewerFragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
// ...
if (pdfViewerFragment == null) {
pdfViewerFragment =
supportFragmentManager
.findFragmentByTag(PDF_VIEWER_FRAGMENT_TAG) as PdfViewerFragment?
}
}
// Used to instantiate and commit the fragment.
@RequiresExtension(extension = Build.VERSION_CODES.S, version = 13)
private fun initializePdfViewerFragment() {
// This condition can be skipped if you want to create a new fragment every time.
if (pdfViewerFragment == null) {
val fragmentManager: FragmentManager = supportFragmentManager
// Fragment initialization.
pdfViewerFragment = PdfViewerFragmentExtended()
val transaction: FragmentTransaction = fragmentManager.beginTransaction()
// Replace an existing fragment in a container with an instance of a new fragment.
transaction.replace(
R.id.fragment,4_container_view,
pdfViewerFragment!!,
PDF_VIEWER_FRAGMENT_TAG
)
transaction.commitAllowingStateLoss()
fragmentManager.executePendingTransactions()
}
}
companion object {
private const val MIME_TYPE_PDF = "application/pdf"
private const val PDF_VIEWER_FRAGMENT_TAG = "pdf_viewer_fragment_tag"
}
}
擴充 PdfViewerFragment 功能
PdfViewerFragment 會公開函式,您可以覆寫這些函式來擴充功能。建立從 PdfViewerFragment 沿用的新類別。在子類別中,覆寫 onLoadDocumentSuccess() 和 onLoadDocumentError() 等方法,加入自訂邏輯,例如記錄指標。
@RequiresExtension(extension = Build.VERSION_CODES.S, version = 13)
class PdfViewerFragmentExtended : PdfViewerFragment() {
private val someLogger : SomeLogger = // ... used to log metrics
override fun onLoadDocumentSuccess() {
someLogger.log(/** log document success */)
}
override fun onLoadDocumentError(error: Throwable) {
someLogger.log(/** log document error */, error)
}
}
啟用文件搜尋功能
PdfViewerFragment 不提供內建搜尋選單,但支援搜尋列。您可以使用 isTextSearchActive API 控制搜尋列的顯示設定。如要啟用文件搜尋功能,請設定 PdfViewerFragment 執行個體的 isTextSearchActive 屬性。
使用 WindowCompat.setDecorFitsSystemWindows() 確保 WindowInsetsCompat 正確傳遞至內容檢視區塊,這是搜尋檢視區塊正確定位的必要條件。
class MainActivity : AppCompatActivity() {
@RequiresExtension(extension = Build.VERSION_CODES.S, version = 13)
override fun onCreate(savedInstanceState: Bundle?) {
// ...
searchButton.setOnClickListener {
pdfViewerFragment?.isTextSearchActive =
pdfViewerFragment?.isTextSearchActive == false
}
// Ensure WindowInsetsCompat are passed to content views without being
// consumed by the decor view. These insets are used to calculate the
// position of the search view.
WindowCompat.setDecorFitsSystemWindows(window, false)
}
}
與檔案選擇器整合
如要允許使用者從裝置選取 PDF 檔案,請整合 Android 檔案挑選器與 PdfViewerFragment。首先,請更新活動的版面配置 XML,加入啟動檔案挑選器的按鈕。
<...>
<FrameLayout
...
app:layout_constraintBottom_toTopOf="@+id/launch_button"/>
// Adding a button to open file picker.
<com.google.android.material.button.MaterialButton
android:id="@+id/launch_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/launch_string"
app:strokeWidth="1dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/search_button"/>
<com.google.android.material.button.MaterialButton
...
app:layout_constraintStart_toEndOf="@id/launch_button" />
</androidx.constraintlayout.widget.ConstraintLayout>
接著,在活動中,使用 registerForActivityResult(GetContent()) 啟動檔案挑選工具。使用者選取檔案時,回呼會提供 URI。接著,您可以使用這個 URI 設定 PdfViewerFragment 執行個體的 documentUri 屬性,載入並顯示所選 PDF。
class MainActivity : AppCompatActivity() {
// ...
private var filePicker: ActivityResultLauncher<String> =
registerForActivityResult(GetContent()) { uri: Uri? ->
uri?.let {
initializePdfViewerFragment()
pdfViewerFragment?.documentUri = uri
}
}
override fun onCreate(savedInstanceState: Bundle?) {
// ...
getContentButton.setOnClickListener { filePicker.launch(MIME_TYPE_PDF) }
}
private fun initializePdfViewerFragment() {
// ...
}
companion object {
private const val MIME_TYPE_PDF = "application/pdf"
// ...
}
}
自訂 UI
您可以覆寫程式庫公開的 XML 屬性,自訂 PdfViewerFragment 的使用者介面。您可以自訂捲軸和網頁指標等元素的樣式,配合應用程式設計。
可自訂的屬性包括:
fastScrollVerticalThumbDrawable:設定捲軸滑桿的可繪項目。fastScrollPageIndicatorBackgroundDrawable:設定頁面指標的背景可繪項目。fastScrollPageIndicatorMarginEnd:設定頁面指標的右邊界。確認利潤值為正數。fastScrollVerticalThumbMarginEnd:設定垂直捲軸滑桿的右邊界。確認利潤值為正數。
如要套用這些自訂項目,請在 XML 資源中定義自訂樣式。
<resources>
<style name="pdfContainerStyle">
<item name="fastScrollVerticalThumbDrawable">@drawable/custom_thumb_drawable</item>
<item name="fastScrollPageIndicatorBackgroundDrawable">@drawable/custom_page_indicator_background</item>
<item name="fastScrollVerticalThumbMarginEnd">8dp</item>
</style>
</resources>
然後,使用 PdfViewerFragment.newInstance(stylingOptions) 建立片段執行個體時,請使用 PdfStylingOptions,將自訂樣式資源提供給 PdfViewerFragment。
private fun initializePdfViewerFragment() {
// This condition can be skipped if you want to create a new fragment every time.
if (pdfViewerFragment == null) {
val fragmentManager: FragmentManager = supportFragmentManager
// Create styling options.
val stylingOptions = PdfStylingOptions(R.style.pdfContainerStyle)
// Fragment initialization.
pdfViewerFragment = PdfViewerFragment.newInstance(stylingOptions)
// Execute fragment transaction.
}
}
如果您已將 PdfViewerFragment 子類別化,請使用受保護的建構函式提供樣式選項。確保自訂樣式正確套用至擴充片段。
class StyledPdfViewerFragment: PdfViewerFragment {
constructor() : super()
private constructor(pdfStylingOptions: PdfStylingOptions) : super(pdfStylingOptions)
companion object {
fun newInstance(): StyledPdfViewerFragment {
val stylingOptions = PdfStylingOptions(R.style.pdfContainerStyle)
return StyledPdfViewerFragment(stylingOptions)
}
}
}
完成導入程序
下列程式碼提供完整範例,說明如何在活動中實作 PdfViewerFragment,包括初始化、檔案挑選器整合、搜尋功能和 UI 自訂。
class MainActivity : AppCompatActivity() {
private var pdfViewerFragment: PdfViewerFragment? = null
private var filePicker: ActivityResultLauncher<String> =
registerForActivityResult(GetContent()) { uri: Uri? ->
uri?.let {
initializePdfViewerFragment()
pdfViewerFragment?.documentUri = uri
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (pdfViewerFragment == null) {
pdfViewerFragment =
supportFragmentManager
.findFragmentByTag(PDF_VIEWER_FRAGMENT_TAG) as PdfViewerFragment?
}
val getContentButton: MaterialButton = findViewById(R.id.launch_button)
val searchButton: MaterialButton = findViewById(R.id.search_button)
getContentButton.setOnClickListener { filePicker.launch(MIME_TYPE_PDF) }
searchButton.setOnClickListener {
pdfViewerFragment?.isTextSearchActive = pdfViewerFragment?.isTextSearchActive == false
}
}
private fun initializePdfViewerFragment() {
// This condition can be skipped if you want to create a new fragment every time.
if (pdfViewerFragment == null) {
val fragmentManager: FragmentManager = supportFragmentManager
// Create styling options.
// val stylingOptions = PdfStylingOptions(R.style.pdfContainerStyle)
// Fragment initialization.
// For customization:
// pdfViewerFragment = PdfViewerFragment.newInstance(stylingOptions)
pdfViewerFragment = PdfViewerFragmentExtended()
val transaction: FragmentTransaction = fragmentManager.beginTransaction()
// Replace an existing fragment in a container with an instance of a new fragment.
transaction.replace(
R.id.fragment_container_view,
pdfViewerFragment!!,
PDF_VIEWER_FRAGMENT_TAG
)
transaction.commitAllowingStateLoss()
fragmentManager.executePendingTransactions()
}
}
companion object {
private const val MIME_TYPE_PDF = "application/pdf"
private const val PDF_VIEWER_FRAGMENT_TAG = "pdf_viewer_fragment_tag"
}
}
程式碼重點
- 確認專案符合最低 API 級別和 SDK 擴充功能需求。
- 主辦
PdfViewerFragment的活動必須擴充AppCompatActivity。 - 您可以擴充
PdfViewerFragment,新增自訂行為。 - 如要自訂
PdfViewerFragment的 UI,請覆寫 XML 屬性。