PdfViewerFragment é um Fragment especializado que você pode usar para mostrar documentos PDF no seu aplicativo Android.
O PdfViewerFragment simplifica a renderização de PDF, permitindo que você se concentre em
outros aspectos da funcionalidade do app.
Resultados
Compatibilidade de versões
Para usar PdfViewerFragment, seu aplicativo precisa ser direcionado a um mínimo de Android S
(nível 31 da API) e nível 13 da extensão do SDK. Se esses requisitos de compatibilidade
não forem atendidos, a biblioteca vai gerar um
UnsupportedOperationException.
É possível verificar a versão da extensão do SDK no momento da execução usando o módulo SdkExtensions. Isso permite carregar condicionalmente o fragmento e o documento PDF
somente se o dispositivo atender aos requisitos necessários.
if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
// Load the fragment and document.
}
Dependências
Para incorporar o visualizador de PDF ao seu aplicativo, declare a
dependência androidx.pdf no arquivo build.gradle
do módulo do app. A biblioteca PDF está acessível no repositório Maven do Google.
dependencies {
val pdfVersion = "1.0.0-alpha0X"
implementation("androidx.pdf:pdf:pdf-viewer-fragment:$pdfVersion")
}
Recursos PdfViewerFragment
O PdfViewerFragment apresenta documentos PDF em formato paginado, facilitando a navegação. Para um carregamento eficiente, o fragmento usa uma estratégia de renderização de duas passagens que carrega progressivamente as dimensões da página.
Para otimizar o uso da memória, o PdfViewerFragment renderiza apenas as páginas visíveis no momento
e libera os bitmaps das páginas que estão fora da tela.
Além disso, PdfViewerFragment inclui um botão de ação flutuante (FAB) que
oferece suporte a anotações ao acionar uma intent android.intent.action.ANNOTATE
implícita que contém o URI do documento.
Implementação
Adicionar um visualizador de PDF ao seu aplicativo Android é um processo de várias etapas.
Criar o layout da atividade
Comece definindo o XML de layout da atividade que hospeda o visualizador de PDF. O
layout precisa incluir um FrameLayout para conter o PdfViewerFragment e
botões para interações do usuário, como pesquisar no 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>
Configurar a atividade
A atividade que hospeda PdfViewerFragment precisa estender
AppCompatActivity. No método onCreate() da atividade, defina a visualização de conteúdo como o layout criado e inicialize os elementos de interface necessários.
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)
}
}
Inicializar o PdfViewerFragment
Crie uma instância de PdfViewerFragment usando um gerenciador de fragmentos obtido de
getSupportFragmentManager(). Verifique se uma
instância do fragmento já existe antes de criar uma nova, principalmente
durante mudanças de configuração.
No exemplo a seguir, a função initializePdfViewerFragment() processa
a criação e a confirmação da transação de fragmento. A função substitui
um fragmento existente em um contêiner por uma instância do seu
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"
}
}
Estender a funcionalidade PdfViewerFragment
O PdfViewerFragment expõe funções públicas que podem ser substituídas para estender os recursos dele. Crie uma classe que herde de PdfViewerFragment. Na sua
subclasse, substitua métodos como onLoadDocumentSuccess() e
onLoadDocumentError() para adicionar lógica personalizada, como métricas de registro.
@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)
}
}
Ativar a pesquisa de documentos
Embora o PdfViewerFragment não inclua um menu de pesquisa integrado, ele é compatível com uma barra de pesquisa. Você controla a visibilidade da barra de pesquisa usando a
API isTextSearchActive. Para ativar a pesquisa de documentos, defina a propriedade isTextSearchActive da instância PdfViewerFragment.
Use
WindowCompat.setDecorFitsSystemWindows()
para garantir que WindowInsetsCompat seja transmitido corretamente às visualizações de conteúdo, o que é
necessário para o posicionamento adequado da visualização de pesquisa.
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)
}
}
Integrar com o seletor de arquivos
Para permitir que os usuários selecionem arquivos PDF no dispositivo, integre
PdfViewerFragment ao seletor de arquivos do Android. Primeiro, atualize o XML de layout da atividade para incluir um botão que inicia o seletor de arquivos.
<...>
<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>
Em seguida, na sua atividade, inicie o seletor de arquivos usando
registerForActivityResult(GetContent()). Quando o usuário seleciona um arquivo, o callback fornece um URI. Em seguida, defina a propriedade
documentUri da sua instância PdfViewerFragment com esse URI para
carregar e mostrar o PDF selecionado.
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"
// ...
}
}
Personalizar a interface
É possível personalizar a interface do usuário de PdfViewerFragment substituindo atributos XML
que a biblioteca expõe. Isso permite personalizar a aparência de elementos como a barra de rolagem e o indicador de página para corresponder ao design do app.
Os atributos personalizáveis incluem:
fastScrollVerticalThumbDrawable: define o drawable para o marcador da barra de rolagem.fastScrollPageIndicatorBackgroundDrawable: define o elemento desenhável de plano de fundo para o indicador de página.fastScrollPageIndicatorMarginEnd: define a margem direita do indicador de página. Verifique se os valores de margem são positivos.fastScrollVerticalThumbMarginEnd: define a margem direita da alça da barra de rolagem vertical. Verifique se os valores de margem são positivos.
Para aplicar essas personalizações, defina um estilo personalizado nos seus recursos 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>
Em seguida, forneça o recurso de estilo personalizado a PdfViewerFragment usando
PdfStylingOptions ao criar uma instância do fragmento com
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 você criou uma subclasse de PdfViewerFragment, use o construtor protegido para
fornecer as opções de estilo. Isso garante que seus estilos personalizados sejam aplicados
corretamente ao fragmento estendido.
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)
}
}
}
Implementação completa
O código a seguir oferece um exemplo completo de como implementar
PdfViewerFragment na sua atividade, incluindo inicialização, integração do
seletor de arquivos, funcionalidade de pesquisa e personalização da interface.
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"
}
}
Pontos principais sobre o código
- Verifique se o projeto atende aos requisitos mínimos de nível da API e extensão do SDK.
- A atividade que hospeda
PdfViewerFragmentprecisa estenderAppCompatActivity. - Você pode estender
PdfViewerFragmentpara adicionar comportamentos personalizados. - Personalize a interface do
PdfViewerFragmentsubstituindo atributos XML.