This is my first question and I'm realy sorry for my English. I don't want to navigation in JComboBox's dropdown list show the selected item in the "JComboBox's main field" (sorry, don't know how to call it, see the images).
This is what happen when I navigate in list:
But I want something like this:
and when I press Enter or clicked on item, it appear in the main field.
Here is my code:
import javax.swing.*;
import javax.swing.text.JTextComponent;
import java.awt.*;
public class ComboTest {
JFrame frame;
JPanel panel;
String[] choices = new String[]{
"Java",
"Python",
"C++",
"PHP",
"Perl"
};
JComboBox<String> comboBox = new JComboBox<>(choices);
JTextComponent textComponent;
public static void main(String[] args){
new ComboTest();
}
public ComboTest() {
frame = new JFrame();
panel = new JPanel();
textComponent = (JTextComponent) comboBox.getEditor().getEditorComponent();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 150);
frame.setResizable(false);
panel.setLayout(new FlowLayout());
comboBox.setEditable(true);
comboBox.setSelectedItem(null);
panel.add(comboBox);
frame.add(panel);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Try this code block
comboBox.addPopupMenuListener(new PopupMenuListener() {
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
//when Popupmenu is visible, remove the combobox editor text
comboBox.getEditor().setItem(null);
}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
}
#Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
});
Thanks to zilk. Your answer helped me to solve this. The listener addPopupMenuListener doesn't work for me. But when I changed the addPopupMenuListener to addItemListener, everything worked fine.
comboBox.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(final ItemEvent itemEvent) {
comboBox.getEditor().setItem(null);
comboBox.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
super.keyPressed(e);
if(e.getKeyCode() == KeyEvent.VK_ENTER)
{
comboBox.getEditor().setItem(itemEvent.getItem());
}
}
});
}
});
Related
Like I described in the title, I am required to make a JButton clicked if a key from a keyboard is pressed. For example :
ActionListenerClass actionListener = new ActionListenerClass();
KeyListenerClass actionListener = new KeyListenerClass();
JButton aButton = new JButton("A");
aButton.setActionCommand("A");
aButton.addActionListener(actionListener);
aButton.addKeyListener(keyListener);
When "A" is pressed from the keyboard, button A will perform a doClick() and send the action command to a private class of the action listener for event handling. Now I have read a lot of solution from stack overflow, and they all used key binding, which is to bind between an input map and an action map. The thing is I absolutely have to use a key listener with a private class but not the binding. The only thing I can guess now is the keyListener above have to somehow receive the keyboard input and perform doClick on the button it is binded to in a keyPressed method, which I have try and it didn't work at all.
Edit: Here's my entire code.
CalculatorViewController.java
import java.awt.*;
import java.awt.event.*;
import java.util.regex.Pattern;
import javax.swing.*;
/**Create the app GUI
* #author Bach Le
* #version 1.0
* #see java.awt, java.awt.event, javax.swing
* #since 12.0.1
*/
public class CalculatorViewController extends JPanel {
private JButton backSpaceButton;
public CalculatorViewController() {
Controller controller = new Controller();
KeyController keyController = new KeyController();
setBorder(BorderFactory.createMatteBorder(5, 5, 5, 5,Color.black));//Adding the panel border
backSpaceButton = new JButton("\u21DA");
backSpaceButton.setPreferredSize(new Dimension(52,55));
backSpaceButton.setOpaque(false);//set transparency
backSpaceButton.setContentAreaFilled(false);
backSpaceButton.setBorderPainted(false);
backSpaceButton.setActionCommand("Backspace Button");//set the action command
backSpaceButton.addActionListener(controller);//add action listener
backSpaceButton.setToolTipText("Backspace (Alt+B)");//set tooltip text
backSpaceButton.setFont(font);//set the font
backSpaceButton.addKeyListener(keyController);
add(backSpaceButton) ;
}
private class Controller implements ActionListener{
public void actionPerformed(ActionEvent e) {
//event handling here
}
}
private class KeyController implements KeyListener{
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==65) {
backSpaceButton.doClick();
}
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
}
}
2.Calculator.java
public class Calculator {
public static void main(String[] args) {
CalculatorViewController pane = new CalculatorViewController();
JFrame frame = new JFrame("Calculator");
frame.setContentPane(pane);
frame.setSize(380, 520);
frame.setLocationByPlatform(true);
frame.setResizable(true);
frame.setVisible(true);
}
}
Focus on CalculatorViewController, I am trying to make backSpaceButton clicked when A is pressed (Of course it is the actual backspace button but I will fix it later), so it will send its action command to the action listener registered to it, which will be processed in the method of Controller inner class. I am not sure the proper way to achieve this.
Adding a KeyListener will only work for components with focus. You would not want to add the KeyListener to the JButtons, because only one JButton will be in focus.
For just setting a value you could use setMnemonic but then you would have to use a modifier (such as 'alt) when you press the key.
The 'correct' way to do this is to use key bindings
Here is an example with two buttons.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ButtonKeys{
public void buildGui(){
JFrame frame = new JFrame("key buttons");
JPanel panel = new JPanel(new BorderLayout());
JButton a = new JButton("A");
a.addActionListener(evt->{ System.out.println("a pressed");});
JButton b = new JButton("B");
b.addActionListener(evt->{ System.out.println("b pressed");});
panel.add(a, BorderLayout.EAST);
panel.add(b, BorderLayout.WEST);
frame.setContentPane(panel);
frame.setVisible(true);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
KeyStroke us = KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false);
panel.getInputMap().put(us, "A");
panel.getActionMap().put("A", new AbstractAction(){
#Override
public void actionPerformed(ActionEvent evt){
a.doClick();
}
});
KeyStroke us2 = KeyStroke.getKeyStroke(KeyEvent.VK_B, 0, false);
panel.getInputMap().put(us2, "B");
panel.getActionMap().put("B", new AbstractAction(){
#Override
public void actionPerformed(ActionEvent evt){
b.doClick();
}
});
a.setFocusable(false);
b.setFocusable(false);
}
public static void main(String[] args){
EventQueue.invokeLater( new ButtonKeys()::buildGui);
}
}
I made the buttons to not obtain focus because if they get focus then their input map will be used.
This code should work for you. For the KeyListener I used a KeyAdapter more here as it is more convenient if you simply need to use one of the methods. You can of course move the Listeners to separate own classes if needed, but the behavior would stay the same.
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JButton btn = new JButton("A");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button clicked");
}
});
btn.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) {
if (e.getKeyChar() == 'A' || e.getKeyChar() == 'a') {
((JButton) e.getSource()).doClick();
}
}
});
panel.add(btn);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(0, 0, 800, 600);
frame.getContentPane().add(panel);
frame.setVisible(true);
}
If you really need to use a KeyListener, it would look like this:
btn.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
if (e.getKeyChar() == 'A' || e.getKeyChar() == 'a') {
((JButton) e.getSource()).doClick();
}
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
}
});
Well what i'm trying to do is change the text of the JRadioButton's when they're selected, i got them to change the color. I know I can do it by putting the code to change the text inside the dedicated event handling method specific to each button, but how do I do it so that I use A DIFFERENT event handling method that just changes the buttons? I already created one but it doesn't work, here's the code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class LessonTwenty extends JFrame implements ActionListener{
JRadioButton b1,b2;
JTextArea t1;
JScrollPane s1;
JPanel jp = new JPanel();
public LessonTwenty()
{
b1= new JRadioButton("green");
b1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jp.setBackground(Color.GREEN);
}
});
b2= new JRadioButton("red");
b2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jp.setBackground(Color.RED);
}
});
//Method to change the text of the JRadion Buttons, what i'm trying to make work
new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(b1.isSelected()){
b1.setText("Welcome");
}
else if(b2.isSelected()){
b2.setText("Hello");
}
}
};
jp.add(b1);
jp.add(b2);
this.add(jp);
setTitle("Card");
setSize(700,500);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String [ ] args){
new LessonTwenty();
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
if i understand you right, you want do do something like this:
//Method to change the text of the JRadion Buttons, what i'm trying to make work
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(b1.isSelected()){
b1.setText("Welcome");
}
else if(b2.isSelected()){
b2.setText("Hello");
}
}
};
b1= new JRadioButton("green");
b1.addActionListener(al);
b2= new JRadioButton("red");
b2.addActionListener(al);
ie. you define one ActionListener which you use in all your objects.
The anonymous object you define in your original code does absolutely nothing, it just creates an ActionListener which nobody can ever access, since it is not assigned to any Button.
Maybe this could help
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(e.getSource() == b1){
b1.setText("Welcome");
} else if(e.getSource() == b2){
b2.setText("Hello");
}
}
};
I have a problem with the focus listener implemented by CustomTextField class. The focus listener is only called when another Swing component is getting the focus. But If I move the JFrame istelf by dragging it with the mouse, the focusLost() method is never called (in other words, it doesn´t seem that the focus is shifting from CustomTextField to JFrame).
EDIT: The solution of my question is below:
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
import javax.swing.*;
public class ScrollFocus extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ScrollFocus();
}
});
}
public ScrollFocus() {
this.setLayout(new BorderLayout());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Vector<String> values = new Vector<>();
values.add("a");
values.add("b");
values.add("c");
values.add("d");
values.add("e");
JComboBox<String> comboBox = new JComboBox<>(values);
JScrollPane scrollPane = new JScrollPane(comboBox);
this.add(scrollPane, BorderLayout.NORTH);
CustomTextField customTextField = new CustomTextField();
this.add(customTextField, BorderLayout.CENTER);
JButton button = new JButton("press");
final JPopupMenu menu = new JPopupMenu("Menu");
menu.add(new JMenuItem("Test"));
button.setComponentPopupMenu(menu);
this.add(button, BorderLayout.SOUTH);
pack();
setVisible(true);
}
class CustomTextField extends JTextField implements FocusListener {
private CustomPopup customPopup = new CustomPopup();
public CustomTextField() {
this.addFocusListener(this);
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "VK_UP");
this.getActionMap().put("VK_UP", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
setPopupSize();
customPopup.show(CustomTextField.this, CustomTextField.this.getX(), CustomTextField.this.getY() + CustomTextField.this.getHeight());
customPopup.setSelectedIndex(0);
}
});
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "VK_DOWN");
this.getActionMap().put("VK_DOWN", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
setPopupSize();
customPopup.show(CustomTextField.this, CustomTextField.this.getX(), CustomTextField.this.getY() + CustomTextField.this.getHeight());
customPopup.setSelectedIndex(0);
}
});
}
public void setPopupSize() {
customPopup.setPopupSize(new Dimension(this.getWidth(), 110));
}
#Override
public void focusGained(FocusEvent e) {
}
#Override
public void focusLost(FocusEvent e) {
}
class CustomPopup extends JPopupMenu {
String[] values = new String[]{"Value1", "Value2", "Value3", "Value4", "Value5", "Value6", "Value7",
"Value8","Value9", "Value10", "Value11", "Value12", "Value13", "Value14", "Value15", "Value16",};
JList<String> list = new JList<>(values);
JScrollPane scrollPane = new JScrollPane(list);
public int index = 0;
public CustomPopup() {
this.setLayout(new GridLayout(0,1));
this.add(scrollPane);
this.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_UP){
if(customPopup.index > 0)
customPopup.setSelectedIndex(--customPopup.index);
}
else if(e.getKeyCode() == KeyEvent.VK_DOWN){
if(customPopup.index < customPopup.getListSize()-1)
customPopup.setSelectedIndex(++customPopup.index);
}
}
});
this.addFocusListener(new FocusAdapter() {
#Override
public void focusLost(FocusEvent e) {
index=0;
}
});
pack();
}
public void setSelectedIndex(int index) {
list.setSelectedIndex(index);
list.ensureIndexIsVisible(index);
requestFocus();
}
public int getListSize() {
return values.length;
}
}
}
}
//customPopup.setVisible(true);
customPopup.show((JComponent)e.getSource(), 0, 20);
You should be using the show(...) method to show the popup. This must add some listeners to the popup so you will no longer need the FocusListener on the text field.
However, now this is a different problem. The text field loses focus so the Action never get invoked. That would be ok, but the JList never gains focus so it doesn't respond to the up/down keys unless you click on the list box first. I'm not sure what the problem is here.
Maybe you can try to make the popup, scrollpane and list all non-focusable so that focus remains on the text field?
'Focus', which is admittedly a slightly ambiguous term, generally applies to a component, not to an entire window. We think of the "window with focus", but I think what we really mean is "the current window, the one which contains the focus." I would not expect focus_lost to be called if I moved the window (aka JFrame) itself.
Another way to think of it; if I had a text field, clicked in it, and typed a letter or two, I would see those letters in that text field. If I then moved the window slightly and typed another letter or two, I would still expect those letters to appear in that field. It still has focus, and never lost it.
I need to display a swing popup with my custom component. The popup should stay visible, until I hide it myself, but shouldn't get focus.
I have a code written by some other developer that does it in the following way:
popupMenu = new JPopupMenu();
popupMenu.add(myCustomComponent, BorderLayout.CENTER);
popupMenu.setFocusable(false);
popupMenu.setVisible(true);
popupMenu.show(parentComponent, x, y);
This seems to work, but has a bug - when the popup is visible, first mouse click outside the component is consumed by the popup. So I need to click twice to set focus to another component.
How can I fix it? Or what is correct way to make the popup?
UPDATE
At last I've managed to reproduce my problem in short code fragment. Thanks to Guillaume Polet for giving me a starting point.
Here's the code:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class TestJPopup {
protected void initUI() {
JFrame frame = new JFrame(TestJPopup.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JTextField textField = new JTextField("Some text field");
frame.add(textField, BorderLayout.WEST);
final JButton buttonToHit = new JButton("Hit me");
buttonToHit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(buttonToHit, "You hit the button successfully");
}
});
frame.add(buttonToHit);
frame.setSize(200, 100);
frame.setVisible(true);
final JPopupMenu popup = new JPopupMenu();
popup.add(new JLabel("<html>Hey!<br>I'm the popup window!</html>"),
BorderLayout.NORTH);
popup.setFocusable(false);
popup.setVisible(true);
popup.show(textField, 60, 60);
// I want to activate popup when user clicks in the text field
textField.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (popup != null) {
popup.show(textField, 60, 60);
}
}
});
}
public static void main(String[] args) throws Exception {
Class lnfClass = Class.forName("com.sun.java.swing.plaf.windows.WindowsLookAndFeel", true,
Thread.currentThread().getContextClassLoader());
LookAndFeel feel = (LookAndFeel) lnfClass.newInstance();
UIManager.setLookAndFeel(feel);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestJPopup().initUI();
}
});
}
}
Two critical moments:
Windows look and feel used (with default not reproducible)
Mouse listener attached to text field in main frame
Not an answer, but just an example SSCCE in which I can't currently reproduce the behaviour you described. Maybe start from this code, try to reproduce the error and the edit your post with modified non-working code.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class TestJPopup {
protected void initUI() {
JFrame frame = new JFrame(TestJPopup.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel leftLabel = new JLabel("Left");
frame.add(leftLabel, BorderLayout.WEST);
final JButton buttonToHit = new JButton("Hit me");
buttonToHit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(buttonToHit, "You hit the button successfully");
}
});
frame.add(buttonToHit);
frame.setSize(500, 400);
frame.setVisible(true);
JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new JLabel("<html>A Custom<br>component<br>made to<br> simulate <br>your custom component</html>"),
BorderLayout.NORTH);
JTextField textfield = new JTextField(30);
popupMenu.add(textfield);
popupMenu.setFocusable(false);
popupMenu.setVisible(true);
popupMenu.show(leftLabel, 20, 20);
// Let's force the focus to be in a component in the popupMenu
textfield.requestFocusInWindow();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestJPopup().initUI();
}
});
}
}
Not a solution, but:
Looks like a bug to me, even a plain componentPopup exhibits the same mis-behaviour (in winLAF and Nimbus, not in Metal):
JTextField field = new JTextField("some popup owner");
JPopupMenu menu = new JPopupMenu();
menu.add("dummy");
field.setComponentPopupMenu(menu);
Action action = new AbstractAction("hit me!") {
#Override
public void actionPerformed(ActionEvent e) {
LOG.info("got hit!");
}
};
JComponent content = new JPanel();
content.add(new JButton(action));
content.add(field);
for quick research and/or for future readers,
this issue is reproducible and presented for,
a) JPopup
b) JMenu
tested on jdk1.6.0_25 and jdk1.7.0_04,
same issue on WinXp and Win7,
for Look and Feel to SystemLookAndFeel / WindowsLookAndFeel,
Here's a possible workaround with JWindow instead of JPopupMenu, that was proposed by mKorbel in comments:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TestJPopup {
protected void initUI() {
final JFrame frame = new JFrame(TestJPopup.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JTextField textField = new JTextField("Some text field");
frame.add(textField, BorderLayout.WEST);
final JButton buttonToHit = new JButton("Hit me");
buttonToHit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(buttonToHit, "You hit the button successfully");
}
});
frame.add(buttonToHit);
frame.setSize(200, 70);
frame.setVisible(true);
final JWindow popup = new JWindow();
popup.getContentPane().add(new JLabel("<html>Hey!<br>I'm the popup window!</html>"),
BorderLayout.NORTH);
popup.setLocation(frame.getLocation().x + 60, frame.getLocation().y + 60);
popup.pack();
popup.setFocusable(false);
popup.setVisible(true);
// I want to activate popup when user clicks in the text field
textField.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
if (popup != null) {
popup.setVisible(true);
popup.setLocation(frame.getLocation().x + 60, frame.getLocation().y + 60);
popup.toFront();
}
}
});
textField.addFocusListener(new FocusAdapter() {
#Override
public void focusLost(FocusEvent e) {
if (popup != null) {
popup.setVisible(false);
}
}
});
}
public static void main(String[] args) throws Exception {
Class lnfClass = Class.forName("com.sun.java.swing.plaf.windows.WindowsLookAndFeel", true,
Thread.currentThread().getContextClassLoader());
LookAndFeel feel = (LookAndFeel) lnfClass.newInstance();
UIManager.setLookAndFeel(feel);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestJPopup().initUI();
}
});
}
}
Here is the magic line that fixes the problem:
UIManager.put("PopupMenu.consumeEventOnClose", Boolean.FALSE);
I found this after looking into the source code for the BasicPopupMenuUI class. Apparently this behaviour is a deliberate design choice according to the following comments in the code, but it sure feels like a bug to me.
// Ask UIManager about should we consume event that closes
// popup. This made to match native apps behaviour.
By the way, it happens in Java 5 and 6 too.
I want to create a Toolbar in my application. If you click a button on that toolbar, it will pop up a menu, just like in Eclipse's toolbar. I don't know how to do this in Swing. Can someone help me please? I've tried Google but found nothing.
This is way harder in Swing than it needs to be. So instead of pointing you to tutorials I've created a fully working example.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class ToolbarDemo {
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setPreferredSize(new Dimension(600, 400));
final JToolBar toolBar = new JToolBar();
//Create the popup menu.
final JPopupMenu popup = new JPopupMenu();
popup.add(new JMenuItem(new AbstractAction("Option 1") {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frame, "Option 1 selected");
}
}));
popup.add(new JMenuItem(new AbstractAction("Option 2") {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frame, "Option 2 selected");
}
}));
final JButton button = new JButton("Options");
button.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
popup.show(e.getComponent(), e.getX(), e.getY());
}
});
toolBar.add(button);
frame.getContentPane().add(toolBar, BorderLayout.NORTH);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
I don't see why this is harder than it needs to be or why you should use a MouseListener. The solution by Steve McLeod works, but where the menu appears depends on where the mouse was clicked. Why not just use an ActionListener as normally used for a JButton. It seems neither harder nor less hard.
final JPopupMenu menu = new JPopupMenu();
menu.add(...whatever...);
final JButton button = new JButton();
button.setText("My Menu");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
menu.show(button, button.getBounds().x, button.getBounds().y
+ button.getBounds().height);
}
});
This positions the menu about the same as a menu in a JMenuBar for me, and the position is consistent. You could place it differently by modifying the x and y in menu.show().
Here is a simple and nice class
import javax.swing.JPopupMenu;
import javax.swing.JToggleButton;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class MenuButton extends JToggleButton {
JPopupMenu popup;
public MenuButton(String name, JPopupMenu menu) {
super(name);
this.popup = menu;
addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ev) {
JToggleButton b = MenuButton.this;
if (b.isSelected()) {
popup.show(b, 0, b.getBounds().height);
} else {
popup.setVisible(false);
}
}
});
popup.addPopupMenuListener(new PopupMenuListener() {
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
MenuButton.this.setSelected(false);
}
#Override
public void popupMenuCanceled(PopupMenuEvent e) {}
});
}
}
I think it's the same as in AWT.
You should put an ActionCommand on that button and when it's executed show the pop-up menu according to the mouse coordinates.
See the section Bringing Up a Popup Menu, in How to Use Menus.
I'm not sure I understand you correctly but if you want to know how to make toolbars in Swing check this
Java Tutorials: How to Use Tool Bars and this
Java Tutorials: How to Use Actions
Above, Adam Goode asked,
Does your solution have the behavior where if you click the button again with the menu up, it pops up the menu again, instead of dismissing it?
This turned out to be a testing task. I finally solved it with an invokeLater to re-vanish the popup in that particular case. My solution also allows the client to tailor the button and the popup menu.
/**
* A button that will popup a menu.
* The button itself is a JLabel and can be adjusted with all
* label attributes. The popup menu is returned by getPopup;
* menu items must be added to it.
* <p>
* Clicks outside the menu will dismiss it.
*/
public class MenuButton extends JLabel
implements MouseListener, PopupMenuListener {
JPopupMenu popMenu;
#SuppressWarnings("")
public MenuButton() {
super();
popMenu = new JPopupMenu();
addMouseListener(this);
popMenu.addPopupMenuListener(this);
}
public JPopupMenu getPopup() { return popMenu; }
#Override
public void mousePressed(MouseEvent e) {
if ( ! popMenu.isShowing()) {
popMenu.show(this, 0, getBounds().height);
}
}
#Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
SwingUtilities.invokeLater(()->{
if (popMenu.isShowing()) {
// if shpwing, it was hidden and reshown
// by a mouse down in the 'this' button
popMenu.setVisible(false);
}
});
}
#Override public void mouseClicked(MouseEvent e) { }
#Override public void mouseReleased(MouseEvent e) { }
#Override public void mouseEntered(MouseEvent e) { }
#Override public void mouseExited(MouseEvent e) { }
#Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) { }
#Override public void popupMenuCanceled(PopupMenuEvent e) { }
} // end MenuButton
Sample invocation
MenuButton button = new MenuButton();
JPopupMenu menu = button.getPopup();
menu.add("Browse Sample");
menu.add("Save As ...");
Icon hamburger = IOUtils.loadIconResource(
IndexGofer.class, "images/hamburgerMenu.png");
(IOUtils is on page http://physpics.com/Java/tools/
You should use your own tool to load an icon.)
button.setIcon(hamburger);
button.setOpaque(false);