PdfViewerFragment — это специализированный Fragment , который можно использовать для отображения PDF-документов в вашем Android-приложении. PdfViewerFragment упрощает рендеринг PDF-файлов, позволяя вам сосредоточиться на других аспектах функциональности вашего приложения.
Результаты

Совместимость версий
Для использования PdfViewerFragment ваше приложение должно быть ориентировано как минимум на Android S (уровень API 31) и иметь уровень расширения SDK 13. Если эти требования совместимости не выполняются, библиотека выдаст исключение UnsupportedOperationException .
Проверить версию расширения SDK во время выполнения можно с помощью модуля SdkExtensions . Это позволяет загружать фрагмент и PDF-документ только при условии, что устройство соответствует необходимым требованиям.
if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
// Load the fragment and document.
}
Зависимости
Чтобы интегрировать программу для просмотра PDF-файлов в ваше приложение, укажите зависимость androidx.pdf в файле build.gradle модуля вашего приложения. Библиотека для работы с PDF доступна в репозитории Google Maven.
dependencies {
val pdfVersion = "1.0.0-alpha0X"
implementation("androidx.pdf:pdf:pdf-viewer-fragment:$pdfVersion")
}
Функции PdfViewerFragment
PdfViewerFragment отображает PDF-документы в постраничном формате, что упрощает навигацию по ним. Для эффективной загрузки фрагмент использует двухпроходную стратегию рендеринга, которая постепенно загружает размеры страниц.
Для оптимизации использования памяти PdfViewerFragment отображает только видимые в данный момент страницы и освобождает растровые изображения для страниц, находящихся за пределами экрана. Кроме того, PdfViewerFragment включает плавающую кнопку действия (FAB), которая поддерживает аннотации, запуская неявный интент android.intent.action.ANNOTATE , содержащий URI документа.
Выполнение
Добавление программы для просмотра PDF-файлов в ваше Android-приложение — это многоэтапный процесс.
Создайте макет действия
Для начала определите XML-макет для активности, которая содержит средство просмотра PDF-файлов. Макет должен включать 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
Создайте экземпляр PdfViewerFragment , используя менеджер фрагментов, полученный из getSupportFragmentManager() . Перед созданием нового экземпляра проверьте, существует ли уже существующий, особенно при изменении конфигурации.
В следующем примере функция 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 не имеет встроенного меню поиска, он поддерживает строку поиска. Вы управляете видимостью строки поиска с помощью API isTextSearchActive . Чтобы включить поиск по документу, необходимо установить свойство isTextSearchActive экземпляра PdfViewerFragment .
Используйте 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-файлы со своих устройств, интегрируйте PdfViewerFragment с Android-файловым виджетом. Сначала обновите XML-файл разметки вашего Activity, добавив кнопку, которая запускает файловый виджет.
<androidx.constraintlayout.widget.ConstraintLayout
...
tools:context=".MainActivity">
<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>
Далее, в вашем Activity запустите средство выбора файлов, используя registerForActivityResult(GetContent()) . Когда пользователь выберет файл, функция обратного вызова предоставит URI. Затем вы устанавливаете свойство documentUri вашего экземпляра PdfViewerFragment с этим URI, чтобы загрузить и отобразить выбранный 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"
// ...
}
}
Настройте пользовательский интерфейс
Вы можете настроить пользовательский интерфейс PdfViewerFragment , переопределив XML-атрибуты, предоставляемые библиотекой. Это позволяет адаптировать внешний вид таких элементов, как полоса прокрутки и индикатор страницы, в соответствии с дизайном вашего приложения.
К числу настраиваемых атрибутов относятся:
-
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) , укажите пользовательский ресурс стиля для PdfViewerFragment , используя PdfStylingOptions .
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 в вашей активности, включая инициализацию, интеграцию средства выбора файлов, функцию поиска и настройку пользовательского интерфейса.
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, переопределив XML-атрибуты.