PdfViewerFragment est un Fragment spécialisé que vous pouvez utiliser pour afficher des documents PDF dans votre application Android.
PdfViewerFragment simplifie le rendu des PDF, ce qui vous permet de vous concentrer sur d'autres aspects des fonctionnalités de votre application.
Résultats
Compatibilité des versions
Pour utiliser PdfViewerFragment, votre application doit cibler au minimum Android S (niveau d'API 31) et le niveau d'extension du SDK 13. Si ces exigences de compatibilité ne sont pas respectées, la bibliothèque génère une erreur UnsupportedOperationException.
Vous pouvez vérifier la version de l'extension SDK au moment de l'exécution à l'aide du module SdkExtensions. Cela vous permet de charger conditionnellement le fragment et le document PDF uniquement si l'appareil répond aux exigences nécessaires.
if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
// Load the fragment and document.
}
Dépendances
Pour intégrer le lecteur PDF à votre application, déclarez la dépendance androidx.pdf dans le fichier build.gradle du module de votre application. La bibliothèque PDF est accessible depuis le dépôt Maven de Google.
dependencies {
val pdfVersion = "1.0.0-alpha0X"
implementation("androidx.pdf:pdf:pdf-viewer-fragment:$pdfVersion")
}
Fonctionnalités PdfViewerFragment
PdfViewerFragment présente les documents PDF au format paginé, ce qui facilite la navigation. Pour un chargement efficace, le fragment utilise une stratégie de rendu en deux passes qui charge progressivement les dimensions de la page.
Pour optimiser l'utilisation de la mémoire, PdfViewerFragment n'affiche que les pages actuellement visibles et libère les bitmaps pour les pages hors écran.
De plus, PdfViewerFragment inclut un bouton d'action flottant (FAB) qui prend en charge les annotations en déclenchant un intent android.intent.action.ANNOTATE implicite contenant l'URI du document.
Implémentation
L'ajout d'un lecteur PDF à votre application Android est un processus en plusieurs étapes.
Créer la mise en page de l'activité
Commencez par définir la mise en page XML de l'activité qui héberge le lecteur PDF. La mise en page doit inclure un FrameLayout pour contenir les boutons PdfViewerFragment et pour les interactions utilisateur, comme la recherche dans le document.
<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>
Configurer l'activité
L'activité qui héberge PdfViewerFragment doit étendre AppCompatActivity. Dans la méthode onCreate() de l'activité, définissez la vue de contenu sur la mise en page que vous avez créée et initialisez tous les éléments d'UI nécessaires.
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)
}
}
Initialiser PdfViewerFragment
Créez une instance de PdfViewerFragment à l'aide d'un gestionnaire de fragments obtenu à partir de getSupportFragmentManager(). Vérifiez si une instance du fragment existe déjà avant d'en créer une, en particulier lors des changements de configuration.
Dans l'exemple suivant, la fonction initializePdfViewerFragment() gère la création et l'engagement de la transaction de fragment. La fonction remplace un fragment existant dans un conteneur par une instance de votre 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"
}
}
Étendre les fonctionnalités de PdfViewerFragment
PdfViewerFragment expose des fonctions publiques que vous pouvez remplacer pour étendre ses capacités. Créez une classe qui hérite de PdfViewerFragment. Dans votre sous-classe, remplacez des méthodes telles que onLoadDocumentSuccess() et onLoadDocumentError() pour ajouter une logique personnalisée, comme l'enregistrement de métriques.
@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)
}
}
Activer la recherche de documents
Bien que PdfViewerFragment n'inclue pas de menu de recherche intégré, il est compatible avec une barre de recherche. Vous contrôlez la visibilité de la barre de recherche à l'aide de l'API isTextSearchActive. Pour activer la recherche de documents, vous devez définir la propriété isTextSearchActive de votre instance PdfViewerFragment.
Utilisez WindowCompat.setDecorFitsSystemWindows() pour vous assurer que WindowInsetsCompat est correctement transmis aux vues de contenu, ce qui est nécessaire pour positionner correctement la vue de recherche.
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)
}
}
Intégrer au sélecteur de fichier
Pour permettre aux utilisateurs de sélectionner des fichiers PDF sur leur appareil, intégrez PdfViewerFragment au sélecteur de fichiers Android. Commencez par mettre à jour le fichier XML de mise en page de votre activité pour inclure un bouton qui lance le sélecteur de fichiers.
<...>
<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>
Ensuite, dans votre activité, lancez le sélecteur de fichiers à l'aide de registerForActivityResult(GetContent()). Lorsque l'utilisateur sélectionne un fichier, le rappel fournit un URI. Vous définissez ensuite la propriété documentUri de votre instance PdfViewerFragment avec cet URI pour charger et afficher le PDF sélectionné.
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"
// ...
}
}
Personnaliser l'UI
Vous pouvez personnaliser l'interface utilisateur de PdfViewerFragment en remplaçant les attributs XML exposés par la bibliothèque. Cela vous permet d'adapter l'apparence d'éléments tels que la barre de défilement et l'indicateur de page pour qu'ils correspondent à la conception de votre application.
Voici les attributs personnalisables :
fastScrollVerticalThumbDrawable: définit le drawable pour le pouce de la barre de défilement.fastScrollPageIndicatorBackgroundDrawable: définit l'arrière-plan drawable de l'indicateur de page.fastScrollPageIndicatorMarginEnd: définit la marge de droite de l'indicateur de page. Assurez-vous que les valeurs de marge sont positives.fastScrollVerticalThumbMarginEnd: définit la marge de droite du curseur de la barre de défilement verticale. Assurez-vous que les valeurs de marge sont positives.
Pour appliquer ces personnalisations, définissez un style personnalisé dans vos ressources 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>
Ensuite, fournissez la ressource de style personnalisé à PdfViewerFragment à l'aide de PdfStylingOptions lorsque vous créez une instance du fragment avec 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.
}
}
Si vous avez créé une sous-classe de PdfViewerFragment, utilisez le constructeur protégé pour fournir les options de style. Cela garantit que vos styles personnalisés sont correctement appliqués à votre fragment étendu.
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)
}
}
}
Implémentation complète
Le code suivant fournit un exemple complet d'implémentation de PdfViewerFragment dans votre activité, y compris l'initialisation, l'intégration du sélecteur de fichiers, la fonctionnalité de recherche et la personnalisation de l'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"
}
}
Points clés concernant le code
- Assurez-vous que votre projet répond aux exigences minimales concernant le niveau d'API et l'extension du SDK.
- L'activité d'hébergement
PdfViewerFragmentdoit étendreAppCompatActivity. - Vous pouvez étendre
PdfViewerFragmentpour ajouter des comportements personnalisés. - Personnalisez l'UI de
PdfViewerFragmenten remplaçant les attributs XML.