PdfViewerFragment è un Fragment specializzato che puoi utilizzare per visualizzare documenti PDF all'interno della tua applicazione Android.
PdfViewerFragment semplifica il rendering dei PDF, consentendoti di concentrarti su
altri aspetti della funzionalità della tua app.
Risultati
Compatibilità delle versioni
Per utilizzare PdfViewerFragment, la tua applicazione deve avere come target almeno Android S
(livello API 31) e il livello di estensione SDK 13. Se questi requisiti di compatibilità non vengono soddisfatti, la libreria genera un UnsupportedOperationException.
Puoi controllare la versione dell'estensione SDK in fase di runtime utilizzando il modulo SdkExtensions. In questo modo, puoi caricare in modo condizionale il frammento e il documento PDF
solo se il dispositivo soddisfa i requisiti necessari.
if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
// Load the fragment and document.
}
Dipendenze
Per incorporare il visualizzatore PDF nella tua applicazione, dichiara la dipendenza
androidx.pdf nel file build.gradle del modulo dell'app. La libreria PDF è accessibile dal repository Maven di Google.
dependencies {
val pdfVersion = "1.0.0-alpha0X"
implementation("androidx.pdf:pdf:pdf-viewer-fragment:$pdfVersion")
}
PdfViewerFragment funzionalità
PdfViewerFragment presenta i documenti PDF in formato impaginato, rendendoli facili
da esplorare. Per un caricamento efficiente, il frammento utilizza una strategia di rendering a due passaggi che carica progressivamente le dimensioni della pagina.
Per ottimizzare l'utilizzo della memoria, PdfViewerFragment esegue il rendering solo delle pagine attualmente visibili e rilascia le bitmap per le pagine fuori dallo schermo.
Inoltre, PdfViewerFragment include un Floating Action Button (FAB) che
supporta le annotazioni attivando un intent implicito android.intent.action.ANNOTATE
contenente l'URI del documento.
Implementazione
L'aggiunta di un visualizzatore PDF all'applicazione Android è un processo in più passaggi.
Creare il layout dell'attività
Inizia definendo il layout XML per l'attività che ospita il visualizzatore PDF. Il
layout deve includere un FrameLayout per contenere i pulsanti PdfViewerFragment e
per le interazioni degli utenti, ad esempio la ricerca all'interno del documento.
<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>
Configurare l'attività
L'attività che ospita PdfViewerFragment deve estendere
AppCompatActivity. Nel metodo onCreate() dell'attività, imposta la visualizzazione dei contenuti sul layout che hai creato e inizializza gli elementi dell'interfaccia utente necessari.
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)
}
}
Inizializza PdfViewerFragment
Crea un'istanza di PdfViewerFragment utilizzando un fragment manager ottenuto da
getSupportFragmentManager(). Prima di crearne uno nuovo, controlla se esiste già un'istanza del frammento, soprattutto durante le modifiche alla configurazione.
Nell'esempio seguente, la funzione initializePdfViewerFragment() gestisce
la creazione e l'impegno della transazione di frammento. La funzione sostituisce
un frammento esistente in un contenitore con un'istanza del tuo
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"
}
}
Estendere la funzionalità di PdfViewerFragment
PdfViewerFragment espone funzioni pubbliche che puoi sostituire per estenderne le
funzionalità. Crea una nuova classe che eredita da PdfViewerFragment. Nella sottoclasse, esegui l'override di metodi come onLoadDocumentSuccess() e onLoadDocumentError() per aggiungere logica personalizzata, ad esempio la registrazione delle metriche.
@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)
}
}
Attivare la ricerca di documenti
Anche se PdfViewerFragment non include un menu di ricerca integrato, supporta una
barra di ricerca. Controlli la visibilità della barra di ricerca utilizzando l'API
isTextSearchActive. Per attivare la ricerca di documenti, imposta la proprietà
isTextSearchActive dell'istanza PdfViewerFragment.
Utilizza
WindowCompat.setDecorFitsSystemWindows()
per assicurarti che WindowInsetsCompat venga passato correttamente alle visualizzazioni dei contenuti, il che è
necessario per il corretto posizionamento della visualizzazione di ricerca.
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)
}
}
Integrare il selettore file
Per consentire agli utenti di selezionare file PDF dal proprio dispositivo, integra
PdfViewerFragment con il selettore di file Android. Per prima cosa, aggiorna il layout XML dell'attività in modo da includere un pulsante che avvii il selettore di file.
<...>
<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>
Successivamente, nell'attività, avvia il selettore di file utilizzando
registerForActivityResult(GetContent()). Quando
l'utente seleziona un file, il callback fornisce un URI. A questo punto, imposta la proprietà
documentUri dell'istanza PdfViewerFragment con questo URI per
caricare e visualizzare il PDF selezionato.
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"
// ...
}
}
Personalizzare l'interfaccia utente
Puoi personalizzare l'interfaccia utente di PdfViewerFragment sostituendo gli attributi XML
esposti dalla libreria. In questo modo puoi personalizzare l'aspetto di elementi come la barra di scorrimento e l'indicatore di pagina in modo che corrispondano al design della tua app.
Gli attributi personalizzabili includono:
fastScrollVerticalThumbDrawable: imposta il drawable per il cursore della barra di scorrimento.fastScrollPageIndicatorBackgroundDrawable: imposta la risorsa disegnabile di sfondo per l'indicatore di pagina.fastScrollPageIndicatorMarginEnd: imposta il margine destro per l'indicatore di pagina. Assicurati che i valori del margine siano positivi.fastScrollVerticalThumbMarginEnd: imposta il margine destro del cursore della barra di scorrimento verticale. Assicurati che i valori del margine siano positivi.
Per applicare queste personalizzazioni, definisci uno stile personalizzato nelle risorse 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>
Poi, fornisci la risorsa di stile personalizzato a PdfViewerFragment utilizzando
PdfStylingOptions quando crei un'istanza del frammento con
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.
}
}
Se hai creato una sottoclasse di PdfViewerFragment, utilizza il costruttore protetto per
fornire le opzioni di stile. In questo modo, gli stili personalizzati vengono applicati
correttamente allo snippet esteso.
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)
}
}
}
Implementazione completa
Il seguente codice fornisce un esempio completo di come implementare
PdfViewerFragment nella tua attività, inclusi l'inizializzazione, l'integrazione del selettore di file,
la funzionalità di ricerca e la personalizzazione dell'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"
}
}
Punti chiave del codice
- Assicurati che il tuo progetto soddisfi i requisiti minimi relativi al livello API e all'estensione SDK.
- L'attività di hosting
PdfViewerFragmentdeve estendereAppCompatActivity. - Puoi estendere
PdfViewerFragmentper aggiungere comportamenti personalizzati. - Personalizza la UI di
PdfViewerFragmentsostituendo gli attributi XML.