---
title: "Annotation Sidebar"
canonical_url: "https://www.nutrient.io/guides/android/samples/annotation-sidebar-kotlin/"
md_url: "https://www.nutrient.io/guides/android/samples/annotation-sidebar-kotlin.md"
last_updated: "2026-05-15T19:10:04.916Z"
description: "Display a persistent sidebar listing all annotations with tap-to-navigate."
---

# Annotation Sidebar

Display a persistent sidebar listing all annotations with tap-to-navigate.

[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 © 2020-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.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.pspdfkit.annotations.Annotation
import com.pspdfkit.annotations.AnnotationProvider
import com.pspdfkit.annotations.AnnotationType
import com.pspdfkit.catalog.R
import com.pspdfkit.catalog.SdkExample
import com.pspdfkit.catalog.examples.kotlin.PersistentAnnotationSidebarActivity.Companion.EXTRA_CONFIGURATION
import com.pspdfkit.catalog.examples.kotlin.PersistentAnnotationSidebarActivity.Companion.EXTRA_URI
import com.pspdfkit.catalog.tasks.ExtractAssetTask
import com.pspdfkit.configuration.activity.PdfActivityConfiguration
import com.pspdfkit.configuration.sharing.ShareFeatures
import com.pspdfkit.document.PdfDocument
import com.pspdfkit.listeners.DocumentListener
import com.pspdfkit.ui.PdfUiFragment
import com.pspdfkit.ui.PdfUiFragmentBuilder
import com.pspdfkit.utils.getSupportParcelable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.launch
import java.util.EnumSet

class PersistentAnnotationSidebarExample(context: Context) :
    SdkExample(context, R.string.annotationSidebarExampleTitle, R.string.annotationSidebarExampleDescription) {
    override fun launchExample(context: Context, configuration: PdfActivityConfiguration.Builder) {
        // We don't need to show it in the outline since we will build our own UI for this.
        configuration.annotationListEnabled(false)

        // We also disable the rest of the outline to make a bit more space in the toolbar.
        configuration.outlineEnabled(false)
        configuration.bookmarkListEnabled(false)
        configuration.documentInfoViewEnabled(false)

        // We also hide the share option to make a bit more space.
        configuration.setEnabledShareFeatures(EnumSet.noneOf(ShareFeatures::class.java))
        configuration.printingEnabled(false)

        ExtractAssetTask.extract(WELCOME_DOC, title, context) { documentFile ->
            val intent = Intent(context, PersistentAnnotationSidebarActivity::class.java)
            intent.putExtra(EXTRA_URI, Uri.fromFile(documentFile))
            intent.putExtra(EXTRA_CONFIGURATION, configuration.build())
            context.startActivity(intent)
        }
    }
}

class PersistentAnnotationSidebarActivity : AppCompatActivity() {
    /** The adapter we use for our recycler view. */
    private val annotationRecyclerAdapter = AnnotationRecyclerAdapter(this)

    /** View that is shown in the side bar when no annotations are in the document. */
    private lateinit var noAnnotationsView: View

    /** The currently displayed PdfUiFragment. */
    private lateinit var pdfUiFragment: PdfUiFragment

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // We need to load our layout first.
        setContentView(R.layout.activity_persistent_sidebar)
        noAnnotationsView = findViewById(R.id.noAnnotationsView)
        val recyclerView: RecyclerView = findViewById(R.id.annotationList)
        recyclerView.adapter = annotationRecyclerAdapter
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
        annotationRecyclerAdapter.annotationRecyclerAdapterListener =
            object : AnnotationRecyclerAdapter.AnnotationRecyclerAdapterListener {
                override fun onAnnotationClicked(annotation: Annotation) {
                    // When an annotation is clicked we scroll to the page containing it.
                    // And also select the annotation for editing if possible.
                    pdfUiFragment.setPageIndex(annotation.pageIndex, true)
                    pdfUiFragment.pdfFragment?.setSelectedAnnotation(annotation)
                }

                override fun onAnnotationsLoaded(annotations: List<Annotation>) {
                    // We want to show a short description of what's going on if there are no annotations.
                    if (annotations.isEmpty()) {
                        noAnnotationsView.visibility = View.VISIBLE
                    } else {
                        noAnnotationsView.visibility = View.GONE
                    }
                }
            }

        window.setBackgroundDrawableResource(R.color.primaryLight)

        // Finally we can setup our PDF fragment.
        obtainPdfFragment()
    }

    /** This adds or retrieves the [PdfUiFragment] we use to display the PDF. */
    private fun obtainPdfFragment() {
        // We either grab the existing fragment or add a new one.
        pdfUiFragment = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? PdfUiFragment
            // There is no existing fragment, create a new one.?: PdfUiFragmentBuilder.fromUri(this, intent.extras!!.getSupportParcelable(EXTRA_URI, Uri::class.java)).configuration(intent.extras!!.getSupportParcelable(EXTRA_CONFIGURATION, PdfActivityConfiguration::class.java)).build().apply {
                    // After creation we actually add it to the fragment manager.
                    supportFragmentManager.beginTransaction().add(R.id.fragmentContainer, this, FRAGMENT_TAG).commit()
                }
    }

    override fun onStart() {
        super.onStart()
        // We need to be notified when the document was loaded.
        pdfUiFragment.pdfFragment?.addDocumentListener(
            object : DocumentListener {
                override fun onDocumentLoaded(document: PdfDocument) {
                    // When the document is loaded clear the previous annotations.
                    annotationRecyclerAdapter.clear()

                    // We need to set the current document so we can load the annotations.
                    annotationRecyclerAdapter.currentDocument = document

                    // We need to be aware of any change to the annotations so we can keep our list updated.
                    pdfUiFragment.pdfFragment?.addOnAnnotationUpdatedListener(
                        object : AnnotationProvider.OnAnnotationUpdatedListener {
                            override fun onAnnotationCreated(annotation: Annotation) {
                                annotationRecyclerAdapter.refreshAnnotationsForPage(annotation.pageIndex)
                            }

                            override fun onAnnotationUpdated(annotation: Annotation) {
                                annotationRecyclerAdapter.refreshAnnotationsForPage(annotation.pageIndex)
                            }

                            override fun onAnnotationRemoved(annotation: Annotation) {
                                annotationRecyclerAdapter.refreshAnnotationsForPage(annotation.pageIndex)
                            }

                            override fun onAnnotationZOrderChanged(pageIndex: Int, oldOrder: List<Annotation>, newOrder: List<Annotation>) {
                                annotationRecyclerAdapter.refreshAnnotationsForPage(pageIndex)
                            }
                        },
                    )

                    // We also need to initialize the list of annotations to begin with.
                    // This is a bit ineffective since we refresh the RecyclerView adapter for each page but this is fine for our small example.
                    for (pageIndex in 0 until document.pageCount) {
                        annotationRecyclerAdapter.refreshAnnotationsForPage(pageIndex)
                    }
                }
            },
        )
    }

    override fun onDestroy() {
        super.onDestroy()
        // This will cancel all running operations.
        annotationRecyclerAdapter.clear()
    }

    companion object {
        /** Tag we give to our PdfUiFragment. */
        const val FRAGMENT_TAG = "PersistentAnnotationSidebarActivity.Fragment"
        const val EXTRA_URI = "PersistentAnnotationSidebarActivity.DocumentUri"
        const val EXTRA_CONFIGURATION = "PersistentAnnotationSidebarActivity.PdfConfiguration"
    }
}

class AnnotationRecyclerAdapter(private val context: Context) : RecyclerView.Adapter<AnnotationRecyclerAdapterViewHolder>() {
    /** We keep a list of all annotations we display for easy access. */
    private val displayedItems = mutableListOf<Annotation>()

    /** We keep a list of annotations per page so we can update only single pages easily. */
    private val annotationsPerPage = mutableMapOf<Int, List<Annotation>>()

    private val adapterScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
    private val loadingJobs = mutableMapOf<Int, Job>()

    var currentDocument: PdfDocument? = null
    var annotationRecyclerAdapterListener: AnnotationRecyclerAdapterListener? = null

    // We only list certain annotation types.
    private val listedAnnotationTypes =
        AnnotationType.entries.toMutableSet().apply {
            // We don't want to clutter the list with widget or link annotations.
            remove(AnnotationType.WIDGET)
            remove(AnnotationType.LINK)
        }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnnotationRecyclerAdapterViewHolder {
        val root = LayoutInflater.from(context).inflate(R.layout.item_annotation, parent, false)
        return AnnotationRecyclerAdapterViewHolder(root)
    }

    override fun getItemCount(): Int = displayedItems.size

    override fun onBindViewHolder(holder: AnnotationRecyclerAdapterViewHolder, position: Int) {
        val item = displayedItems[position]
        // In the top text view we display whatever information we can get on the annotation.
        holder.titleView.text = item.contents?: item.name?: item.uuid

        // In the bottom text view we display the annotation type.
        holder.infoView.text = item.type.toString()
        holder.itemView.setOnClickListener {
            annotationRecyclerAdapterListener?.onAnnotationClicked(item)
        }
    }

    /** Removes all currently loaded annotations, and clears the state */
    fun clear() {
        displayedItems.clear()
        annotationsPerPage.clear()
        loadingJobs.values.forEach { it.cancel() }
        loadingJobs.clear()
        adapterScope.coroutineContext.cancelChildren()
        currentDocument = null
    }

    /** Reloads the list of annotations for the given page. */
    fun refreshAnnotationsForPage(pageIndex: Int) {
        // If no document is set we don't to anything.
        val document = currentDocument?: return

        // Cancel any already running loading operation for this page.
        loadingJobs[pageIndex]?.cancel()

        // We grab the annotations for the current page index.
        // This operates on a background scheduler so we have to explicitly observe it on the main thread.
        loadingJobs[pageIndex] =
            adapterScope.launch {
                val annotations = document.annotationProvider.getAllAnnotationsOfType(listedAnnotationTypes, pageIndex, 1)
                annotationsPerPage[pageIndex] = annotations
                refreshDisplayedItems()
            }
    }

    @SuppressLint("NotifyDataSetChanged")
    private fun refreshDisplayedItems() {
        val document = currentDocument?: return
        displayedItems.clear()
        for (pageIndex in 0 until document.pageCount) {
            // We add all pages we already loaded here.
            val items = annotationsPerPage[pageIndex]
            if (items!= null) {
                displayedItems.addAll(items)
            }
        }

        // We notify the listener so we can update the visibility of our empty view.
        annotationRecyclerAdapterListener?.onAnnotationsLoaded(displayedItems)

        notifyDataSetChanged()
    }

    interface AnnotationRecyclerAdapterListener {
        fun onAnnotationClicked(annotation: Annotation)

        fun onAnnotationsLoaded(annotations: List<Annotation>)
    }
}

class AnnotationRecyclerAdapterViewHolder(root: View) : RecyclerView.ViewHolder(root) {
    val titleView: TextView = root.findViewById(R.id.annotation_list_item_title)
    val infoView: TextView = root.findViewById(R.id.annotation_list_item_info)
}

```

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)
- [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)
- [Overlay Views](/guides/android/samples/overlay-views-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)

