PDF görüntüleyici uygulama

PdfViewerFragment, Android uygulamanızda PDF dokümanlarını görüntülemek için kullanabileceğiniz özel bir Fragment'dir. PdfViewerFragment, PDF oluşturmayı basitleştirerek uygulamanızın işlevselliğinin diğer yönlerine odaklanmanızı sağlar.

Sonuçlar

PdfViewerFragment kullanılarak bir Android uygulamasında oluşturulan PDF belgesi.
Bir uygulamada görüntülenen PDF dokümanı.

Sürüm uyumluluğu

PdfViewerFragment özelliğini kullanmak için uygulamanızın en az Android S (API düzeyi 31) ve SDK uzantı düzeyi 13'ü hedeflemesi gerekir. Bu uyumluluk koşulları karşılanmazsa kitaplık UnsupportedOperationException oluşturur.

SDK uzantısı sürümünü çalışma zamanında SdkExtensions modülünü kullanarak kontrol edebilirsiniz. Bu sayede, cihaz gerekli koşulları karşılıyorsa yalnızca koşullu olarak parçayı ve PDF belgesini yükleyebilirsiniz.

if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
    // Load the fragment and document.
}

Bağımlılıklar

PDF görüntüleyiciyi uygulamanıza dahil etmek için uygulamanızın modül build.gradle dosyasında androidx.pdf bağımlılığını beyan edin. PDF kitaplığına Google Maven deposundan erişilebilir.

dependencies {
    val pdfVersion = "1.0.0-alpha0X"
    implementation("androidx.pdf:pdf:pdf-viewer-fragment:$pdfVersion")
}

PdfViewerFragment özellik

PdfViewerFragment, PDF belgelerini sayfalandırılmış biçimde sunarak gezinmeyi kolaylaştırır. Parça, verimli yükleme için sayfa boyutlarını kademeli olarak yükleyen iki geçişli bir oluşturma stratejisi kullanır.

PdfViewerFragment, bellek kullanımını optimize etmek için yalnızca şu anda görünür olan sayfaları oluşturur ve ekran dışında kalan sayfaların bit eşlemlerini serbest bırakır. Ayrıca, PdfViewerFragment, doküman URI'sini içeren örtülü bir android.intent.action.ANNOTATE amacı tetikleyerek ek açıklamaları destekleyen bir kayan işlem düğmesi (FAB) içerir.

Uygulama

Android uygulamanıza PDF görüntüleyici eklemek çok adımlı bir işlemdir.

Etkinlik düzenini oluşturma

PDF görüntüleyiciyi barındıran etkinliğin düzen XML'sini tanımlayarak başlayın. Düzen, PdfViewerFragment ve kullanıcı etkileşimleri için düğmeleri (ör. belge içinde arama) içeren bir FrameLayout içermelidir.

<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>

Etkinliği ayarlama

PdfViewerFragment öğesini barındıran etkinlik, AppCompatActivity öğesini genişletmelidir. Etkinliğin onCreate() yönteminde, içerik görünümünü oluşturduğunuz düzene ayarlayın ve gerekli kullanıcı arayüzü öğelerini başlatın.

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 başlatılıyor

getSupportFragmentManager() öğesinden alınan bir parça yöneticisini kullanarak PdfViewerFragment örneği oluşturun. Yeni bir parça oluşturmadan önce, özellikle yapılandırma değişiklikleri sırasında, parçanın bir örneğinin zaten mevcut olup olmadığını kontrol edin.

Aşağıdaki örnekte, initializePdfViewerFragment() işlevi parça işleminin oluşturulmasını ve işlenmesini sağlar. Bu işlev, kapsayıcıdaki mevcut bir parçayı PdfViewerFragment örneğinizle değiştirir.

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 işlevini genişletme

PdfViewerFragment, özelliklerini genişletmek için geçersiz kılınabilecek herkese açık işlevler sunar. PdfViewerFragment öğesinden devralan yeni bir sınıf oluşturun. Alt sınıfınızda, metrikleri kaydetme gibi özel mantık eklemek için onLoadDocumentSuccess() ve onLoadDocumentError() gibi yöntemleri geçersiz kılın.

@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 yerleşik bir arama menüsü içermese de arama çubuğunu destekler. Arama çubuğunun görünürlüğünü isTextSearchActive API'siyle kontrol edersiniz. Doküman aramasını etkinleştirmek için PdfViewerFragment örneğinizin isTextSearchActive özelliğini ayarlarsınız.

Arama görünümünün doğru konumlandırılması için gerekli olan WindowInsetsCompat öğesinin içerik görünümlerine doğru şekilde iletildiğinden emin olmak için WindowCompat.setDecorFitsSystemWindows() kullanın.

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)
    }
}

Dosya seçiciyle entegrasyon

Kullanıcıların cihazlarından PDF dosyaları seçmesine izin vermek için PdfViewerFragment'yı Android dosya seçiciyle entegre edin. Öncelikle, dosya seçiciyi başlatan bir düğme eklemek için etkinliğinizin düzen XML'sini güncelleyin.

<...>
    <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>

Ardından, etkinliğinizde registerForActivityResult(GetContent()) kullanarak dosya seçiciyi başlatın. Kullanıcı bir dosya seçtiğinde geri çağırma, URI sağlar. Ardından, seçilen PDF'yi yüklemek ve görüntülemek için PdfViewerFragment örneğinizin documentUri özelliğini bu URI ile ayarlarsınız.

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"
        // ...
    }
}

Kullanıcı arayüzünü özelleştirme

Kitaplığın sunduğu XML özelliklerini geçersiz kılarak PdfViewerFragment kullanıcı arayüzünü özelleştirebilirsiniz. Bu sayede, kaydırma çubuğu ve sayfa göstergesi gibi öğelerin görünümünü uygulamanızın tasarımına uyacak şekilde özelleştirebilirsiniz.

Özelleştirilebilen özellikler şunlardır:

  • fastScrollVerticalThumbDrawable: Kaydırma çubuğu başparmağı için çizilebilir öğeyi ayarlar.
  • fastScrollPageIndicatorBackgroundDrawable — Sayfa göstergesi için arka plan çizilebilir öğesini ayarlar.
  • fastScrollPageIndicatorMarginEnd: Sayfa göstergesi için sağ kenar boşluğunu ayarlar. Marj değerlerinin pozitif olduğundan emin olun.
  • fastScrollVerticalThumbMarginEnd: Dikey kaydırma çubuğu başparmağı için sağ kenar boşluğunu ayarlar. Marj değerlerinin pozitif olduğundan emin olun.

Bu özelleştirmeleri uygulamak için XML kaynaklarınızda özel bir stil tanımlayın.

<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>

Ardından, PdfViewerFragment kullanarak özel stil kaynağını sağlayın. PdfStylingOptions ile parçanın bir örneğini oluşturduğunuzda PdfViewerFragment.newInstance(stylingOptions) kullanın.

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 sınıfını alt sınıfa ayırdıysanız stil seçeneklerini sağlamak için protected oluşturucuyu kullanın. Bu, özel stillerinizin genişletilmiş parçanıza doğru şekilde uygulanmasını sağlar.

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)
        }
    }
}

Uygulamayı tamamlama

Aşağıdaki kod, başlatma, dosya seçici entegrasyonu, arama işlevi ve kullanıcı arayüzü özelleştirme dahil olmak üzere PdfViewerFragment öğesini etkinliğinizde nasıl uygulayacağınızla ilgili eksiksiz bir örnek sunar.

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"
    }
}

Kodla ilgili önemli noktalar

  • Projenizin minimum API düzeyi ve SDK uzantısı şartlarını karşıladığından emin olun.
  • Etkinliğe ev sahipliği yapan PdfViewerFragment, AppCompatActivity öğesini genişletmelidir.
  • Özel davranışlar eklemek için PdfViewerFragment öğesini genişletebilirsiniz.
  • XML özelliklerini geçersiz kılarak PdfViewerFragment kullanıcı arayüzünü özelleştirin.