As of now, I can only get one of my key bindings to work one at a time. It's usually the first one created. They each have their own class to go to when I press a key so I don't understand why all of my keys would not work. Is it because they all share the same panel? Currently I have three action classes, UpAction, PlusAction, and MinusAction. UpAction moves the rectangle up and the other two increase/decrease the size of the rectangle. Another problem I ran into was that when I could move the rectangle upward, when I would click my increase/decrease buttons, my up command would not work anymore. Would I have to write something for my code to regain foccus on the up arrow key again?
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.geom.Rectangle2D;
import javax.swing.Icon;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
public class Rectangle extends JPanel {
private int height;
private int width;
private int x;
private int y;
private Graphics2D g2;
private Action plusAction;
private Action minusAction;
private Action up;
private static JButton buttonIncrease;
private static JButton buttonDecrease;
public Rectangle(int width, int height,int x, int y)
{
this.height = height;
this.width = width;
this.x = x;
this.y = y;
JLabel label = new JLabel();
buttonIncrease = new JButton(" Increase Size ");
buttonDecrease = new JButton(" Decrease Size ");
buttonIncrease.addActionListener(new ButtonListener());
buttonDecrease.addActionListener(new ButtonListener());
buttonIncrease.setActionCommand("1");
buttonDecrease.setActionCommand("2");
up = new UpAction();
getInputMap().put(KeyStroke.getKeyStroke("UP"), "doUpAction");
getActionMap().put("doUpAction", up);
plusAction = new PlusAction();
minusAction = new MinusAction();
buttonIncrease.getInputMap().put(KeyStroke.getKeyStroke(" released Q"), "doPlusAction");
buttonIncrease.getActionMap().put("doPlusAction", plusAction);
buttonDecrease.getInputMap().put(KeyStroke.getKeyStroke(" released A"), "doMinusAction");
buttonDecrease.getActionMap().put("doMinusAction", minusAction);
add(buttonIncrease);
add(buttonDecrease);
label.addKeyListener(new LabelListener());
label.setFocusable(true);
label.setOpaque(true);
this.add(label);
//label.requestFocusInWindow();
setVisible(true);
}
public Dimension getPreferredSize() {
return new Dimension(500,500);
}
static class PlusAction extends AbstractAction
{
//When the plus key is pressed, the increase button
//will be pressed
public void actionPerformed(ActionEvent arg0) {
buttonIncrease.doClick();
}
}
static class MinusAction extends AbstractAction
{
//When the minus key is pressed, the decrease button
//will be pressed
public void actionPerformed(ActionEvent arg0) {
buttonDecrease.doClick();
}
}
class UpAction extends AbstractAction
{
public void actionPerformed(ActionEvent e) {
y = y - 5;
repaint();
}
}
// Listener for increasing/decreasing size buttons
class ButtonListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
int action = Integer.parseInt(e.getActionCommand());
switch(action){
case 1:
height = height + 5;
width = width + 5;
repaint();
break;
case 2:
height = height - 5;
width = width - 5;
repaint();
break;
}
}
}
class LabelListener implements KeyListener {
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_DOWN)
{
y = y + 5;
repaint();
}
if (e.getKeyCode() == KeyEvent.VK_LEFT)
{
x = x - 5;
repaint();
}
if (e.getKeyCode() == KeyEvent.VK_UP)
{
y = y - 5;
repaint();
}
if (e.getKeyCode() == KeyEvent.VK_RIGHT)
{
x = x + 5;
repaint();
}
}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.g2 = (Graphics2D) g;
g2.drawString("X: " + x,10,20);
g2.drawString("Y: " + y,10,35);
g2.drawString("Width: " + width,10,55);
g2.drawString("Height: " + height,10,70);
g2.drawRect(x, y, width, height);
}
}
Test class:
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
JFrame f = new JFrame("Rectangle");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new Rectangle(40,20,250,250));
f.pack();
f.setVisible(true);
}
}
You're not setting the input map's condition correctly:
private static final int INPUT_CONDITION = JComponent.WHEN_IN_FOCUSED_WINDOW;
private int height;
private int width;
//...
public Rectangle(int width, int height, int x, int y) {
// .....
up = new UpAction();
// ******** note difference to the getInputMap method below
getInputMap(INPUT_CONDITION).put(KeyStroke.getKeyStroke("UP"),
"doUpAction");
getActionMap().put("doUpAction", up);
plusAction = new PlusAction();
minusAction = new MinusAction();
buttonIncrease.getInputMap(INPUT_CONDITION).put(
KeyStroke.getKeyStroke("released Q"), "doPlusAction");
buttonIncrease.getActionMap().put("doPlusAction", plusAction);
buttonDecrease.getInputMap(INPUT_CONDITION).put(
KeyStroke.getKeyStroke("released A"), "doMinusAction");
The default InputMap condition is JComponent.WHEN_FOCUSED, and the key binding will only work when the listened to component has focus. Better in your case to set it explicitly to JComponent.WHEN_IN_FOCUSED_WINDOW, and now it will work as long as the component is in a window that has focus.
You set it when you call getInputMap
. Rather than call it in a default way, getInputMap()
, I suggest that you call it like, getInputMap(INPUT_CONDITION)
, setting up the appropriate constant.
You've also got KeyListeners in that code -- why? And having a Graphics2D or Graphics class field is a recipe for possible disaster. I would get rid of the Graphics2D field and just use it locally in the paintComponent method or methods called off of paintComponent.
Edit
You ask:
Why would using Graphics2D globally be a bad thing? –
Because having the variable would make it tempting to use it, and if you used it outside of paintComponent, you're liable to run into a NullPointerException, because a Graphics object obtained from the JVM this way is a very short lived thing. Trust me, get rid of that variable.