'Adding a JScrollPane to a JMenu

I have a JMenu which will include JMenuItems that are generated on start-up from a database. As such, it's quite likely that the menu will be too large and run off the screen.

As such, I am trying to add a JScrollPane to the JMenu.

Example, to the effect of;

JMenu employeesMenu = new JMenu("Employees");
JScrollPane emScroll = new JScrollPane();
JList contents = new JList();

contents.add(new JRadioButton("1"));
contents.add(new JRadioButton("2"));
contents.add(new JRadioButton("3"));
// ... etc

emScroll.add(contents);
employeesMenu.add(emScroll);

Now, my understanding is that a JMenu's contents are stored in a JList inside a JPopupMenu. So my question now is, what is a way of forcing that JList into a JScrollPane? Alternatively, is it possible to use a JScrollBar instead? Any input appreciated.



Solution 1:[1]

Maybe you can use Darryl's Menu Scroller approach. It adds arrow buttons at the top/bottom of the menu when required.

Solution 2:[2]

The below shows how to add JScrollPane to a JMenu. JLabel components are used as menu items below, as JMenuItem seems that can't be used in a JScrollPane. A mouseListener is added to each JLabel to mimic the JMenuItem behaviour, i.e., changing colors on mouse entering/exiting, as well as taking action on clicking an item (in this case, the text of the label is printed out, which can be used to decide what follows next). One may need to adjust the scrollPane.setPreferredSize, as well as the colours when the mouse enters/exits an item (even though, for convenience, the default colours used by the current LookAndFeel for the JMenuItem are used), as required.

The reason for using <html> tags in the text of the JLabel is to allow the background colour (when you move your mouse over the items) to fill the width of each item in the JScrollPane, rather than applying background colour only up to where the text ends. The <html> tags are removed when reading the text of the selected item/label.

MenuExample.java

import javax.swing.*;
import java.awt.*;
import java.util.Random;

public class MenuExample {
    Random rand = new Random();
    Color menuBackCol;
    Color mItemBackCol;
    Color mItemForegCol;
    Color mItmSelBackCol;
    Color mItmSelForegCol;

    MenuExample() {
        menuBackCol = UIManager.getColor("Menu.background");
        mItemBackCol = UIManager.getColor("MenuItem.background");
        mItemForegCol = UIManager.getColor("MenuItem.foreground");
        mItmSelBackCol = UIManager.getColor("MenuItem.selectionBackground");
        mItmSelForegCol = UIManager.getColor("MenuItem.selectionForeground");

        Box box = new Box(BoxLayout.Y_AXIS);

        for (int i = 0; i < 250; i++) {
            box.add(Box.createRigidArea(new Dimension(0, 2))); // creates space between the components
            JLabel lbl = new JLabel("<html> &ensp;" + i + ": " + rand.nextInt(10000) + "</html>");
            lbl.setOpaque(true);
            lbl.setBackground(mItemBackCol);
            lbl.addMouseListener(
                    new LabelController(lbl, mItemBackCol, mItemForegCol, mItmSelBackCol, mItmSelForegCol));
            box.add(lbl);
        }

        JScrollPane scrollPane = new JScrollPane(box);
        scrollPane.getVerticalScrollBar().setUnitIncrement(20); // adjusts scrolling speed
        scrollPane.setPreferredSize(new Dimension(100, 300));
        scrollPane.setBorder(BorderFactory.createEmptyBorder());
        scrollPane.getViewport().setBackground(menuBackCol);

        JMenuBar mb = new JMenuBar();
        JMenu menu = new JMenu("Menu");
        JMenu submenu = new JMenu("Sub Menu");
        submenu.add(scrollPane);
        menu.add(new JMenuItem("Item 1"));
        menu.add(new JMenuItem("Item 2"));
        menu.add(new JMenuItem("Item 3"));
        menu.add(submenu);
        mb.add(menu);

        JFrame f = new JFrame("Menu with ScrollBar Example");
        f.setJMenuBar(mb);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(640, 480);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String args[]) {
        new MenuExample();
    }
}

LabelController.java

import java.awt.event.MouseEvent;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class LabelController implements MouseListener {

    JLabel lbl;
    Color mItemBackCol;
    Color mItemForegCol;
    Color mItmSelBackCol;
    Color mItmSelForegCol;

    public LabelController(JLabel lbl, Color mItemBackCol, Color mItemForegCol, Color mItmSelBackCol,
            Color mItmSelForegCol) {
        this.lbl = lbl;
        this.mItemBackCol = mItemBackCol;
        this.mItemForegCol = mItemForegCol;
        this.mItmSelBackCol = mItmSelBackCol;
        this.mItmSelForegCol = mItmSelForegCol;
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        String selectedText = lbl.getText().replaceAll("<[^>]*>", "").replace("&ensp;","").trim();
        System.out.println(selectedText);
        javax.swing.MenuSelectionManager.defaultManager().clearSelectedPath(); // close the menu
        lbl.setBackground(mItemBackCol);
        lbl.setForeground(mItemForegCol);
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        lbl.setBackground(mItmSelBackCol);
        lbl.setForeground(mItmSelForegCol);
    }

    @Override
    public void mouseExited(MouseEvent e) {
        lbl.setBackground(mItemBackCol);
        lbl.setForeground(mItemForegCol);
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 camickr
Solution 2