Using contextual toolbars within PdfFragment

PdfActivity provides contextual toolbars out of the box for so-called special modes, which include text selection, annotation creation, annotation editing, and document editing. Since Nutrient Android SDK 2.4, you have been able to use these toolbars as standalone views and connect them with corresponding special modes.

Special modes

When a document is in a special mode, it means that some kind of specific interaction is taking place, and as a result, the UI could behave in a slightly different way.

For example, if you’re in annotation creation mode with ink as a selected annotation type, sliding your finger across the page will not cause the page to move. Instead, it will draw the ink path beneath your finger. In such a state, we consider a document (or PdfFragment) to be in a special mode.

Each special mode has its own controller. What PdfActivity implements is the wiring of those controllers to their corresponding UI representations, which are contextual toolbars. When using PdfFragment, you can either use the controllers to create a custom UI for special modes or use ContextualToolbars provided by the framework.

Here is a list of the current special modes and their controllers:

Contextual toolbars

Contextual toolbars extend the ContextualToolbar class and, as already mentioned, are one possible UI representation of special modes.

Nutrient currently provides these toolbars:

Additionally, each of the toolbars has a method for binding (and unbinding) the special mode controller:

Calling this method will automatically provide all the necessary logic — for example, changing selected buttons depending upon the selected annotation type, or changing the color of the color picker icon.

Manually adding toolbars

Finally, for adding a toolbar to PdfFragment in a custom implementation, we need to manually add contextual toolbars to a view wrapping the PdfFragment. We’ll use a Catalog example named ToolbarsInFragmentExample for the code snippets:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             xmlns:tools="http://schemas.android.com/tools"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             tools:context=".examples.activities.ToolbarsInFragmentActivity"
             tools:ignore="UnusedAttribute">

    <FrameLayout
        android:id="@+id/fragmentContainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">

        <com.pspdfkit.ui.toolbar.ToolbarCoordinatorLayout
            android:id="@+id/toolbarCoordinatorLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </FrameLayout>

</FrameLayout>

Here’s a simple layout containing one FrameLayout serving as a fragment placeholder, and a second one above it containing the ToolbarCoordinatorLayout.

ToolbarCoordinatorLayout is a ViewGroup for laying out child ContextualToolbars and allowing the user to drag them to different positions. It is a strongly recommended to wrap toolbars in the ToolbarCoordinatorLayout, since it also provides the correct size for the toolbars, along with many little details such as the submenu indicator position, depending on the toolbar position on the screen.

Controllers are retrieved from mode change listeners registered on the PdfFragment. In your activity, you can register a listener to a fragment, bind a controller to the previously created toolbar, and add that toolbar to the coordinator:

class MyActivity: AppCompatActivity(), OnAnnotationCreationModeChangeListener {
    private val annotationCreationToolbar: AnnotationCreationToolbar
    private val toolbarCoordinatorLayout: ToolbarCoordinatorLayout
    private val fragment: PdfFragment

    protected fun onCreate(savedInstanceState: Bundle) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_annotation_toolbar_fragment)

        toolbarCoordinatorLayout = findViewById(R.id.toolbarCoordinatorLayout) as ToolbarCoordinatorLayout
        annotationCreationToolbar = AnnotationCreationToolbar(this)

        /// ... Init fragment here ...

        // Register a listener for special mode changes.
        fragment.addOnAnnotationCreationModeChangeListener(this)
    }

    /**
     * Called when the annotation creation mode has been entered.
     * @param controller Provided controller for managing annotation creation mode.
     */
    fun onEnterAnnotationCreationMode(controller: AnnotationCreationController) {
        // Bind the toolbar to the controller.
        annotationCreationToolbar.bindController(controller)

        // Now display the toolbar in the `toolbarCoordinatorLayout`.
        toolbarCoordinatorLayout.displayContextualToolbar(annotationCreationToolbar, true)
    }
}
class MyActivity extends AppCompatActivity implements OnAnnotationCreationModeChangeListener {
    private AnnotationCreationToolbar annotationCreationToolbar;
    private ToolbarCoordinatorLayout toolbarCoordinatorLayout;
    private PdfFragment fragment;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_annotaton_toolbar_fragment);

        toolbarCoordinatorLayout = (ToolbarCoordinatorLayout) findViewById(R.id.toolbarCoordinatorLayout);
        annotationCreationToolbar = new AnnotationCreationToolbar(this);

        // ... Init fragment here ...

        // Register a listener for special mode changes.
        fragment.addOnAnnotationCreationModeChangeListener(this);
    }

    /**
     * Called when the annotation creation mode has been entered.
     * @param controller Provided controller for managing annotation creation mode.
     */
    @Override
    public void onEnterAnnotationCreationMode(@NonNull AnnotationCreationController controller) {
        // Bind the toolbar to the controller.
        annotationCreationToolbar.bindController(controller);

        // Now display the toolbar in the `toolbarCoordinatorLayout`.
        toolbarCoordinatorLayout.displayContextualToolbar(annotationCreationToolbar, true);
    }
}

This implementation will only result in the annotation creation toolbar. See the entirety of ToolbarsInFragmentExample for other toolbars.

Integrating annotation inspectors

If you wish to use our default annotation inspectors with toolbars, follow the using property inspectors within fragment guide.