Reputation: 626
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);
}
}
Upvotes: 0
Views: 213
Reputation: 285405
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.
Upvotes: 4
Reputation: 324118
Check out Motion Using the Keyboard. The KeyboardAnimation
example show how you can get multiple KeyStrokes to work at one time to provide diagonal motion.
Basically you need to keep track of which keys have been pressed and then add together the motion for each key to get the combined motion.
Your code would obviously be different because you have a different type of action for each keystroke you might need to keep two different maps of keystrokes, one for the motion and one for the size change. Then every time the Timer fires you would query both maps separately to determine the combined changes.
Upvotes: 4