Customizing toolbars in our Android PDF viewer

Nutrient comes with a flexible toolbar system that provides basic action bar functionality, as well as powerful editing features.

This guide covers contextual toolbar customization. For customizing the main toolbar (referred to as a menu), see our customizing menus guide.

Styling the toolbar

By default, Nutrient will apply default styles to all of its toolbars. A large set of style attributes allows you to modify the appearance of toolbars according to your app’s look. Check out the styling guide, which gives an overview of all available style attributes.

Change the grouping of toolbar items

Each toolbar contains ContextualToolbarMenuItems representing toolbar elements/items.

The rules of how menu items in each toolbar are grouped are defined in classes extending PresetMenuItemGroupingRule, which you can set through setMenuItemGroupingRule() called on the specific toolbar. What you want to do is override PresetMenuItemGroupingRule#getGroupPreset(), returning a different structure depending on the given toolbar capacity.

MenuItem represents a structure, using IDs for identification, of how ContextualToolbarMenuItems with those IDs would be positioned/grouped. For more details on the implementation, check out CustomToolbarIconGroupingExample in the Catalog app.

Custom toolbar grouping: A markup item group, a color picker, a custom menu item, and an image picker, all defined in a custom grouping structure.

For example, let’s define a custom grouping of menu items for AnnotationCreationToolbar, but with our own added custom item:

class MyCustomGrouping(context: Context) : PresetMenuItemGroupingRule(context) {

    override fun getGroupPreset(capacity: Int, itemsCount: Int): List<MenuItem> {
        // The capacity shouldn't be less than 4. If that is the case, return an empty list.
        if (capacity < ContextualToolbar.MIN_TOOLBAR_CAPACITY) return emptyList<MenuItem>()

        return if (capacity <= 7) FOUR_ITEMS_GROUPING else SEVEN_ITEMS_GROUPING
    }

    /** Annotation toolbar grouping with 4 elements. */
    private val FOUR_ITEMS_GROUPING = listOf(
            MenuItem(R.id.pspdf_menu_custom),
            MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_group_markup,
                    intArrayOf(
                        com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_highlight,
                        com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_squiggly,
                        com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_strikeout,
                        com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_underline))
            MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_picker),
            MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_image)
            )

    /** Annotation toolbar grouping with 7 elements. */
    private val SEVEN_ITEMS_GROUPING = listOf(
            MenuItem(R.id.pspdf_menu_custom),
            MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_highlight),
            MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_squiggly),
            MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_strikeout),
            MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_underline),
            MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_picker),
            MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_image)
        )

}
public class MyCustomGrouping extends PresetMenuItemGroupingRule {

    public CustomAnnotationCreationToolbarGroupingRule(@NonNull Context context) {
        super(context);
    }

    @Override
    public List<MenuItem> getGroupPreset(@IntRange(from = ContextualToolbar.MIN_TOOLBAR_CAPACITY) int capacity, int itemsCount) {
        // The capacity shouldn't be less than 4. If that is the case, return an empty list.
        if (capacity < ContextualToolbar.MIN_TOOLBAR_CAPACITY) return new ArrayList<>(0);

        return (capacity <= 7) ? FOUR_ITEMS_GROUPING : SEVEN_ITEMS_GROUPING;
    }

    /** Annotation toolbar grouping with 4 elements. */
    private static final List<MenuItem> FOUR_ITEMS_GROUPING = new ArrayList<>(4);
    static {
        // Make sure our custom item is included.
        FOUR_ITEMS_GROUPING.add(new MenuItem(R.id.pspdf_menu_custom));
        FOUR_ITEMS_GROUPING.add(new MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_group_markup,
                new int[]{
                        com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_highlight,
                        com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_squiggly,
                        com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_strikeout,
                        com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_underline}));
        FOUR_ITEMS_GROUPING.add(new MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_picker));
        FOUR_ITEMS_GROUPING.add(new MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_image));
    }

    /** Annotation toolbar grouping with 7 elements. */
    private static final List<MenuItem> SEVEN_ITEMS_GROUPING = new ArrayList<>(7);
    static {
        // Make sure our custom item is included.
        SEVEN_ITEMS_GROUPING.add(new MenuItem(R.id.pspdf_menu_custom));
        SEVEN_ITEMS_GROUPING.add(new MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_highlight));
        SEVEN_ITEMS_GROUPING.add(new MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_squiggly));
        SEVEN_ITEMS_GROUPING.add(new MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_strikeout));
        SEVEN_ITEMS_GROUPING.add(new MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_underline));
        SEVEN_ITEMS_GROUPING.add(new MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_picker));
        SEVEN_ITEMS_GROUPING.add(new MenuItem(com.pspdfkit.R.id.pspdf__annotation_creation_toolbar_item_image));
    }

}

Now let’s create an activity where we can listen to the annotation creation toolbar being displayed and apply our own structure and add a custom item to it:

class MyCustomActivity : PdfActivity(), ToolbarCoordinatorLayout.OnContextualToolbarLifecycleListener {

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

    override fun onPrepareContextualToolbar(toolbar: ContextualToolbar<*>) {
        if (toolbar is AnnotationCreationToolbar) {
            toolbar.setMenuItemGroupingRule(CustomAnnotationCreationToolbarGroupingRule(this))

            // Get the existing menu items so we can add our item later.
            val menuItems = toolbar.menuItems

            // Create our custom menu item.
            val customItem = ContextualToolbarMenuItem.createSingleItem(
                this,
                R.id.pspdf_menu_custom,
                ContextCompat.getDrawable(this, R.drawable.ic_bookmark_outline),
                "Bookmark",
                Color.WHITE,
                Color.WHITE,
                ContextualToolbarMenuItem.Position.START,
                false
            )

            // Tell the toolbar about our new item.
            menuItems.add(customItem)
            toolbar.setMenuItems(menuItems)

            // Add a listener so we can handle clicking on our item.
            toolbar.setOnMenuItemClickListener(
                ContextualToolbar.OnMenuItemClickListener { toolbar, menuItem ->
                    return@OnMenuItemClickListener (
                        if (menuItem.id == R.id.pspdf_menu_custom) {
                            Toast.makeText(this@MyCustomActivity, "Custom Action clicked", Toast.LENGTH_SHORT).show()
                            true
                        } else false
                    )
                }
            )
    }

    ...

}
public class MyCustomActivity extends PdfActivity implements ToolbarCoordinatorLayout.OnContextualToolbarLifecycleListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setOnContextualToolbarLifecycleListener(this);
    }

    @Override
    public void onPrepareContextualToolbar(@NonNull ContextualToolbar toolbar) {
        if (toolbar instanceof AnnotationCreationToolbar) {
            toolbar.setMenuItemGroupingRule(new CustomAnnotationCreationToolbarGroupingRule(this));

            // Get the existing menu items so we can add our item later.
            final List<ContextualToolbarMenuItem> menuItems = ((AnnotationCreationToolbar) toolbar).getMenuItems();

            // Create our custom menu item.
            final ContextualToolbarMenuItem customItem = ContextualToolbarMenuItem.createSingleItem(
                this,
                R.id.pspdf_menu_custom,
                ContextCompat.getDrawable(this, R.drawable.ic_bookmark_outline),
                "Bookmark",
                Color.WHITE,
                Color.WHITE,
                ContextualToolbarMenuItem.Position.START,
                false
            );

            // Tell the toolbar about our new item.
            menuItems.add(customItem);
            toolbar.setMenuItems(menuItems);

            // Add a listener so we can handle clicking on our item.
            toolbar.setOnMenuItemClickListener(new ContextualToolbar.OnMenuItemClickListener() {
                @Override
                public boolean onToolbarMenuItemClick(@NonNull ContextualToolbar toolbar, @NonNull ContextualToolbarMenuItem menuItem) {
                    if (menuItem.getId() == R.id.pspdf_menu_custom) {
                        Toast.makeText(MyCustomActivity.this, "Custom Action clicked", Toast.LENGTH_SHORT).show();
                        return true;
                    }
                    return false;
                }
            });
        }
    }


}

Group MenuItems

When creating a MenuItem with subitems, keep the following two points in mind:

  1. For your id, always use one of the IDs marked as group, e.g. pspdf__annotation_creation_toolbar_group_drawing and not pspdf__annotation_creation_toolbar_item_underline. This is to ensure you don’t accidentally use the same ID twice — for example, using pspdf__annotation_creation_toolbar_item_underline both for the group ID and as an actual item inside the menu. Using an ID marked as group also makes the difference between group items and their children more obvious. Starting with Nutrient Android SDK 7, this will be enforced and an exception will be thrown if an invalid ID is passed in for a group item.

  2. A group item in the AnnotationCreationToolbar never has its own icon. Instead, it will always look like the last item that was selected from the subgroup. This doesn’t apply in other toolbars where some group items get their own special icon — for example, pspdf__document_editing_toolbar_group_more will result in an ellipsis icon instead of using one of the child icons, or pspdf__annotation_editing_toolbar_group_undo_redo will result in a special icon showing if undo/redo is available.

Toolbar positions

You can programmatically set the position of a toolbar or lock the toolbar into a specific position using ToolbarCoordinatorLayout.LayoutParams. To set the toolbar to a specific position, you can call setPosition() directly on the toolbar instance you want to move:

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

    // This example will move every toolbar that is shown to the user to the right position.
    setOnContextualToolbarLifecycleListener(object : OnContextualToolbarLifecycleListener {
        override fun onPrepareContextualToolbar(toolbar: ContextualToolbar) {
            toolbar.position = ToolbarCoordinatorLayout.LayoutParams.Position.RIGHT
        }

        ...
    })
}
@Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // This example will move every toolbar that is shown to the user to the right position.
    setOnContextualToolbarLifecycleListener(new OnContextualToolbarLifecycleListener() {
        @Override public void onPrepareContextualToolbar(@NonNull ContextualToolbar toolbar) {
            toolbar.setPosition(ToolbarCoordinatorLayout.LayoutParams.Position.RIGHT);
        }

        ...
    });
}

Lock toolbar into position

You can also permanently lock a toolbar to a specific position by limiting the allowedPositions using the toolbar’s ToolbarCoordinatorLayout.LayoutParams:

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

    // This example will lock every toolbar to the left position,
    // making you unable to drag the toolbar to a different position.
    setOnContextualToolbarLifecycleListener(object : OnContextualToolbarLifecycleListener {
        override fun onPrepareContextualToolbar(toolbar: ContextualToolbar) {
            toolbar.layoutParams = ToolbarCoordinatorLayout.LayoutParams(
                Position.LEFT, EnumSet.of(Position.LEFT)
            ))
        }

        ...
    })
}
@Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // This example will lock every toolbar to the left position,
    // making you unable to drag the toolbar to a different position.
    setOnContextualToolbarLifecycleListener(new OnContextualToolbarLifecycleListener() {
        @Override public void onPrepareContextualToolbar(@NonNull ContextualToolbar toolbar) {
            toolbar.setLayoutParams(new ToolbarCoordinatorLayout.LayoutParams(
                Position.LEFT, EnumSet.of(Position.LEFT)
            ));
        }

        ...
    });
}