I'm running an application with similar behaviour to that in the test case below.
The problem is that when you switch focus to a different window by clicking on it and keeping the below application frame in view and then click directly into the text field with the focusGained listener, close the dialog and then all key input will be lost to the all of the text fields in the application.
If you click anywhere in the application first or the icon in the task bar to gain focus back then this does not occur.
This is Java 8 specific - in Java 7 it will not lose focus, not sure about java 9 but that is not an option anyway
The test case below demonstrates this behaviour.
public class FocusTest extends JFrame
{
JTextField noFocus;
public static void main(String[] args)
{
FocusTest ft = new FocusTest();
ft.setVisible(true);
}
public FocusTest()
{
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setBounds(100,100,300,150);
setLayout(new GridLayout(3,1, 2, 2));
setTitle("Losing keyboard...");
noFocus = new JTextField();
add(noFocus);
JTextField jft = new JTextField();
jft.addFocusListener(new FocusAdapter()
{
#Override
public void focusGained(FocusEvent e)
{
createDialog().setVisible(true);
noFocus.requestFocusInWindow();
}
});
add(jft);
JButton jb = new JButton("OPEN");
jb.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getActionCommand().equals("OPEN"))
createDialog().setVisible(true);
}
});
add(jb);
}
private JDialog createDialog()
{
final JDialog jd = new JDialog(this, true);
jd.setLocationRelativeTo(this);
jd.setLayout(new BorderLayout());
jd.getContentPane().add(new JTextField(), BorderLayout.CENTER);
JButton jb = new JButton("Close");
jb.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getActionCommand().equals("Close"))
jd.dispose();
}
});
jd.getContentPane().add(jb, BorderLayout.SOUTH);
jd.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
jd.pack();
return jd;
}
}
Not exactly sure what's happening, but one solution is to use a SwingUtilities.invokeLater():
#Override
public void focusGained(FocusEvent e)
{
noFocus.requestFocusInWindow();
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createDialog().setVisible(true);
}
});
}
This will allow the text field to get focus properly before the dialog is made visible.
My Program has a JMenuBar with JMenuItems.
They have a ActionListener, and I set a Shortcut with setAccelerator.
Now I am hiding the menu bar when the window become unfocused, to get more space for a displayed image.
But after the first hiding of the menubar, the hotkeys just stop working.
How can I fix that?
I created a little example code to illustrate that strange behavior:
import javax.swing.*;
import java.awt.event.*;
class Example extends JFrame{
public static void main(String[] args) {
new Example(); //main is static
}
static JMenuBar menubar; //be accessable for the ActionListener
Example() {
//JPanel
this.setSize(50,50);
this.setVisible(true);
//Menubar, static
menubar = new JMenuBar();
this.setJMenuBar(menubar);
//Menu
JMenu filemenu = new JMenu("File");
menubar.add(filemenu);
//Item
JMenuItem menuitem = new JMenuItem("Do Something...");
filemenu.add(menuitem);
menuitem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, KeyEvent.SHIFT_DOWN_MASK)); // Shift + D
menuitem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Action!");
}
});
JButton button = new JButton("Show/Hide menubar");
this.add(button);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Toggle Bar!");
menubar.setVisible(!menubar.isVisible()); //Toggle
}
});
}
}
For reference:
I'm using Java 1.7.0_60-ea (Java 7) on a Mac.
But this error occurs independent of using the Mac native menu bar or the normal java menu bar inside the JFrame.
You could try to add global keybindings. How to add keybindings is explained here.
Here is an example of what you could do:
//Any component that is always visible in the window (like the image)
JComponent c;
//Get input and action map
InputMap imap = c.getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap amap = c.getActionMap();
//Put keybinding and action
imap.put(KeyStroke.getKeyStroke("shift D"), "doSomething");
amap.put("doSomething", anAction);
Note that it only works in the focused window. But should work regardless of the menubar being visible or not.
I did a bit of googling and poking around in SO but all examples are find are for cases when the JMenuItem is enabled.
Context for what I'm trying to do is that I want my disabled JMenuItem (because of limited privileges), when clicked, to display a pop up box requesting that the user upgrade so that they can access said JMenuItem.
The following is a stripped down version of what I currently have, nothing got printed out on the command line:
public class ExportMenuItem extends JMenuItem
{
public ExportMenuItem()
{
super("Menu Item Name");
addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent mouseEvent)
{
if (!isEnabled())
{
JOptionPane.showMessageDialog(editor.getFrame(), "Hello world.");
System.out.println("Sys print hello.");
}
System.out.println("Sys print hello outside.");
}
});
}
}
is this what you are looking for?
import javax.swing.*;
import java.awt.event.*;
public class ExportMenuItem extends JMenuItem{
public ExportMenuItem(){
super("menu item");
addMouseListener(new MouseListener(){
#Override
public void mouseClicked(MouseEvent mouseEvent){
if (!isEnabled()) {
JOptionPane.showMessageDialog(null, "Upgrade me!");
}//end of if
}//end of mouseClicked
public void mouseExited(MouseEvent mouseEvent){}
public void mouseEntered(MouseEvent mouseEvent){}
public void mouseReleased(MouseEvent mouseEvent){}
public void mousePressed(MouseEvent mouseEvent){}
// And the remaining methods to implement...
});//end of anonymous class
}//end of constructor
public static void main(String[] a){
JFrame f = new JFrame();
JMenu menu = new JMenu("menu");
JMenuBar menuBar = new JMenuBar();
f.setJMenuBar(menuBar);
f.setSize(300, 300);
f.setVisible(true);
menuBar.add(menu);
JMenuItem item = new ExportMenuItem();
menu.add(item);
item.setEnabled(false);
}//end of main
}//end of class
Maybe a complete different approach, that is more logical for users:
Place a describing text behind the menu item:
private void addRestartHint(JMenuItem m, String text) {
final String spaceholder = " ";
String t = m.getText() + spaceholder;
m.setLayout(new BorderLayout());
m.setText(t);
m.add(new JLabel(text), BorderLayout.EAST);
}
I'm having difficulty using anonymous inner classes with actionListener. Can someone explain to me what is wrong with my code and how to use anonymous inner classes with actionListener. I'm trying to make a menu bar in one class and the action listener in the other. I ran into some difficulty when I tried using anonymous inner classes. The java website wasn't clear. Can you please explain it to me and help me fix my code.
public class Listener implements ActionListener {
HangmanView hangmanView = new HangmanView();
JFrame dialogFrame = new JFrame();
ImageIcon logo = new ImageIcon("logo.png");
public void listener1() {
hangmanView.getMenuItem().addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {// right click key
JOptionPane.showMessageDialog(dialogFrame, "Developer: Joe"
, "Developer",
JOptionPane.INFORMATION_MESSAGE, logo);
}// end actionPerformed method
});
}
}
another class:
public class HangmanView {
public JMenuItem getMenuItem() {
JMenuItem menuItem = new JMenuItem("Developer", KeyEvent.VK_T);
menuItem.addActionListener(new Listener());
return menuItem;
}
public JMenuBar menuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("File");
menuBar.add(menu);
menu.add(getMenuItem());// return here
return menuBar;
}
If you're trying to implement the listener for different JMenuItems, what I would do instead is create a custom Action class that you can use for multiple JMenuItems, as JMenuItems are a good example of when to use an Action.
private class MyAction extends AbstractAction {
String name;
public MyAction(String name, Icon icon) {
super(name, icon);
this.name = name;
}
public MyAction(String name, Icon icon, String desc,
Integer mnemonic, KeyStroke accelorator) {
super(name, icon);
putValue(Action.SHORT_DESCRIPTION, desc);
putValue(Action.MNEMONIC_KEY, mnemonic);
putValue(Action.ACCELERATOR_KEY, accelorator);
this.name = name;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (name) {
case "menu1Action":
// do something for menuItem1
break;
case "menu2Action":
// do something for menuItem2
break;
case "menu3Action":
// do something for menuItem3
break;
}
}
}
Have this class as an inner class of HangmanView. You can then create an instance of this custom Action class for each JMenuItem. Here's a example
Action menu1Action = new MyAction(
/* arg 1 */ "menu1Action",
/* arg 2 */ someIcon,
/* arg 3 */ "Some Short description of the action",
/* arg 4 */ new Integer(KeyEvent.VK_T),
/* arg 5 */ KeyStroke.getKeyStroke(KeyEvent.VK_T, ActionEvent.CTRL_MASK));
The first argument is the name of the action. This name will be the name that you will see in the menu
The second argument is the icon that you will see in the menu next to the name.
The third argument is the Description of the menu item action
The fourth argument is the Mnemonic (i.e. Alt + T).
The fifth argument is the Accelerator (i.e. Ctrl + T).
When you add an Action to a JMenu, the title of that Action will automatically get placed as what you see in the JMenu. So all you need to do is add this custom Action to your JMenu. You don't ever have to actually create a JMenuItem at all. The Action will serve as the replacement for the JMenuItem. Just add all your MyAction objects to the JMenu.
menu.add(menu1Action);
What I have left out, is the implementation for each separate switch case in the actionPerformed. The case will be what you name the action in the constructor. Because I named the Action "menu1Action", I should have the corresponding name in the the switch case. In that case, you can do your JOptionPane or what ever else you wish to perform when that JMenuItem is clicked or accessed by keyboard.
Another great benefit of using an Action is that it can serve multiple purposes. With the same MyAction menu1Action you created, you can use the same Action for a JToolBar. Without any alteration to the above menu1Action, you could just do this:
JTooBar toolbar = new JToolBar();
toolbar.add(menu1Action);
Now in your tool bar and in your menu item, you have the same action. The tool bar will only show the icon and not the name.
Here is an example. What I do is create three different MyAction objects. One for left-alignment, one for center-alignment, and one for right-alignment. Each of these actions is used three separate times for three separate components, a menu item, a toll bar and a button
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ActionInterfaceDemo extends JFrame {
static JPanel buttonPanel = new JPanel();
static FlowLayout flowLayout = new FlowLayout();
public ActionInterfaceDemo(){
ImageIcon centerIcon = new ImageIcon(
ActionInterfaceDemo.class.getResource("image/centeralignment.png"));
ImageIcon rightIcon = new ImageIcon(
ActionInterfaceDemo.class.getResource("image/rightalignment.png"));
ImageIcon leftIcon = new ImageIcon(
ActionInterfaceDemo.class.getResource("image/leftalignment.png"));
Action leftAction = new MyAction("Left", leftIcon,
"Left alignment for the buttons in the panel",
new Integer(KeyEvent.VK_L),
KeyStroke.getKeyStroke(KeyEvent.VK_L, ActionEvent.CTRL_MASK));
Action rightAction = new MyAction("Right", rightIcon,
"Right alignment for the buttons in the panel",
new Integer(KeyEvent.VK_R),
KeyStroke.getKeyStroke(KeyEvent.VK_R, ActionEvent.CTRL_MASK));
Action centerAction = new MyAction("Center", centerIcon,
"Center alignment for the buttons in the panel",
new Integer(KeyEvent.VK_C),
KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK));
JMenuBar menuBar = new JMenuBar();
JMenu menuAlignment = new JMenu("Alignment");
setJMenuBar(menuBar);
menuBar.add(menuAlignment);
menuAlignment.add(leftAction);
menuAlignment.add(centerAction);
menuAlignment.add(rightAction);
JToolBar toolBar = new JToolBar("Alignment");
toolBar.setBorder(BorderFactory.createLineBorder(Color.BLUE));
toolBar.add(leftAction);
toolBar.add(centerAction);
toolBar.add(rightAction);
buttonPanel.setLayout(flowLayout);
JButton jbtLeft = new JButton(leftAction);
JButton jbtCenter = new JButton(centerAction);
JButton jbtRight = new JButton(rightAction);
buttonPanel.add(jbtLeft);
buttonPanel.add(jbtCenter);
buttonPanel.add(jbtRight);
add(toolBar, BorderLayout.EAST);
add(buttonPanel, BorderLayout.CENTER);
pack();
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run(){
new ActionInterfaceDemo();
}
});
}
private class MyAction extends AbstractAction {
String name;
public MyAction(String name, Icon icon, String desc,
Integer mnemonic, KeyStroke accelorator) {
super(name, icon);
putValue(Action.SHORT_DESCRIPTION, desc);
putValue(Action.MNEMONIC_KEY, mnemonic);
putValue(Action.ACCELERATOR_KEY, accelorator);
this.name = name;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (name) {
case "Left":
flowLayout.setAlignment(FlowLayout.LEFT);
break;
case "Right":
flowLayout.setAlignment(FlowLayout.RIGHT);
break;
case "Center":
flowLayout.setAlignment(FlowLayout.CENTER);
break;
}
buttonPanel.revalidate();
}
}
}
You can press the "Left" in either the menu, the toolbar, or the button and they will produce the same result, as they are derived from the same Action.
Here are the images I used if you want to test it out
Note You don't have to use either of these exact constructors. You can create your own with different arguments. This is just a custom one I like to use.
Aslo See How to use Action tutorial
Listener, being an ActionListener through its inheritance hierarchy,
public class Listener implements ActionListener {
needs to implement an actionPerfomed(ActionEvent) method
#Override
public void actionPerformed(ActionEvent e) {
// implement it
}
However, since you seem to be adding an anonymous ActionListener, just don't have your Listener class implement ActionListener. Remove that bit.
Your object-oriented programming is all over the place. Your code in the OP looks like it's supposed to be some kind of GUI with different classes somehow working together but it's just creating new objects all over the place. There are too many reasons it doesn't work. I'd suggest you stick to something simple until you have a better grasp on how all this works.
You also got a very good suggestion in your other question that's a lot like this one but you haven't really followed it. Your code does something completely different.
Here is a very basic GUI. You have one object that has everything as fields. Everything is in one place. The containing object is the listener and decides what to do based on the event source. You should stick with a design like this until you are more comfortable with OOP.
public class HangmanView
implements ActionListener {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new HangmanView().setFrameVisible(true);
}
});
}
private JFrame theFrame = new JFrame("Main Window");
private JPanel theContent = new JPanel();
private JMenuBar theBar = new JMenuBar();
private JMenu fileMenu = new JMenu("File");
private JMenuItem exitMenuItem = new JMenuItem("Exit");
private JMenuItem devMenuItem = new JMenuItem("Developer");
public HangmanView() {
assert SwingUtilities.isEventDispatchThread();
exitMenuItem.addActionListener(this);
devMenuItem.addActionListener(this);
fileMenu.add(exitMenuItem);
theBar.add(fileMenu);
theBar.add(devMenuItem);
theContent.setPreferredSize(new Dimension(500, 500));
theFrame.setJMenuBar(theBar);
theFrame.setContentPane(theContent);
theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
theFrame.pack();
theFrame.setLocationRelativeTo(null);
}
public void setFrameVisible(boolean vis) {
theFrame.setVisible(vis);
}
#Override
public void actionPerformed(ActionEvent ae) {
if(ae.getSource() == devMenuItem) {
showDevDiag();
} else if(ae.getSource() == exitMenuItem) {
systemExit();
}
}
private void showDevDiag() {
JOptionPane.showMessageDialog(
theFrame,
"Developer: Joe",
"Developer",
JOptionPane.INFORMATION_MESSAGE,
null
);
}
private void systemExit() {
System.exit(0);
}
}
if I should use anonymous class, that's what should be done:
public static void main(String args[]){
/*bla bla bla...*/
JButton button1=new JButton("button1");
button1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae){
/*bla bla bla...*/
}
};
}
On the other hand, I always do this:
public class Class implements Runnable,ActionListener
{
private static Map<Thread,ActionEvent> THREAD_ATTRIB
=new HashMap<Thread,ActionEvent> (0);
JButton button1=new JButton("button1");
JButton button2=new JButton("button2");
//constructor
public Class(){
this.button1.addActionListener(this);
this.button2.addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent ae){
Thread thread=new Thread(this);
THREAD_ATTRIB.put(thread,ae);
thread.start();
}
#Override
public void run(){
ActionEvent ae=THREAD_ATTRIB.get(Thread.currentThread());
if(ae!=null){
Object source=ae.getSource();
if(source.equals(this.button1){
/*bla bla bla...*/
} else if(source.equals(this.button2){
/*bla bla bla...*/
}
} else{
/*bla bla bla...*/
}
}
}
for some reason, I am unable to compile this in Eclipse. The "quit" menuItem works and no other menuItem works. Why is that?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class GUI{
private JFrame frame;
public GUI(){
makeFrame();
}
//This method makes the overall GUI and adds panels, labels,
//buttons, and everything else to the GUI.
public void makeFrame(){
frame = new JFrame("Tower Defense");
Container contentPane = frame.getContentPane();
makeMenus();
JButton shootButton = new JButton("Shoot");
contentPane.add(shootButton);
frame.pack();
frame.setVisible(true);
}
//This method makes the menu and all of the items contained
//in the menu which is then called by the makeFrame() method.
//I also add the menuItem's various ActionLiteners here.
public void makeMenus(){
JMenuBar menubar = new JMenuBar();
frame.setJMenuBar(menubar);
JMenu fileMenu = new JMenu("File");
JMenu actionsMenu = new JMenu("Actions");
JMenu buildMenu = new JMenu("Build");
menubar.add(fileMenu);
menubar.add(actionsMenu);
menubar.add(buildMenu);
JMenuItem sellItem = new JMenuItem("Sell");
JMenuItem quitItem = new JMenuItem("Quit");
JMenuItem turretsItem = new JMenuItem("Turrets");
JMenuItem minesItem = new JMenuItem("Mines");
JMenuItem workersItem = new JMenuItem("Workers");
quitItem.addActionListener(new QuitActionListener());
sellItem.addActionListener(new SellActionListener());
turretsItem.addActionListener(new TurretsActionListener());
minesItem.addActionListener(new MinesActionListener());
workersItem.addActionListener(new WorkersActionListener());
fileMenu.add(quitItem);
actionsMenu.add(sellItem);
buildMenu.add(turretsItem);
buildMenu.add(minesItem);
buildMenu.add(workersItem);
}
//Main method. It creates a new GUI.
public static void main(String args []){
GUI gui = new GUI();
}
class QuitActionListener implements ActionListener{
public void actionPerformed(ActionEvent e){
System.exit(0);
}
}
class SellActionListener{
public void actionPerformed(ActionEvent e){
System.exit(0);
}
}
class TurretsActionListener{
public void actionPerformed(ActionEvent e){
System.exit(0);
}
}
class MinesActionListener{
public void actionPerformed(ActionEvent e){
System.exit(0);
}
}
class WorkersActionListener{
public void actionPerformed(ActionEvent e){
System.exit(0);
}
}
class ShootActionListener{
public void actionPerformed(ActionEvent e){
System.exit(0);
}
}
}
It is not compiling because the actions listeners (like SellActionListener, for example) that you are providing (all bar the quit action) are not implementing ActionListener. The method addActionListener expects an object that implements ActionListener.
This:
class SellActionListener{
public void actionPerformed(ActionEvent e){
System.exit(0);
}
}
Needs to become this:
class SellActionListener implements ActionListener{
public void actionPerformed(ActionEvent e){
System.exit(0);
}
}
(along with the other action listeners)
For posterity
These sort of errors can usually be figured out from the compiler feedback. When eclipse said that there were compile errors, you should be able to see the error details. I guess it would say something like "class SellActionListener does not implement the interface ActionListener" (or something along those lines). If you google that error message you might be able to find the answer faster that waiting for someone to answer your specific question.