---
title: "Overlay Views"
canonical_url: "https://www.nutrient.io/guides/android/samples/overlay-views-kotlin/"
md_url: "https://www.nutrient.io/guides/android/samples/overlay-views-kotlin.md"
last_updated: "2026-05-15T19:10:04.916Z"
description: "Add custom Android Views over PDF pages with OverlayViewProvider."
---

# Overlay Views

Add custom Android Views over PDF pages with OverlayViewProvider.

[Get Started](https://www.nutrient.io/sdk/android/getting-started.md)

[All Samples](https://www.nutrient.io/guides/android/samples.md)

[Download](https://www.nutrient.io/guides/android/downloads.md)

[Launch Demo](https://www.nutrient.io/demo/)

---

```kotlin

/*
 *   Copyright © 2019-2026 PSPDFKit GmbH. All rights reserved.
 *
 *   The PSPDFKit Sample applications are licensed with a modified BSD license.
 *   Please see License for details. This notice may not be removed from this file.
 */

package com.pspdfkit.catalog.examples.kotlin

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Color
import android.graphics.Matrix
import android.graphics.PointF
import android.graphics.RectF
import android.net.Uri
import android.os.Bundle
import android.util.TypedValue
import android.view.MotionEvent
import android.view.View
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.TextView
import androidx.activity.viewModels
import com.pspdfkit.annotations.Annotation
import com.pspdfkit.annotations.AnnotationType
import com.pspdfkit.annotations.FreeTextAnnotation
import com.pspdfkit.annotations.StampAnnotation
import com.pspdfkit.annotations.appearance.AssetAppearanceStreamGenerator
import com.pspdfkit.catalog.R
import com.pspdfkit.catalog.SdkExample
import com.pspdfkit.catalog.tasks.ExtractAssetTask
import com.pspdfkit.configuration.activity.PdfActivityConfiguration
import com.pspdfkit.document.PdfDocument
import com.pspdfkit.ui.PdfActivity
import com.pspdfkit.ui.PdfActivityIntentBuilder
import com.pspdfkit.ui.PdfFragment
import com.pspdfkit.ui.overlay.OverlayLayoutParams
import com.pspdfkit.ui.overlay.OverlayViewProvider
import kotlinx.coroutines.runBlocking
import kotlin.getValue

class OverlayViewsExample(context: Context) : SdkExample(context, R.string.overlayViewsExample, R.string.overlayViewsExampleDescription) {
    override fun launchExample(context: Context, configuration: PdfActivityConfiguration.Builder) {
        // We use a custom utility class to extract the example document from the assets.
        ExtractAssetTask.extract(WELCOME_DOC, title, context, true) { documentFile ->
            val intent =
                PdfActivityIntentBuilder.fromUri(context, Uri.fromFile(documentFile)).configuration(
                        configuration
                            // We disable annotation editing to keep the example focused..editableAnnotationTypes(listOf(AnnotationType.NONE)).build(),
                    ).activityClass(OverlayViewsActivity::class).build()

            // Start the OverlayViewsActivity for the extracted document.
            context.startActivity(intent)
        }
    }
}

// Suppress warnings about experimental classes.
class OverlayViewsActivity : PdfActivity() {
    private val viewModel: AnnotationCreationViewModel by viewModels()

    private lateinit var viewProvider: MyViewProvider

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        viewProvider = MyViewProvider(this, requirePdfFragment(), savedInstanceState)
        // Add our view provider to the fragment.
        requirePdfFragment().addOverlayViewProvider(viewProvider)
    }

    override fun onDocumentLoaded(document: PdfDocument) {
        super.onDocumentLoaded(document)

        viewModel.createObjects {
            // We add a simple annotation to explain to users what to do.
            val clickHereAnnotation =
                FreeTextAnnotation(
                    0,
                    RectF(50f, 600f, 350f, 400f),
                    "Tap Anywhere on The Page",
                )
            clickHereAnnotation.textSize = 48f
            clickHereAnnotation.color = Color.RED

            runBlocking { document.annotationProvider.addAnnotationToPage(clickHereAnnotation) }

            // Add a stamp with a custom AP stream that overlaps the overlay view for z-order testing.
            val stampAnnotation =
                StampAnnotation(
                    0,
                    RectF(320f, 460f, 520f, 300f),
                    "Overlay Order Stamp",
                ).apply {
                    appearanceStreamGenerator = AssetAppearanceStreamGenerator("images/Nutrient_Logo.pdf")
                }
            runBlocking { document.annotationProvider.addAnnotationToPage(stampAnnotation) }
        }
    }

    override fun onPageClick(
        document: PdfDocument,
        pageIndex: Int,
        event: MotionEvent?,
        pagePosition: PointF?,
        clickedAnnotation: Annotation?,
    ): Boolean {
        if (pageIndex == 0) {
            // User tapped on first page, toggle overlay ordering.
            viewProvider.toggleAnnotationOverlayOrder()
            return true
        }
        if (pageIndex == 1) {
            // User tapped on second page, add our overlay views.
            viewProvider.areSecondPageViewsVisible =!viewProvider.areSecondPageViewsVisible
            return true
        }

        return super.onPageClick(document, pageIndex, event, pagePosition, clickedAnnotation)
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        // In order to remember what the user entered we have to save it here.
        viewProvider.saveState(outState)
    }
}

/** A custom OverlayViewProvider that will display views on the first and second. */
@SuppressLint("SetTextI18n", "SetJavaScriptEnabled")
class MyViewProvider(context: Context, private val pdfFragment: PdfFragment, savedInstanceState: Bundle?) : OverlayViewProvider() {
    companion object {
        private const val STATE_FIRST_PAGE_CLICKED = "MyOverlayProvider.FirstPageClicked"
        private const val STATE_SECOND_PAGE_CLICKED = "MyOverlayProvider.SecondPageClicked"
        private const val STATE_ANNOTATION_OVERLAY_ON_TOP = "MyOverlayProvider.AnnotationOverlayOnTop"

        private const val TEXT_SIZE = 24f
    }

    private val firstPageView: TextView
    private val secondPageView: WebView

    private val pageToViewTransformation = Matrix()

    /** This indicates whether our other form views are visible or not. */
    var areFirstPageViewsVisible = true
        set(value) {
            field = value
            notifyOverlayViewsChanged()
        }

    /** This indicates whether our other form views are visible or not. */
    var areSecondPageViewsVisible = false
        set(value) {
            field = value
            notifyOverlayViewsChanged()
        }

    private var isAnnotationOverlayAboveOverlayViews = false
        set(value) {
            field = value
            pdfFragment.isAnnotationOverlayAboveOverlayViews = value
            updateFirstPageText()
            notifyOverlayViewsChanged()
        }

    init {
        // If you need a static set of views pre creating them is the most efficient way to go.
        firstPageView = TextView(context)
        updateFirstPageText()
        firstPageView.setBackgroundColor(Color.WHITE)
        firstPageView.setOnClickListener {
            isAnnotationOverlayAboveOverlayViews =!isAnnotationOverlayAboveOverlayViews
        }
        // We put this view next to our freetext annotation.

        // The rect are the PDF coordinates on the page where our TextView should be.
        firstPageView.layoutParams =
            OverlayLayoutParams(
                RectF(250f, 500f, 668f, 300f),
                // We use SizingMode.SCALING here, this has the effect that the view will only be measured once and then a scale will be applied to it.
                // This means that the text size will automatically scale up as the page is zoomed.
                OverlayLayoutParams.SizingMode.SCALING,
            )

        // You can embed any kind of view, even a WebView.
        secondPageView = WebView(context)
        secondPageView.settings.javaScriptEnabled = true
        // Set a WebViewClient so navigation stays within the WebView instead of opening the system browser.
        secondPageView.webViewClient = WebViewClient()
        // Load the Nutrient homepage.
        secondPageView.loadUrl("https://nutrient.io/")
        // We fill the entire page, the Webview will consume all scroll events so scrolling the page will only work in the margins.
        secondPageView.layoutParams =
            OverlayLayoutParams(
                RectF(0f, 1024f, 768f, 0f),
                // We use the SizingMode.LAYOUT here since we want the WebView to actually increase the available size if the page is zoomed in.
                OverlayLayoutParams.SizingMode.LAYOUT,
            )

        if (savedInstanceState!= null) {
            // If we have a saved state we restore it here.
            areFirstPageViewsVisible = savedInstanceState.getBoolean(STATE_FIRST_PAGE_CLICKED)
            areSecondPageViewsVisible = savedInstanceState.getBoolean(STATE_SECOND_PAGE_CLICKED)
            isAnnotationOverlayAboveOverlayViews = savedInstanceState.getBoolean(STATE_ANNOTATION_OVERLAY_ON_TOP)
        }
    }

    // We're only putting views on Page 13 containing the form elements.
    override fun getPagesWithViews(): Set<Int> = setOf(0, 1)

    // This method is called for all pages returned by getFilteredPages().
    // You simply return a list of all views for the page and they will be displayed.
    override fun getViewsForPage(context: Context, document: PdfDocument, pageIndex: Int): List<View> {
        val views = mutableListOf<View>()

        if (areFirstPageViewsVisible && pageIndex == 0) {
            // If the user clicked on the first page we add our text view to the list of views.
            // This way it will be overlaid on the document.

            // We update the text size so it stays consistent no matter the device size.
            val matrix = pdfFragment.viewProjection.getPageToViewTransformation(pageIndex, pageToViewTransformation)
            firstPageView.setTextSize(TypedValue.COMPLEX_UNIT_PX, convertPdfPointsToPixel(TEXT_SIZE, pageIndex, matrix))
            views.add(firstPageView)
        }

        if (areSecondPageViewsVisible && pageIndex == 1) {
            views.add(secondPageView)
        }

        return views
    }

    override fun onViewsRecycled(pageIndex: Int, views: List<View>) {
        // This is called for every page with the views for the page that were recycled.
        // If you are pooling your views for efficient reuse this is good place to return them to the pool.
    }

    override fun onViewsShown(pageIndex: Int, views: List<View>) {
        // This is called for every page with the views for the page that are now visible because the hosting page is now visible.
    }

    override fun onViewsHidden(pageIndex: Int, views: List<View>) {
        // This is called for every page with the views for the page that are no longer visible because the hosting page is no longer visible.
    }

    fun saveState(outState: Bundle) {
        // In our example we simply store whatever the user entered.
        outState.putBoolean(STATE_FIRST_PAGE_CLICKED, areFirstPageViewsVisible)
        outState.putBoolean(STATE_SECOND_PAGE_CLICKED, areSecondPageViewsVisible)
        outState.putBoolean(STATE_ANNOTATION_OVERLAY_ON_TOP, isAnnotationOverlayAboveOverlayViews)
    }

    private fun updateFirstPageText() {
        val orderLabel =
            if (isAnnotationOverlayAboveOverlayViews) {
                "Annotation overlay above overlay views"
            } else {
                "Overlay views above annotation overlay"
            }
        firstPageView.text = "\uD83D\uDC4B I'm a custom overlay view!\nTap me or the page to toggle order.\nNow: $orderLabel"
    }

    fun toggleAnnotationOverlayOrder() {
        isAnnotationOverlayAboveOverlayViews =!isAnnotationOverlayAboveOverlayViews
    }

    /** This method converts the given pdf points to pixels for use in our text size. */
    private fun convertPdfPointsToPixel(pdfPoints: Float, pageIndex: Int, pageToViewTransformation: Matrix): Float {
        val f = FloatArray(9)
        pageToViewTransformation.getValues(f)

        // We multiply by the scale value stored in the matrix, but divide by the zoomscale so the result doesn't change as we zoom in.
        return (pdfPoints * f[Matrix.MSCALE_X]) / pdfFragment.getZoomScale(pageIndex)
    }
}

```

This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.

---

## Related pages

- [Application Policy](/guides/android/samples/application-policy-kotlin.md)
- [Custom Form Highlight Color](/guides/android/samples/custom-form-highlight-color-java.md)
- [Custom Page Templates](/guides/android/samples/custom-page-templates-java.md)
- [Digital Signature (Basic)](/guides/android/samples/digital-signature-basic-kotlin.md)
- [Disabled Annotation Property](/guides/android/samples/disabled-annotation-property-java.md)
- [Image Document](/guides/android/samples/image-document-kotlin.md)
- [Compose Image Document](/guides/android/samples/compose-image-document-kotlin.md)
- [Inline Multimedia](/guides/android/samples/inline-multimedia-kotlin.md)
- [JavaScript Form Filling](/guides/android/samples/javascript-form-filling-kotlin.md)
- [Overlay Visibility](/guides/android/samples/overlay-visibility-kotlin.md)
- [PdfFragment](/guides/android/samples/pdffragment-kotlin.md)
- [Reader View](/guides/android/samples/reader-view-kotlin.md)
- [Playground](/guides/android/samples/playground-kotlin.md)
- [JavaScript Calculator](/guides/android/samples/javascript-calculator-kotlin.md)
- [Text Field Suggestions](/guides/android/samples/text-field-suggestions-kotlin.md)
- [Thumbnail Bar Modes](/guides/android/samples/thumbnail-bar-modes-kotlin.md)
- [Signature Storage Database](/guides/android/samples/signature-storage-database-kotlin.md)
- [Selection Customization](/guides/android/samples/selection-customization-java.md)
- [Password Protected PDF](/guides/android/samples/password-protected-pdf-kotlin.md)
- [Scientific Paper](/guides/android/samples/scientific-paper-kotlin.md)
- [Try Instant](/guides/android/samples/try-instant-kotlin.md)
- [Merge Documents](/guides/android/samples/merge-documents-kotlin.md)
- [Annotation Rendering](/guides/android/samples/annotation-rendering-kotlin.md)
- [Custom Data Provider](/guides/android/samples/custom-data-provider-kotlin.md)
- [Annotations with Transparency](/guides/android/samples/annotations-with-transparency-kotlin.md)
- [Annotation Flags](/guides/android/samples/annotation-flags-kotlin.md)
- [AI Assistant (Multiple Documents, ViewPager)](/guides/android/samples/ai-assistant-multiple-documents-viewpager-kotlin.md)
- [Custom Sharing Menu](/guides/android/samples/custom-sharing-menu-java.md)
- [Add LTV to Existing Signature](/guides/android/samples/add-ltv-to-existing-signature-kotlin.md)
- [Custom Toolbar Grouping](/guides/android/samples/custom-toolbar-grouping-java.md)
- [Custom Layout](/guides/android/samples/custom-layout-kotlin.md)
- [Custom ActionBar Actions](/guides/android/samples/custom-actionbar-actions-kotlin.md)
- [Custom Activity Toolbars](/guides/android/samples/custom-activity-toolbars-java.md)
- [Custom Note Hinter](/guides/android/samples/custom-note-hinter-kotlin.md)
- [Custom Main Toolbar](/guides/android/samples/custom-main-toolbar-kotlin.md)
- [Annotation Configuration](/guides/android/samples/annotation-configuration-kotlin.md)
- [Annotation Selection Styling](/guides/android/samples/annotation-selection-styling-kotlin.md)
- [Custom Search UI (Compose)](/guides/android/samples/custom-search-ui-compose-kotlin.md)
- [Document Switcher](/guides/android/samples/document-switcher-java.md)
- [File Annotation Creation](/guides/android/samples/file-annotation-creation-kotlin.md)
- [Dynamic Pages on Scroll](/guides/android/samples/dynamic-pages-on-scroll-kotlin.md)
- [Custom Activity Form Editing](/guides/android/samples/custom-activity-form-editing-java.md)
- [Custom Stamp Annotations](/guides/android/samples/custom-stamp-annotations-java.md)
- [Custom Outline Provider](/guides/android/samples/custom-outline-provider-kotlin.md)
- [Compose Navigation](/guides/android/samples/compose-navigation-kotlin.md)
- [Fragment Runtime Configuration](/guides/android/samples/fragment-runtime-configuration-kotlin.md)
- [Annotation Overlay](/guides/android/samples/annotation-overlay-java.md)
- [Instant Document JSON](/guides/android/samples/instant-document-json-kotlin.md)
- [DocumentView Composable](/guides/android/samples/documentview-composable-kotlin.md)
- [Form Creation](/guides/android/samples/form-creation-kotlin.md)
- [Document Download](/guides/android/samples/document-download-kotlin.md)
- [JavaScript Actions](/guides/android/samples/javascript-actions-kotlin.md)
- [Instant JSON Attachment](/guides/android/samples/instant-json-attachment-kotlin.md)
- [Digital Signature (Manual)](/guides/android/samples/digital-signature-manual-kotlin.md)
- [Digital Signature (Third-Party)](/guides/android/samples/digital-signature-third-party-kotlin.md)
- [Inline Search](/guides/android/samples/inline-search-java.md)
- [Form Filling](/guides/android/samples/form-filling-kotlin.md)
- [Form Click Intercept (Compose)](/guides/android/samples/form-click-intercept-compose-kotlin.md)
- [Document Sharing](/guides/android/samples/document-sharing-java.md)
- [Custom Download Dialog](/guides/android/samples/custom-download-dialog-java.md)
- [Download Progress](/guides/android/samples/download-progress-kotlin.md)
- [Popup Toolbar Customization](/guides/android/samples/popup-toolbar-customization-kotlin.md)
- [Custom Sharing Dialog](/guides/android/samples/custom-sharing-dialog-java.md)
- [PDF from Image](/guides/android/samples/pdf-from-image-kotlin.md)
- [Digital Signature (Two-Step)](/guides/android/samples/digital-signature-two-step-kotlin.md)
- [Remote URL](/guides/android/samples/remote-url-kotlin.md)
- [PdfUiFragment](/guides/android/samples/pdfuifragment-kotlin.md)
- [Runtime Configuration](/guides/android/samples/runtime-configuration-kotlin.md)
- [Sound Extraction](/guides/android/samples/sound-extraction-kotlin.md)
- [Document from Canvas](/guides/android/samples/document-from-canvas-kotlin.md)
- [Tabbed Documents](/guides/android/samples/tabbed-documents-kotlin.md)
- [Watermarks](/guides/android/samples/watermarks-kotlin.md)
- [Programmatic Zoom](/guides/android/samples/programmatic-zoom-kotlin.md)
- [Signature Parcelize](/guides/android/samples/signature-parcelize-kotlin.md)
- [OCR](/guides/android/samples/ocr-kotlin.md)
- [Vertical Scrollbar](/guides/android/samples/vertical-scrollbar-java.md)
- [Split View](/guides/android/samples/split-view-java.md)
- [XFDF Import/Export](/guides/android/samples/xfdf-import-export-kotlin.md)
- [UI View Modes](/guides/android/samples/ui-view-modes-kotlin.md)
- [LTV Signature](/guides/android/samples/ltv-signature-kotlin.md)
- [Bookmark Highlighting](/guides/android/samples/bookmark-highlighting-kotlin.md)
- [Custom Annotation Inspector](/guides/android/samples/custom-annotation-inspector-java.md)
- [Annotation Sidebar](/guides/android/samples/annotation-sidebar-kotlin.md)
- [AI Assistant (Single Document)](/guides/android/samples/ai-assistant-single-document-kotlin.md)
- [AI Assistant (Multiple Documents, Compose)](/guides/android/samples/ai-assistant-multiple-documents-compose-kotlin.md)
- [Document Comparison](/guides/android/samples/document-comparison-kotlin.md)
- [Document Processing](/guides/android/samples/document-processing-kotlin.md)
- [Custom Annotation Creation Toolbar](/guides/android/samples/custom-annotation-creation-toolbar-java.md)
- [Custom Electronic Signature](/guides/android/samples/custom-electronic-signature-java.md)
- [E-Learning](/guides/android/samples/e-learning-kotlin.md)
- [Electronic + Digital Signing](/guides/android/samples/electronic-digital-signing-kotlin.md)
- [Generate PDF Report](/guides/android/samples/generate-pdf-report-kotlin.md)
- [Multimedia Annotations](/guides/android/samples/multimedia-annotations-kotlin.md)
- [Forms with JavaScript](/guides/android/samples/forms-with-javascript-kotlin.md)
- [External Document](/guides/android/samples/external-document-kotlin.md)
- [Kiosk Grid](/guides/android/samples/kiosk-grid-kotlin.md)
- [Search Indexing](/guides/android/samples/search-indexing-kotlin.md)
- [Annotation Creation](/guides/android/samples/annotation-creation-kotlin.md)
- [Filterable Thumbnail Grid](/guides/android/samples/filterable-thumbnail-grid-kotlin.md)
- [Tabbed Documents (Persistent)](/guides/android/samples/tabbed-documents-persistent-kotlin.md)
- [Measurement Tools](/guides/android/samples/measurement-tools-kotlin.md)
- [HTML-to-PDF Conversion](/guides/android/samples/html-to-pdf-conversion-kotlin.md)
- [AES Encrypted File](/guides/android/samples/aes-encrypted-file-java.md)
- [Construction Floor Plan](/guides/android/samples/construction-floor-plan-kotlin.md)
- [Hide and Reveal Areas](/guides/android/samples/hide-and-reveal-areas-kotlin.md)
- [Multiple Documents (Compose Pager)](/guides/android/samples/multiple-documents-compose-pager-kotlin.md)
- [Screen Reader](/guides/android/samples/screen-reader-java.md)
- [Custom Search UI (Views)](/guides/android/samples/custom-search-ui-views-java.md)

