---
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: "2025-07-02T00:00:00.000Z"
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

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

