3

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.

Rudi Kershaw
  • 12,332
  • 7
  • 52
  • 77
  • I wonder if this is possible? – Ron Sep 30 '13 at 14:17
  • @Ron E - I've had a read around and it seems that it is, but people's answers to previous similar questions have never been comprehensive enough for me to be able to figure it out for myself. – Rudi Kershaw Sep 30 '13 at 14:18
  • Perhaps something involving JMenu.add(Component c) and adding the JScrollPane? – Ron Sep 30 '13 at 14:20
  • Try this http://www.java2s.com/Tutorial/Java/0240__Swing/CreatingCustomMenuElementComponentsTheMenuElementInterface.htm – René Link Sep 30 '13 at 14:25
  • @RenéLink - By *this*, do you mean implementing the MenuElement interface on a custom JScrollPane? – Rudi Kershaw Sep 30 '13 at 14:57

2 Answers2

2

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

camickr
  • 321,443
  • 19
  • 166
  • 288
  • Darryl's Menu Scroller, isn't particularly viable for this particular solution because in certain menus there could be many hundreds of entries arranged alphabetically where an actual scroll bar would make life significantly easier. – Rudi Kershaw Sep 30 '13 at 14:48
  • @RudiKershaw menus are not intended for hundreds of entries, you might re-consider your ui. That being the case: you are probably on your own implementing a custom solution (or hire a consultant doing it for you :-) – kleopatra Oct 17 '13 at 08:56
  • 1
    @kleopatra - I have actually already sorted this issue by resorting to using `JComboBox`es instead, but I will keep the question open because I think it's worth having an answer to. I am thinking of using this as a hobby project and creating my own custom `JPopupMenu` class. If I finish that before this is answered I will post the results for others. As for menus not being intended for many hundreds of items, that may be true but excel uses menu item style PopupMenus in their filters and that works really well. – Rudi Kershaw Oct 17 '13 at 09:19
  • 1
    @RudiKershaw fair enough - looking forward to your solution :-) – kleopatra Oct 17 '13 at 10:04
1

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) {
    }
}
Chris
  • 18,724
  • 6
  • 46
  • 80