I have a simple JButton linked to an actionPerformed method. Now I want to bind a key to the button so that when i press this key on the keyboard it performs the same action it would perform by pressing the button with the mouse.
I found out this code is working but as you can see I have to write twice the code to perform when key is pressed or button is pressed.
Is there a way to avoid this?
import java.io.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.*;
public class provakey implements ActionListener{
private JButton bStart;
//Costruttore
public provakey() throws IOException{
initGUI();
}
private void initGUI(){
JFrame frame = new JFrame();
JPanel buttonPanel = new JPanel();
bStart = new JButton("Avvia");
bStart.addActionListener(this);
bStart.setActionCommand("start");
bStart.setBounds(140,10,150,40);
AbstractAction buttonPressed = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
if (bStart.isEnabled()){
System.out.println("pressed");
}
}
};
bStart.getActionMap().put("start", buttonPressed);
bStart.getInputMap(javax.swing.JComponent.WHEN_IN_FOCUSED_WINDOW).
put(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_A,0), "start");
buttonPanel.setLayout(null);
buttonPanel.setBounds(0,240,600,60);
buttonPanel.add(bStart);
frame.setLayout(null);
frame.add(buttonPanel);
frame.setTitle("Prova");
frame.setSize(600, 350);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
if ("start".equals(e.getActionCommand())) {
buttonPressed();
}
}
public void buttonPressed(){
System.out.println("pressed");
}
public static void main (String args[]) throws IOException {
provakey p = new provakey();
}
}
I have to write twice the code to perform when key is pressed or button is pressed.
An Action is an ActionListener.
So, you can add an Action to the button, so the same Action will be executed whether you click on the button or use the key binding:
//bStart.addActionListener(this);
bStart.addActionListener( buttonPressed );
There is no need to set the action command since you now have a unique class for your "Start" processing that is not shared by other buttons in your class.
See: https://stackoverflow.com/a/33739732/131872 for a working example.
Related
I have two classes that implement KeyListener and FocusListener interfaces. Neither works, but if I don't add the JButton by commenting or removing this: add(whiteJButton), then they do work. Could someone explain to me why this happens? Thanks in advance.
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class JFrames {
public static void main(String[] args) {
JFrame myJFrame = new JFrame("MyJFrame");
myJFrame.setBounds(400, 400, 500, 500);
myJFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyJPanel myJPanel = new MyJPanel();
myJFrame.add(myJPanel);
// Doesn't work
myJFrame.addKeyListener(new MyKeyListener());
// Doesn't work
myJFrame.addFocusListener(new MyFocusListener());
myJFrame.setVisible(true);
}
static class MyJPanel extends JPanel {
public MyJPanel() {
JButton whiteJButton = new JButton("WHITE");
add(whiteJButton);
whiteJButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setBackground(Color.WHITE);
}
});
}
}
}
class MyKeyListener implements KeyListener {
#Override
public void keyTyped(KeyEvent e) {
System.out.println("keyTyped: " + e.getKeyChar());
}
#Override
public void keyPressed(KeyEvent e) {
System.out.println("keyPressed: " + e.getKeyChar());
}
#Override
public void keyReleased(KeyEvent e) {
System.out.println("keyReleased: " + e.getKeyChar());
}
}
class MyFocusListener implements FocusListener {
#Override
public void focusGained(FocusEvent e) {
System.out.println("focusGained");
}
#Override
public void focusLost(FocusEvent e) {
System.out.println("focusLost");
}
}
The problem is that the button steals focus from the JFrame, preventing the focus listener from working (focus is already gone). Some solutions if you absolutely need to use a KeyListener are kludges, including making the JButton not focusable: whiteJButton.setFocusable(false);, but if you do this, you need to do this for all components added. And yes you can request focus as the other answer suggests, but it should be requestFocusInWindw(), not requestFocus() (many similar questions explain why this is so). And if you do this and the components are still focusable, then the whole thing breaks down if a component gains focus -- not good.
Better (as per comments) is to use Key Bindings which don't require focus to work if you use the correct InputMap. Note that key bindings is how Swing itself traps keystrokes for components, so using this would follow with the Swing general structure and contracts. The problem with Key Bindings is that you have to bind each key that you wish to trap, but you can use for loops to assist with this.
Another solution is to use a KeyEventDispatcher to the keyboard focus manager:
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() {
#Override
public boolean dispatchKeyEvent(KeyEvent e) {
// code goes here
return false;
}
});
Example using Key Bindings:
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class KeyboardFun extends JPanel {
private InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
private ActionMap actionMap = getActionMap();
public KeyboardFun() {
setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
setLayout(new GridLayout(0, 8, 3, 3));
for (char c = 'A'; c <= 'Z'; c++) {
final String text = String.valueOf(c);
JButton button = new JButton(text);
button.addActionListener(e -> {System.out.println("Key pressed: " + text);});
add(button);
setBinding(c, button);
}
}
private void setBinding(char c, final JButton button) {
KeyStroke keyStroke = KeyStroke.getKeyStroke(Character.toLowerCase(c));
inputMap.put(keyStroke, keyStroke.toString());
actionMap.put(keyStroke.toString(), new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
button.doClick();
}
});
}
private static void createAndShowGui() {
KeyboardFun mainPanel = new KeyboardFun();
JFrame frame = new JFrame("KeyboardFun");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
You must focus the object that you add listener, try add listener to "myJFrame". You can't focus JPanel.
Your JPanel is probably isn't focusable at start and your JButton gets Focus.
So you can also add this codes to "myJPanel" :
setFocusable(true);
requestFocusInWindow();
I am displaying a JOptionPane suppose A on a button click from JFrame, and again displaying another JOptionPane suppose B on a button click from JOptionPane A, and I have a button on JOptionPane B suppoce button1, on the click event of button1, I am using code JOptionPane.getRootFrame().dispose() for closing the JOptionPane B, but it closes both A and B, please help me how can close only B but not A.
here is my sample code
i want second JOptionPane must be open
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class SampleCode extends JFrame {
public SampleCode() {
setSize(new Dimension(500, 500));
setLocation(450, 150);
but1 = new JButton("Click me");
add(but1);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
but1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
but1Function();
}
});
}
public static void main(String args[]) {
new SampleCode();
}
void but1Function() {
JPanel panel1 = new JPanel();
JButton but2 = new JButton("Open new dialog");
panel1.add(but2);
but2.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JPanel pan2 = new JPanel();
JButton but3 = new JButton("click me to close");
pan2.add(but3);
but3.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.getRootFrame().dispose();
}
});
JOptionPane.showMessageDialog(null, pan2);
}
});
JOptionPane jp = new JOptionPane(panel1, JOptionPane.CLOSED_OPTION,
JOptionPane.DEFAULT_OPTION, null, new Object[] {}, null);
JDialog dialog = jp.createDialog(null, "This one must be remain open");
dialog.setLocation(500, 200);
dialog.setSize(new Dimension(345, 200));
dialog.setVisible(true);
}
JButton but1;
}
You don't want to get the root frame nor dispose of it. You want to get the window that is displaying the JOptionPane, a Window that should be a modal JDialog. So instead, use SwingUtilities.getWindowAncestor(someComponentInJOptionPane), and call dispose() on that Window if you want to programmatically dispose of your JOPtionPane.
import java.awt.Component;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class OptionPaneFun {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#SuppressWarnings("serial")
public void run() {
final JPanel panel1 = new JPanel();
panel1.add(new JButton(new AbstractAction("Show new option pane") {
{
putValue(MNEMONIC_KEY, KeyEvent.VK_S);
}
#Override
public void actionPerformed(ActionEvent e1) {
final JPanel panel2= new JPanel();
panel2.add(new JButton(new AbstractAction("Dispose of this option pane") {
{
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
}
#Override
public void actionPerformed(ActionEvent e2) {
Component comp = (Component) e2.getSource();
Window win = SwingUtilities.getWindowAncestor(comp);
if (win != null) {
win.dispose();
}
}
}));
JOptionPane.showMessageDialog(panel1, panel2);
}
}));
JOptionPane.showMessageDialog(null, panel1);
}
});
}
}
The static method "getRootFrame()" returns your root frame which is the only one and it's the same for both your components (A and B). What you need to do - you have to put two frames in your root frame (call them frameA and frameB) and put paneA to frameA and paneB to frameB. Instead of calling this static method just invoke frameB.dispose() on reference frameB which you already have.
Try to add
panel.validiate();
After the dispose command. I had the same problem once and it helped a lot when I used this trick.
Basically when you add this command, it is telling the frame to validate or actually do it.
Read the oracle docs for more info.
My problem is the following: In my application the user clicks a button which brings up a dialog box (a custom jOptionPane). This dialog contains a JTextArea in which the user will type a response, which will then be processed by the application, however I would like this JTextArea (which will hold the user's input and currently contains example text like "Write your answer here") to be automatically highlighted.
I can do this normally, by calling requestFocusInWindow() followed by selectAll() on the JTextArea however there seems to be a problem when this is done using a JOptionPane which I'm guessing is to do with the fact that the focus cannot shift to the JTextArea successfully.
I've made a SSCCE to demonstrate this clearly, and hopefully get an answer from one of you guys as to how I can make this possible. Thanks in advance!
Class 1/2 : Main
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Main extends JFrame{
public static void main(String[] args) {
Main main = new Main();
main.go();
}
private void go() {
JPanel background = new JPanel();
JPanel mainPanel = new ExtraPanel();
((ExtraPanel) mainPanel).setupPanel();
JButton testButton = new JButton("Test the jOptionPane");
testButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
optionPaneTest();
}
});
background.add(mainPanel);
background.add(testButton);
getContentPane().add(background);
pack();
setVisible(true);
}
private void optionPaneTest() {
JPanel testPanel = new ExtraPanel();
((ExtraPanel) testPanel).setupPanel();
int result = JOptionPane.showConfirmDialog(null, testPanel,
"This is a test", JOptionPane.OK_CANCEL_OPTION);
}
}
----------------------------------------------------------------------------- Class 2/2 : ExtraPanel
import javax.swing.JPanel;
import javax.swing.JTextArea;
public class ExtraPanel extends JPanel{
public void setupPanel() {
JTextArea textArea = new JTextArea();
textArea.setText("Write your response here");
textArea.requestFocusInWindow();
textArea.selectAll();
add(textArea);
}
}
Just add
textArea.getCaret().setSelectionVisible(true)
After textArea.selectAll();
If you want focus in the TextArea so that the user can immediately start typing, you can trigger the selection using the ancestor added event.
public void setupPanel() {
final JTextArea textArea = new JTextArea();
textArea.setText("Write your response here");
textArea.addAncestorListener(new AncestorListener() {
public void ancestorRemoved(AncestorEvent event) { }
public void ancestorMoved(AncestorEvent event) { }
public void ancestorAdded(AncestorEvent event) {
if (event.getSource() == textArea) {
textArea.selectAll();
textArea.requestFocusInWindow();
}
}
});
add(textArea);
}
How can I assign two buttons to share the same class for handling events in Java/swing?
For example, I have this:
private class BtnEvtHandler implements ActionListener {
private int counter=10;
public void actionPerformed(ActionEvent e) {
gs.setX(counter);
gs.repaint();
counter=counter+10;
}
public void actionPerformed(ActionEvent e) {
//action for move button
}
}
JButton jumpBtn= new JButton("JUMP");
BtnEvtHandler okButtonHandler= new BtnEvtHandler();
(jumpBtn).addActionListener(okButtonHandler);
menuPanel.add(jumpBtn);
Now I want to add another button as below which can have the same class as event handler but dispatches to different actionPerformed as mentioned in above code.
JButton moveBtn= new JButton("MOVE");
menuPanel.add(moveBtn);
(moveBtn).addActionListener(okButtonHandler);
You can't reuse one ActionListener and expect it to call a different method depending on the button you attach it to. The contract of ActionListener has one method that gets called. But you can check the source of the event and have flow control based on that. Here's an example:
package com.sandbox;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
public class SwingSandbox {
public static void main(String[] args) throws IOException {
JFrame frame = buildFrame();
JPanel pane = new JPanel();
MyActionListener myActionListener = new MyActionListener();
JButton button1 = new JButton("Button1");
button1.addActionListener(myActionListener);
pane.add(button1);
JButton button2 = new JButton("Button2");
button2.addActionListener(myActionListener);
pane.add(button2);
frame.add(pane);
}
private static JFrame buildFrame() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(200, 200);
frame.setVisible(true);
return frame;
}
private static class MyActionListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
JButton source = (JButton) e.getSource();
if ("Button1".equals(source.getText())) {
System.out.println("You clicked button 1");
} else {
System.out.println("You clicked button 2");
}
}
}
}
In the Java file below, I create a frame containing a panel, which then nests a second panel. I'm trying to listen for key strokes in the nested panel. My approach is to use an input map and an action map. I've found if I only have an input map for the nested panel, things work as expected. However, if the parent panel also has an input map, key stroke events are not passed to the nested panel. You can observe this behavior by commenting and uncommenting the first call to getInputMap().put. Does anyone have a solution for this?
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class InputMapTest extends JPanel {
public InputMapTest() {
super(new BorderLayout());
JPanel panel = new JPanel();
KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
getInputMap().put(ks, "someAction");
getActionMap().put("someAction", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("here1");
}
});
ks = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0);
panel.getInputMap().put(ks, "someOtherAction");
panel.getActionMap().put("someOtherAction", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("here2");
}
});
add(panel);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.getContentPane().add(new InputMapTest());
frame.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
see Oracle tutorial How to use KeyBindings
you miss there set focus to JPanel panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(...)