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-код макета вашей активности, добавив кнопку для запуска инструмента выбора файлов.
<...>
<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 свойству documentUri вашего экземпляра PdfViewerFragment для загрузки и отображения выбранного 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 с помощью PdfStylingOptions при создании экземпляра фрагмента с помощью PdfViewerFragment.newInstance(stylingOptions) .
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.