Java-GUI Key Pressed Not Functioning Correctly - java

So I looked at tons of different threads about this but all of the suggestions of how to make it work; I have done, so it should work. I have gotten it to work before, and I am doing the same thing as when I had images move in another program of mine. Anyways, this is a space shooter like game. I am working on the class in charge of the player vs player. You will see the comment for spacebar and key 'e' will have a shooting in added for later. Shooting is another class, that way I can rapid fire and can control each bullet because there independent objects. Anyways, I did some test with printing out stuff, and I know the timer works. I know the move method is working, because I see my images move on screen. However, I have no control. I then put a print statement in the key pressed area, and it is not printing anything. So I know that is the code that is somehow wrong. So any help would be great as I am stumped. This is not for a class in college or high school. It is a personal project. Here is the code:
import javax.swing.*;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class PlayervsPlayer implements KeyListener,ActionListener{
private JFrame window;
private Timer timer;
private int win_size;
private int ship_clearence=45;
private int speed=3;
private final int stop1=0;
private final int stop2=1;
private final int left1=2;
private final int left2=3;
private final int right1=4;
private final int right2=5;
private int dir1=left1;
private int dir2=right2;
Sprite background;
Sprite ship1=new Sprite("Spaceship.png");
Sprite ship2=new Sprite("Spaceship2.png");
public PlayervsPlayer(JFrame w,Sprite backdrop,int win_s) {
window=w;
background=backdrop;
win_size=win_s;
window.addKeyListener(this);
}
public void pvpmain() {
System.out.println("Player vs Player working!");
ship1.setSize(125,125);
ship1.setLocation((int)((win_size/2)-(ship1.getWidth()/2)),win_size-ship1.getHeight()-ship_clearence);
ship2.setSize(125,125);
ship2.setLocation((int)((win_size/2)-(ship1.getWidth()/2)),0);
window.add(ship1,0);
window.add(ship2,0);
window.repaint();
timer = new Timer( 10, this );
timer.start();
}
public void keyPressed(KeyEvent e) {
int key=e.getKeyCode();
if ( key == KeyEvent.VK_RIGHT ){
System.out.println("Right Key");//
dir1 = right1;
}
else if (key==KeyEvent.VK_LEFT) {
dir1=left1;
}
else if (key==KeyEvent.VK_SPACE) {
//Nothing for now as for shooting
}
if (key==KeyEvent.VK_Q) {
dir2=left2;
}
else if (key==KeyEvent.VK_W) {
dir2=right2;
}
else if (key==KeyEvent.VK_E) {
//Nothing for now as for shooting
}
}
public void move() {
if (dir1==left1) {
if (ship1.getX()<=speed) {
dir1=stop1;
}
else {
ship1.setLocation(ship1.getX()-speed,ship1.getY());
}
}
else if (dir1==right1) {
if (ship1.getX()+ship1.getWidth()>=win_size-speed) {
dir1=stop1;
}
else {
ship1.setLocation(ship1.getX()+speed,ship1.getY());
}
}
if (dir2==left2) {
if (ship2.getX()<=speed) {
dir2=stop2;
}
else {
ship2.setLocation(ship2.getX()-speed,ship2.getY());
}
}
else if (dir2==right2) {
if (ship2.getX()+ship2.getWidth()>=win_size-speed) {
dir2=stop2;
}
else {
ship2.setLocation(ship2.getX()+speed,ship2.getY());
}
}
}
public void actionPerformed(ActionEvent e) {
if ( e.getSource() == timer ){
System.out.println("Timer Working!");
move();
}
}
public void keyTyped(KeyEvent e) {
//Nothing
}
public void keyReleased(KeyEvent e) {
//Nothing
}
}
Thanks in advance.

Your primary issue is related to the fact that KeyListener is unreliable in the application you are trying to use it. KeyListener requires that the component it is registered to is capable of receiving keyboard focus AND has keyboard focus before it will trigger key events. It's very easy for focus to be stolen by other components.
The most reliable solution to your problem is to make use of the key bindings API, which was in part, developed to solve this very issue.
You may also want to have a read of How to Use Actions to understand how this part of the API works
So, adapting the code from Trying to move JLabels on a JPanel with GridLayout, which is the simplest example which is most closely aligned with what you seem to be trying to do, you could end up with something like...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel player;
public TestPane() {
player = makeLabel("P");
player.setSize(player.getPreferredSize());
add(player);
addKeyBinding("left", KeyEvent.VK_LEFT, new MoveAction(player, -4, 0));
addKeyBinding("right", KeyEvent.VK_RIGHT, new MoveAction(player, 4, 0));
addKeyBinding("up", KeyEvent.VK_UP, new MoveAction(player, 0, -4));
addKeyBinding("down", KeyEvent.VK_DOWN, new MoveAction(player, 0, 4));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void addKeyBinding(String name, int keyCode, Action action) {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(KeyStroke.getKeyStroke(keyCode, 0), name);
actionMap.put(name, action);
}
protected JLabel makeLabel(String text) {
JLabel label = new JLabel(text);
label.setBorder(new CompoundBorder(
new LineBorder(Color.GRAY),
new EmptyBorder(4, 4, 4, 4)));
return label;
}
public class MoveAction extends AbstractAction {
private final int xDelta, yDelta;
private final JComponent component;
public MoveAction(JComponent component, int xDelta, int yDelta) {
this.component = component;
this.xDelta = xDelta;
this.yDelta = yDelta;
}
#Override
public void actionPerformed(ActionEvent e) {
Point location = component.getLocation();
location.x += xDelta;
location.y += yDelta;
component.setLocation(location);
repaint();
}
}
}
}
But what about multiple, simultaneous, key stokes?
Well, this isn't a unique problem, which is most commonly solved by using a series of flags which determine if a key is currently been pressed or not. You then use these flags to make determinations about how best to move the object
So, adapting from the first example, you might end up with something like...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
public class Test2 {
public static void main(String[] args) {
new Test2();
}
public Test2() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public enum Direction {
UP, DOWN, LEFT, RIGHT;
}
public class Controller {
private Set<Direction> directions;
private JComponent player;
public Controller(JComponent player) {
this.player = player;
directions = new TreeSet<>();
}
public void released(Direction direction) {
directions.remove(direction);
updatePosition();
}
public void pressed(Direction direction) {
directions.add(direction);
updatePosition();
}
protected void updatePosition() {
Point location = player.getLocation();
if (directions.contains(Direction.UP)) {
location.y -= 4;
}
if (directions.contains(Direction.DOWN)) {
location.y += 4;
}
if (directions.contains(Direction.LEFT)) {
location.x -= 4;
}
if (directions.contains(Direction.RIGHT)) {
location.x += 4;
}
player.setLocation(location);
}
}
public class TestPane extends JPanel {
private JLabel player;
public TestPane() {
player = makeLabel("P");
player.setSize(player.getPreferredSize());
add(player);
Controller controller = new Controller(player);
addKeyBinding("left", KeyEvent.VK_LEFT, Direction.LEFT, controller);
addKeyBinding("right", KeyEvent.VK_RIGHT, Direction.RIGHT, controller);
addKeyBinding("up", KeyEvent.VK_UP, Direction.UP, controller);
addKeyBinding("down", KeyEvent.VK_DOWN, Direction.DOWN, controller);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void addKeyBinding(String name, int keyCode, Direction direction, Controller controller) {
InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(KeyStroke.getKeyStroke(keyCode, 0, true), name + "-released");
inputMap.put(KeyStroke.getKeyStroke(keyCode, 0, false), name + "-pressed");
actionMap.put(name + "-released", new MoveAction(direction, controller, true));
actionMap.put(name + "-pressed", new MoveAction(direction, controller, false));
}
protected JLabel makeLabel(String text) {
JLabel label = new JLabel(text);
label.setBorder(new CompoundBorder(
new LineBorder(Color.GRAY),
new EmptyBorder(4, 4, 4, 4)));
return label;
}
public class MoveAction extends AbstractAction {
private Direction direction;
private Controller controller;
private boolean released;
public MoveAction(Direction direction, Controller controller, boolean released) {
this.direction = direction;
this.controller = controller;
this.released = released;
}
#Override
public void actionPerformed(ActionEvent e) {
if (released) {
controller.released(direction);
} else {
controller.pressed(direction);
}
}
}
}
}
Next time, try to work with some ones code instead of forcing them to restructure and make a suggestion to use this key binding thing. If you think key binding is so great, why don't you modify my code with key binding instead, lets see how complicated it is then
Sigh - Because I shouldn't have to, even in your own words, you didn't want a "copy and paste" answer, but, apparent that is what you want ...
import javax.swing.JFrame;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.beans.PropertyChangeListener;
// I'd prefer to extend from JPanel, as it provides bases for
// self contained responsibility, but apparently, that's too "advanced"
public class PlayervsPlayer implements ActionListener {
private JFrame window;
private Timer timer;
private int win_size;
private int ship_clearence = 45;
private int speed = 3;
private final int stop1 = 0;
private final int stop2 = 1;
private final int left1 = 2;
private final int left2 = 3;
private final int right1 = 4;
private final int right2 = 5;
private int dir1 = left1;
private int dir2 = right2;
// Sprite background;
JLabel ship1 = new JLabel("Spaceship.png");
JLabel ship2 = new JLabel("Spaceship2.png");
public PlayervsPlayer(JFrame w, int win_s) {
window = w;
// background = backdrop;
win_size = win_s;
JComponent contenPane = (JComponent) w.getContentPane();
InputMap inputMap = contenPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = contenPane.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "player1-left");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "player1-right");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), "player2-left");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_E, 0), "player2-right");
// I'd prefer a single "Move Action" class which could
// make these updates, but that might be "too advanced"
actionMap.put("player1-left", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 1 - left");
dir1 = left1;
}
});
actionMap.put("player1-right", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 1 - right");
dir1 = right1;
}
});
actionMap.put("player2-left", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 2 - left");
dir2 = left2;
}
});
actionMap.put("player2-right", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 2 - right");
dir2 = right2;
}
});
}
public void pvpmain() {
System.out.println("Player vs Player working!");
ship1.setSize(125, 125);
ship1.setLocation((int) ((win_size / 2) - (ship1.getWidth() / 2)), win_size - ship1.getHeight() - ship_clearence);
ship2.setSize(125, 125);
ship2.setLocation((int) ((win_size / 2) - (ship1.getWidth() / 2)), 0);
window.add(ship1, 0);
window.add(ship2, 0);
window.repaint();
timer = new Timer(10, this);
timer.start();
}
public void move() {
if (dir1 == left1) {
if (ship1.getX() <= speed) {
dir1 = stop1;
} else {
ship1.setLocation(ship1.getX() - speed, ship1.getY());
}
} else if (dir1 == right1) {
if (ship1.getX() + ship1.getWidth() >= win_size - speed) {
dir1 = stop1;
} else {
ship1.setLocation(ship1.getX() + speed, ship1.getY());
}
}
if (dir2 == left2) {
if (ship2.getX() <= speed) {
dir2 = stop2;
} else {
ship2.setLocation(ship2.getX() - speed, ship2.getY());
}
} else if (dir2 == right2) {
if (ship2.getX() + ship2.getWidth() >= win_size - speed) {
dir2 = stop2;
} else {
ship2.setLocation(ship2.getX() + speed, ship2.getY());
}
}
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == timer) {
System.out.println("Timer Working!");
move();
}
}
}
But the players won't stop moving when you release a key...
Hmm, yes, that is true, but the original code didn't seem to have that functionality either
So, in the PlayervsPlayer constructor, we'd replace the existing bindings with something more like...
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "player1-left-pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "player1-right-pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "player2-left-pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_E, 0, false), "player2-right-pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "player1-left-released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "player1-right-released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "player2-left-released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_E, 0, true), "player2-right-released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "space");
// I'd prefer this to be a self containted unit of work, but for demonstration purposes
actionMap.put("player1-left-pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 1 - left");
dir1 = left1;
}
});
actionMap.put("player1-right-pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 1 - right");
dir1 = right1;
}
});
actionMap.put("player2-left-pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 2 - left");
dir2 = left2;
}
});
actionMap.put("player2-right-pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 2 - right");
dir2 = right2;
}
});
actionMap.put("player1-left-released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 1 - left");
dir1 = stop1;
}
});
actionMap.put("player1-right-released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 1 - stop");
dir1 = stop1;
}
});
actionMap.put("player2-left-released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 2 - stop");
dir2 = stop2;
}
});
actionMap.put("player2-right-released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 2 - stop");
dir2 = stop2;
}
});
actionMap.put("space", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Pew pew pew");
}
});
I also added Space because I forget to put it in the previous example
I would be interested in seeing how much longer key binding is to my code
My favourite topic - reduce and reuse. As I stated in my comments above, I'd prefer to have a single "move action" class which could change some kind of state, here I've used a Set, but you could pass an instance of PlayervsPlayer and have the MoveAction call a method on it, telling the class what action has occurred, I prefer this method as it decouples the code (makes it more re-usable)
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.Set;
import java.util.TreeSet;
// I'd prefer to extend from JPanel, as it provides bases for
// self contained responsibility, but apparently, that's too "advanced"
public class PlayervsPlayer implements ActionListener {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setLayout(null);
new PlayervsPlayer(frame, 400).pvpmain();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
private JFrame window;
private Timer timer;
private int win_size;
private int ship_clearence = 45;
private int speed = 3;
private final int stop1 = 0;
private final int stop2 = 1;
private final int left1 = 2;
private final int left2 = 3;
private final int right1 = 4;
private final int right2 = 5;
// private int dir1 = left1;
// private int dir2 = right2;
// Sprite background;
JLabel ship1 = new JLabel("Spaceship.png");
JLabel ship2 = new JLabel("Spaceship2.png");
private Set<Integer> playerKeys;
public PlayervsPlayer(JFrame w, int win_s) {
window = w;
// background = backdrop;
win_size = win_s;
playerKeys = new TreeSet<>();
playerKeys = new TreeSet<>();
playerKeys.add(left1);
playerKeys.add(right2);
JComponent contenPane = (JComponent) w.getContentPane();
InputMap inputMap = contenPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = contenPane.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "player1-left");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "player1-right");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), "player2-left");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_E, 0), "player2-right");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "player1-left-pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "player1-right-pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "player2-left-pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_E, 0, false), "player2-right-pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "player1-left-released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "player1-right-released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "player2-left-released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_E, 0, true), "player2-right-released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "space");
// I'd prefer this to be a self containted unit of work, but for demonstration purposes
actionMap.put("player1-left-pressed", new MoveAction(playerKeys, left1, false));
actionMap.put("player1-right-pressed", new MoveAction(playerKeys, right1, false));
actionMap.put("player2-left-pressed", new MoveAction(playerKeys, left2, false));
actionMap.put("player2-right-pressed", new MoveAction(playerKeys, right2, false));
actionMap.put("player1-left-released", new MoveAction(playerKeys, left1, true));
actionMap.put("player1-right-released", new MoveAction(playerKeys, right1, true));
actionMap.put("player2-left-released", new MoveAction(playerKeys, left2, true));
actionMap.put("player2-right-released", new MoveAction(playerKeys, right2, true));
actionMap.put("space", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Pew pew pew");
}
});
}
public void pvpmain() {
System.out.println("Player vs Player working!");
ship1.setSize(125, 125);
ship1.setLocation((int) ((win_size / 2) - (ship1.getWidth() / 2)), win_size - ship1.getHeight() - ship_clearence);
ship2.setSize(125, 125);
ship2.setLocation((int) ((win_size / 2) - (ship1.getWidth() / 2)), 0);
window.add(ship1, 0);
window.add(ship2, 0);
window.repaint();
timer = new Timer(10, this);
timer.start();
}
public void move() {
if (playerKeys.contains(left1)) {
if (ship1.getX() <= speed) {
playerKeys.clear();
} else {
ship1.setLocation(ship1.getX() - speed, ship1.getY());
}
} else if (playerKeys.contains(right1)) {
if (ship1.getX() + ship1.getWidth() >= win_size - speed) {
playerKeys.clear();
} else {
ship1.setLocation(ship1.getX() + speed, ship1.getY());
}
}
if (playerKeys.contains(left2)) {
if (ship2.getX() <= speed) {
playerKeys.clear();
} else {
ship2.setLocation(ship2.getX() - speed, ship2.getY());
}
} else if (playerKeys.contains(right2)) {
if (ship2.getX() + ship2.getWidth() >= win_size - speed) {
playerKeys.clear();
} else {
ship2.setLocation(ship2.getX() + speed, ship2.getY());
}
}
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == timer) {
move();
}
}
public class MoveAction extends AbstractAction {
private Set<Integer> keys;
private Integer action;
private boolean released;
public MoveAction(Set<Integer> keys, Integer action, boolean released) {
this.keys = keys;
this.action = action;
this.released = released;
}
#Override
public void actionPerformed(ActionEvent e) {
if (released) {
keys.remove(action);
} else {
keys.add(action);
}
}
}
}

Related

How to change value of integer on keyPress

I have a JLabel which has a string that has an integer, which starts at 0. I want it so that way, every time a key (like "w") is pressed, the integer goes up by 1. I have searched the web far and wide, but I have returned nothing (maybe because of my wording). Here's the code:
public void keyTyped(KeyEvent e) {
//keyTyped = Invoked when a key is typed. Uses KeyChar, char output
switch(e.getKeyChar()) {
case 'a': label.setLocation(label.getX()-10, label.getY());
for(int coins=0; coins<1;coins++) {
coins = coins + 1;
}
break;
case 'w': label.setLocation(label.getX(), label.getY()-10);
for(int coins=0; coins<1;coins++) {
coins = coins + 1;
}
break;
case 's': label.setLocation(label.getX(), label.getY()+10);
for(int coins=0; coins<1;coins++) {
coins = coins + 1;
}
break;
case 'd': label.setLocation(label.getX()+10, label.getY());
for(int coins=0; coins<1;coins++) {
coins = coins + 1;
}
break;
}
}
Maybe it is what I wrote in the code and it won't change? I didn't see similar questions like mine.
See How to Use Key Bindings for example
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.border.EmptyBorder;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel coinLabel;
private int coins;
public TestPane() {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(32, 32, 32, 32));
coinLabel = new JLabel("0");
add(coinLabel);
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
CoinAction.Observer observer = new CoinAction.Observer() {
#Override
public void coinDidChange(CoinAction action, int delta) {
coins += delta;
coinLabel.setText(Integer.toString(coins));
}
};
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), "Pressed.Up");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), "Pressed.Down");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), "Pressed.Left");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), "Pressed.Right");
am.put("Pressed.Up", new CoinAction(1, observer));
am.put("Pressed.Down", new CoinAction(-1, observer));
am.put("Pressed.Left", new CoinAction(10, observer));
am.put("Pressed.Right", new CoinAction(-10, observer));
}
}
public class CoinAction extends AbstractAction {
public interface Observer {
public void coinDidChange(CoinAction action, int delta);
}
private int delta;
private Observer observer;
public CoinAction(int delta, Observer observer) {
this.delta = delta;
this.observer = observer;
}
public int getDelta() {
return delta;
}
#Override
public void actionPerformed(ActionEvent e) {
observer.coinDidChange(this, getDelta());
}
}
}

Java KeyBindings stop working after a few seconds

I'm trying to move from Java KeyListeners to KeyBindings for animating but they work for a few seconds and then completely stop. I'm printing messages to the console when the actions fire, and those messages stop, so it's not just the painting that isn't working, it's the firing of the actions when the keys are pressed.
My class extends JFrame, and I just add a JPanel to it, and add a JLabel to the JPanel. I use flags toggled by the Actions to indicate how the JLabel should move, and I use the JFrame's actionPerformed method to check the state of the flags and adjust the JLabel's location.
I've tried adding JComponent.WHEN_IN_FOCUSED_WINDOW inside the getInputMap method, but it made no difference.
Here's the code:
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
public class KeyBindingTest extends JFrame implements ActionListener {
long counter = 0;
int speed = 5;
boolean isUp = false, isDown = false, isLeft = false, isRight = false;
JLabel j = new JLabel();
JPanel p = new JPanel();
Timer t;
Action upPressed = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("up on");
isUp = true;
}
};
Action upReleased = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
isUp = false;
System.out.println("up off");
}
};
Action downPressed = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("down on");
isDown = true;
}
};
Action downReleased = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("down off");
isDown = false;
}
};
Action leftPressed = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("left on");
isLeft = true;
}
};
Action leftReleased = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("left off");
isLeft = false;
}
};
Action rightPressed = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("right on");
isRight = true;
}
};
Action rightReleased = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("right off");
isRight = false;
}
};
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
// The try/catch block prevents errors from crashing the program
try {
KeyBindingTest window = new KeyBindingTest(); // Create and setup the main game window
window.run(); // show the new window
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private void run() {
this.setBounds(640, 400, 640, 400);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
p.setBounds(0, 0, this.getWidth(), this.getHeight());
p.setVisible(true);
p.setBackground(Color.blue);
p.setOpaque(true);
p.setFocusable(true);
p.setLayout(null);
j.setBounds(320, 200, 10, 10);
j.setVisible(true);
j.setBackground(Color.red);
j.setOpaque(true);
this.add(p);
p.add(j);
p.requestFocusInWindow();
setupKeyBindings();
t = new Timer(1000 / 40, this);
t.start();
}
private void setupKeyBindings() {
p.getInputMap().put(KeyStroke.getKeyStroke("W"), "moveUp");
p.getActionMap().put("moveUp", upPressed);
p.getInputMap().put(KeyStroke.getKeyStroke("released W"), "stopUp");
p.getActionMap().put("stopUp", upReleased);
p.getInputMap().put(KeyStroke.getKeyStroke("S"), "moveDown");
p.getActionMap().put("moveDown", downPressed);
p.getInputMap().put(KeyStroke.getKeyStroke("released S"), "stopDown");
p.getActionMap().put("stopDown", downReleased);
p.getInputMap().put(KeyStroke.getKeyStroke("A"), "moveLeft");
p.getActionMap().put("moveLeft", leftPressed);
p.getInputMap().put(KeyStroke.getKeyStroke("released A"), "stopLeft");
p.getActionMap().put("stopLeft", leftReleased);
p.getInputMap().put(KeyStroke.getKeyStroke("D"), "moveRight");
p.getActionMap().put("moveRight", rightPressed);
p.getInputMap().put(KeyStroke.getKeyStroke("released D"), "stopRight");
p.getActionMap().put("stopRight", rightReleased);
}
#Override
public void actionPerformed(ActionEvent e) {
counter++;
System.out.println(counter);
if (isUp) {
j.setLocation(j.getX(), j.getY() - speed);
}
if (isDown) {
j.setLocation(j.getX(), j.getY() + speed);
}
if (isLeft) {
j.setLocation(j.getX() - speed, j.getY());
}
if (isRight) {
j.setLocation(j.getX() + speed, j.getY());
}
repaint();
}
}
This is a personal thing, but, you will generally have less issues if you use something like KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false) over
KeyStroke.getKeyStroke("W")
The equivalent for KeyStroke.getKeyStroke("released W") would be KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true)
I have been back and forward through your example, I've tried replacing KeyStroke.getKeyStroke("W") with KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false) and it's equivalents; I've tried replacing the boolean flags with a Set and I still have the same issue
I then went and threw away your code and started with a new project. First I tried a custom painting route, and that worked fine. I then tried a component based route and that worked ... đŸ˜“
So, while I still don't have an "answer" for why it's not working, I do have an example which does...
Example
And because I actually had test my suggestions...
import com.sun.glass.events.KeyEvent;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashSet;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public enum Input {
UP, DOWN, LEFT, RIGHT
}
public class TestPane extends JPanel {
private Set<Input> inputs = new HashSet<>();
private int delta = 4;
private JLabel label;
public TestPane() {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "Up.pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "Up.relesed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "Down.pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "Down.relesed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "Left.pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "Left.relesed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "Right.pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "Right.relesed");
actionMap.put("Up.pressed", new InputAction(Input.UP, true));
actionMap.put("Up.relesed", new InputAction(Input.UP, false));
actionMap.put("Down.pressed", new InputAction(Input.DOWN, true));
actionMap.put("Down.relesed", new InputAction(Input.DOWN, false));
actionMap.put("Left.pressed", new InputAction(Input.LEFT, true));
actionMap.put("Left.relesed", new InputAction(Input.LEFT, false));
actionMap.put("Right.pressed", new InputAction(Input.RIGHT, true));
actionMap.put("Right.relesed", new InputAction(Input.RIGHT, false));
setLayout(null);
label = new JLabel();
label.setBackground(Color.RED);
label.setOpaque(true);
label.setBounds(0, 0, 10, 10);
add(label);
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int xPos = label.getX();
int yPos = label.getY();
if (inputs.contains(Input.UP)) {
yPos -= delta;
}
if (inputs.contains(Input.DOWN)) {
yPos += delta;
}
if (inputs.contains(Input.LEFT)) {
xPos -= delta;
}
if (inputs.contains(Input.RIGHT)) {
xPos += delta;
}
if (xPos < 0) {
xPos = 0;
} else if (xPos + 10 > getWidth()) {
xPos = getWidth() - 10;
}
if (yPos < 0) {
yPos = 0;
} else if (yPos + 10 > getHeight()) {
yPos = getHeight() - 10;
}
label.setLocation(xPos, yPos);
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
// protected void paintComponent(Graphics g) {
// super.paintComponent(g);
// Graphics2D g2d = (Graphics2D) g.create();
// g2d.setColor(Color.RED);
// g2d.drawRect(xPos, yPos, 10, 10);
// g2d.dispose();
// }
public class InputAction extends AbstractAction {
private Input input;
private boolean pressed;
public InputAction(Input input, boolean pressed) {
this.input = input;
this.pressed = pressed;
}
#Override
public void actionPerformed(ActionEvent e) {
if (pressed) {
inputs.add(input);
} else {
inputs.remove(input);
}
}
}
}
}
A side note...
This "type" of question is getting asked a lot lately, I assume it's some kind of class assignment, as we've seen a number of variants of this style of code. As we've repeatedly advised, using components in this way is ill-advised, they aren't really designed for this kind of thing. You will get better (and easier) results using a custom painting route, as demonstrated in Make an JLabel move with Key Bidings

JButton doesn't show up in JFrame

I'm currently working on a schoolproject, where I have to code the game snake. Now I`m finished with the biggest part and tryed to make the game menue. I tryed to place a JButton for starting the game (startPlay). However, the button won't show up and I can't figure out why. Can someone help? Thanks in advance!!
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.*;
public class Main extends JPanel implements ActionListener, KeyListener{
public static int field[][];
public static GenerateField genField;
public static Snake snake;
public static GenerateFood food;
public static GenerateBarrier barrier;
public int difficultness;
public static int widthField;
public static int heightField;
public static TimerTask move, genBarriers;
public static Timer snakeTimer, barrierTimer;
public JButton startPlay;
public static boolean gameStarted;
public Main ()
{
startPlay = new JButton("Starte Spiel");
startPlay.setBounds(0,0,300,200);
startPlay.addActionListener(this);
add(startPlay);
difficultness = 15;
gameStarted = false;
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
widthField = 150;
heightField = 95;
genField = new GenerateField();
snake = new Snake();
food = new GenerateFood();
barrier = new GenerateBarrier();
barrierTimer = new Timer("Timer");
snakeTimer = new Timer("Timer");
genBarriers = new TimerTask() {
#Override
public void run() {
barrier.clearBarrier();
barrier.multiSpawnBarrier(difficultness);
}
};
move = new TimerTask()
{
public void run()
{
if(GenerateField.inGame)
{
snake.moveSnake();
repaint();
}
}
};
}
private static void startGame()
{
genField.generateField();
field = genField.getField();
snake.generateSnake(40, 75);
food.spawnFood();
snakeTimer.schedule(move,0,50);
barrierTimer.schedule(genBarriers, 0, 25000);
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(1520,1000);
frame.getContentPane().add(new Main());
frame.setLocationRelativeTo(null);
frame.setBackground(Color.LIGHT_GRAY);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
#Override
public void paint(Graphics g)
{
if(gameStarted) {
for (int iy = 0; iy < heightField; iy++) {
for (int ix = 0; ix < widthField; ix++) {
//Zeichnet schwarzen Hintergrund
if (genField.field[iy][ix] == 0) {
g.setColor(Color.BLACK);
g.fillRect(ix * 10, iy * 10, 10, 10);
}
//Zeichnet die Grenze an den Rändern
if (genField.field[iy][ix] == 1) {
g.setColor(Color.red);
g.fillRect(ix * 10, iy * 10, 10, 10);
}
//Zeichnet die Schlange
if (genField.field[iy][ix] == 2) {
g.setColor(Color.green);
g.fillRect(ix * 10, iy * 10, 10, 10);
}
//Zeichnet das "Futter"
if (genField.field[iy][ix] == 3) {
g.setColor(Color.orange);
g.fillRect(ix * 10, iy * 10, 10, 10);
}
//Zeichte die Hindernisse
if (genField.field[iy][ix] == 4) {
g.setColor(Color.blue);
g.fillRect(ix * 10, iy * 10, 10, 10);
}
}
}
}
}
#Override
public void actionPerformed(ActionEvent e)
{
startPlay.setVisible(false);
startGame();
gameStarted = true;
}
#Override
public void keyPressed (KeyEvent e)
{
int code = e.getKeyCode();
if ( code == KeyEvent.VK_LEFT)
{
if (snake.mRight == false)
{
snake.mLeft = true;
snake.mRight = false;
snake.mUp = false;
snake.mDown = false;
}
}
if ( code == KeyEvent.VK_RIGHT)
{
if (snake.mLeft == false)
{
snake.mLeft = false;
snake.mRight = true;
snake.mUp = false;
snake.mDown = false;
}
}
if ( code == KeyEvent.VK_UP)
{
if (snake.mDown == false)
{
snake.mLeft = false;
snake.mRight = false;
snake.mUp = true;
snake.mDown = false;
}
}
if ( code == KeyEvent.VK_DOWN)
{
if (snake.mUp == false)
{
snake.mLeft = false;
snake.mRight = false;
snake.mUp = false;
snake.mDown = true;
}
}
}
#Override
public void keyReleased(KeyEvent e)
{
}
#Override
public void keyTyped(KeyEvent e)
{
}
}
Immediate Problem
The over use of static highlights issues with your design. static is not your friend, you should use it sparingly and wisely.
You're trying to put all your eggs in single basket. This is just going to make the state management harder to handle.
Instead, start by separating your menu and game into separate classes and managing them independently of each other.
This then allows you to use a CardLayout to manage the navigation between the different views.
The following is simple example to demonstrate how you might use CardLayout to perform decoupled navigation
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
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.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
CardLayout cardLayout = new CardLayout();
JPanel base = new JPanel(cardLayout);
NavigationController controller = new NavigationController() {
#Override
public void show(Screen screen) {
cardLayout.show(base, screen.name());
}
};
base.add(new MainMenuPane(controller), Screen.MENU.name());
base.add(new GamePane(controller), Screen.GAME.name());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(base);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public enum Screen {
MENU, GAME;
}
public interface NavigationController {
public void show(Screen scren);
}
public class MainMenuPane extends JPanel {
public MainMenuPane(NavigationController controller) {
setLayout(new GridBagLayout());
JButton start = new JButton("Start");
add(start);
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
controller.show(Screen.GAME);
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class GamePane extends JPanel {
private NavigationController controller;
public GamePane(NavigationController controller) {
this.controller = controller;
setLayout(new GridBagLayout());
add(new JLabel("Ready Player One"));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
Every thing else you'll need to fix
Don't use KeyListener, use Key bindings instead, they will fix the focus related issues in a more reliable way
You're violating the requirements of the paint chain, which is part of your immediate problem - See Performing Custom Painting and Painting in Swing for more details about how painting works and how you should work with it
Swing is single thread AND not thread safe - see Concurrency in Swing for more details. Essentially the use of java.util.Timer is running the risk of dirty read/writes which could lead to any number of weird and near impossible to diagnose issues. Instead, you should be using Swing Timer instead, which will ensure that any updates you make are made within the context of the Event Dispatching Thread. You should also be using a single Timer and scheduling everything in a simple update pass - this will generally improve performance

Can't repaint my JFrame/JPanel

I have created a program that just moves a ball across a screen. I used to have it all in one class, but decided that it looked too messy so I split it up into three different classes: Main... initializes everything, Game... which paints everything and is a JPanel, and AL which is a KeyListener (which is also where the problem is). The problem is that I can't get the program to repaint from my AL class no matter what I try to pass into it. Can anyone help with this? Here are my three classes:
import java.awt.Color;
import javax.swing.JFrame;
public class Main {
static Game game;
static JFrame frame;
public static void main(String[] args) {
game = new Game();
frame = new JFrame();
frame.getContentPane().add(game);
frame.addKeyListener(new AL(game, frame));
frame.setTitle("Game");
frame.setSize(500, 500);
frame.setResizable(true);
frame.setVisible(true);
frame.setBackground(Color.BLACK);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
-
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JPanel implements Runnable {
int x, y, xCoord, yCoord;
private Image dbImage;
private Graphics dbg;
JFrame frame;
public void changeCoord() {
x += xCoord;
y += yCoord;
if (x <= 20) {
x = 20;
}
if (x >= 480) {
x = 480;
}
if (y <= 40) {
y = 40;
}
if (y >= 480) {
y = 480;
}
}
public void setXCoord(int xcoord) {
xCoord = xcoord;
}
public void setYCoord(int ycoord) {
yCoord = ycoord;
}
public static void main(String[] args) {
Game game = new Game();
Thread t = new Thread(game);
t.start();
}
public Game() {
x = 250;
y = 250;
}
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
#Override
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
#Override
public void run() {
try {
while (true) {
changeCoord();
Thread.sleep(30);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
-
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class AL extends KeyAdapter {
Game game;
JFrame frame;
public AL(Game game, JFrame frame) {
this.game = game;
this.frame = frame;
}
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
game.setXCoord(-1);
}
if (keyCode == e.VK_RIGHT) {
game.setXCoord(+1);
}
if (keyCode == e.VK_UP) {
game.setYCoord(-1);
}
if (keyCode == e.VK_DOWN) {
game.setYCoord(+1);
}
game.repaint();
}
#Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
game.setXCoord(0);
}
if (keyCode == e.VK_RIGHT) {
game.setXCoord(0);
}
if (keyCode == e.VK_UP) {
game.setYCoord(0);
}
if (keyCode == e.VK_DOWN) {
game.setYCoord(0);
}
game.repaint();
}
}
Let's start with the obvious....
This is problematic...
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
#Override
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
There's no need to implement double buffering in Swing components, they already are. Also, you're breaking the painting contract, by not call the paint methods super methods
The whole thing should be...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
See Painting in AWT and Swing and Performing Custom Painting for more details
KeyListener is well known for been problematic. It will only raise key events if the component it registered to is focuable AND has keyboard focus. A JPanel by default, is not focusable. Before you run of and try and make it focusable (and get bitterly disappointed), you should be using the Key Bindings API instead, which was designed to over come the limitations of KeyListener
As a basic example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Game());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Game extends JPanel {
int x, y, xCoord, yCoord;
public Game() {
x = 250;
y = 250;
addKeyBinding(KeyEvent.VK_LEFT, "move.left", new MoveAction(this, -1, 0));
addKeyBinding(KeyEvent.VK_RIGHT, "move.right", new MoveAction(this, 1, 0));
addKeyBinding(KeyEvent.VK_UP, "move.up", new MoveAction(this, 0, -1));
addKeyBinding(KeyEvent.VK_DOWN, "move.down", new MoveAction(this, 0, 1));
}
protected void addKeyBinding(int keyCode, String name, Action action) {
addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0), name, action);
}
protected void addKeyBinding(KeyStroke keyStroke, String name, Action action) {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(keyStroke, name);
actionMap.put(name, action);
}
public void changeCoord() {
x += xCoord;
y += yCoord;
if (x <= 20) {
x = 20;
}
if (x >= 480) {
x = 480;
}
if (y <= 40) {
y = 40;
}
if (y >= 480) {
y = 480;
}
repaint();
}
public void setXCoord(int xcoord) {
xCoord = xcoord;
changeCoord();
}
public void setYCoord(int ycoord) {
yCoord = ycoord;
changeCoord();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(480, 480);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
}
public class MoveAction extends AbstractAction {
private int xDelta;
private int yDelta;
// I'd prefer an interface with just the "move" methods, but
// that's more time I don't have
private Game game;
public MoveAction(Game game, int xDelta, int yDelta) {
this.xDelta = xDelta;
this.yDelta = yDelta;
this.game = game;
}
#Override
public void actionPerformed(ActionEvent e) {
game.setXCoord(xDelta);
game.setYCoord(yDelta);
}
}
}
But, wait, that isn't exactly what you want (trust me, I'm an anoymouse person on the Internet ;)), a better example might be...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Game());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public enum Direction {
UP,
LEFT,
DOWN,
RIGHT;
}
public class Game extends JPanel {
int x, y, xCoord, yCoord;
private Set<Direction> movement;
public Game() {
x = 250;
y = 250;
movement = new HashSet<>(4);
addKeyPressedBinding(KeyEvent.VK_LEFT, "left.pressed", new MoveAction(movement, Direction.LEFT, true));
addKeyReleasedBinding(KeyEvent.VK_LEFT, "left.released", new MoveAction(movement, Direction.LEFT, false));
addKeyPressedBinding(KeyEvent.VK_RIGHT, "right.pressed", new MoveAction(movement, Direction.RIGHT, true));
addKeyReleasedBinding(KeyEvent.VK_RIGHT, "right.released", new MoveAction(movement, Direction.RIGHT, false));
addKeyPressedBinding(KeyEvent.VK_UP, "up.pressed", new MoveAction(movement, Direction.UP, true));
addKeyReleasedBinding(KeyEvent.VK_UP, "up.released", new MoveAction(movement, Direction.UP, false));
addKeyPressedBinding(KeyEvent.VK_DOWN, "down.pressed", new MoveAction(movement, Direction.DOWN, true));
addKeyReleasedBinding(KeyEvent.VK_DOWN, "down.released", new MoveAction(movement, Direction.DOWN, false));
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
changeCoord();
}
});
timer.start();
}
protected void addKeyBinding(int keyCode, String name, Action action) {
addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0), name, action);
}
protected void addKeyPressedBinding(int keyCode, String name, Action action) {
addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0, false), name, action);
}
protected void addKeyReleasedBinding(int keyCode, String name, Action action) {
addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0, true), name, action);
}
protected void addKeyBinding(KeyStroke keyStroke, String name, Action action) {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(keyStroke, name);
actionMap.put(name, action);
}
public void changeCoord() {
if (movement.contains(Direction.UP)) {
y--;
} else if (movement.contains(Direction.DOWN)) {
y++;
}
if (movement.contains(Direction.LEFT)) {
x--;
} else if (movement.contains(Direction.RIGHT)) {
x++;
}
x += xCoord;
y += yCoord;
if (x <= 20) {
x = 20;
}
if (x >= 480) {
x = 480;
}
if (y <= 40) {
y = 40;
}
if (y >= 480) {
y = 480;
}
repaint();
}
public void setXCoord(int xcoord) {
xCoord = xcoord;
changeCoord();
}
public void setYCoord(int ycoord) {
yCoord = ycoord;
changeCoord();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(480, 480);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
}
public class MoveAction extends AbstractAction {
private Set<Direction> movement;
private Direction direction;
private boolean pressed;
public MoveAction(Set<Direction> movement, Direction direction, boolean pressed) {
this.movement = movement;
this.direction = direction;
this.pressed = pressed;
}
#Override
public void actionPerformed(ActionEvent e) {
if (pressed) {
movement.add(direction);
} else {
movement.remove(direction);
}
}
}
}
What this does is simply activates a flag when a key is pressed (and deactivates it when it's released), then in a Swing Timer, we check which keys are "active" and update the location of the ball.
What this does is, eliminates the key "stutter" which is caused by the OS when a key is first pressed and held. A delay is inserted between the first key and the repeated key events. Instead, we just turn the flag on and off as we like.
It also allows you to move in two directions at the same time (horizontally and vertically)
Have a look at Concurrency in Swing and How to use Swing Timers for more details

Java - (NetBeans) Make a JTextArea Slide Out From Behind JFrame

I have just written a program in Netbeans that moves/copies/deletes files, and I wanted to give it a "diagnostic mode" where information about selected files, folders, variables, etc is displayed in a Text Area. Now, I could set this to only be visible when the "diagnostic mode" toggle is selected, but I think it would look awesome if the text area started behind the program, and "slid" out from behind the JFrame when the button is toggled. Is there any way to do this?
Thanks!
-Sean
Here is some starter code for you. This will right-slide a panel
of just about any content type. Tweak as necessary. Add error
checking and exception handling.
Tester:
static public void main(final String[] args) throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
final JPanel slider = new JPanel();
slider.setLayout(new FlowLayout());
slider.setBackground(Color.RED);
slider.add(new JButton("test"));
slider.add(new JButton("test"));
slider.add(new JTree());
slider.add(new JButton("test"));
slider.add(new JButton("test"));
final CpfJFrame42 cpfJFrame42 = new CpfJFrame42(slider, 250, 250);
cpfJFrame42.slide(CpfJFrame42.CLOSE);
cpfJFrame42.setSize(300, 300);
cpfJFrame42.setLocationRelativeTo(null);
cpfJFrame42.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
cpfJFrame42.setVisible(true);
}
});
}
Use GAP & MIN to adjust spacing from JFrame and closed size.
The impl is a little long...it uses a fixed slide speed but
that's one the tweaks you can make ( fixed FPS is better ).
package com.java42.example.code;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyAdapter;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionAdapter;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
public class CpfJFrame42 extends JFrame {
public static int GAP = 5;
public static int MIN = 1;
public static final int OPEN = 0x01;
public static final int CLOSE = 0x02;
private JDialog jWindow = null;
private final JPanel basePanel;
private final int w;
private final int h;
private final Object lock = new Object();
private final boolean useSlideButton = true;
private boolean isSlideInProgress = false;
private final JPanel glassPane;
{
glassPane = new JPanel();
glassPane.setOpaque(false);
glassPane.addMouseListener(new MouseAdapter() {
});
glassPane.addMouseMotionListener(new MouseMotionAdapter() {
});
glassPane.addKeyListener(new KeyAdapter() {
});
}
public CpfJFrame42(final Component component, final int w, final int h) {
this.w = w;
this.h = h;
component.setSize(w, h);
addComponentListener(new ComponentListener() {
#Override
public void componentShown(final ComponentEvent e) {
}
#Override
public void componentResized(final ComponentEvent e) {
locateSlider(jWindow);
}
#Override
public void componentMoved(final ComponentEvent e) {
locateSlider(jWindow);
}
#Override
public void componentHidden(final ComponentEvent e) {
}
});
jWindow = new JDialog(this) {
#Override
public void doLayout() {
if (isSlideInProgress) {
}
else {
super.doLayout();
}
}
};
jWindow.setModal(false);
jWindow.setUndecorated(true);
jWindow.setSize(component.getWidth(), component.getHeight());
jWindow.getContentPane().add(component);
locateSlider(jWindow);
jWindow.setVisible(true);
if (useSlideButton) {
basePanel = new JPanel();
basePanel.setLayout(new BorderLayout());
final JPanel statusPanel = new JPanel();
basePanel.add(statusPanel, BorderLayout.SOUTH);
statusPanel.add(new JButton("Open") {
private static final long serialVersionUID = 9204819004142223529L;
{
setMargin(new Insets(0, 0, 0, 0));
}
{
addActionListener(new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
slide(OPEN);
}
});
}
});
statusPanel.add(new JButton("Close") {
{
setMargin(new Insets(0, 0, 0, 0));
}
private static final long serialVersionUID = 9204819004142223529L;
{
addActionListener(new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
slide(CLOSE);
}
});
}
});
{
//final BufferedImage bufferedImage = ImageFactory.getInstance().createTestImage(200, false);
//final ImageIcon icon = new ImageIcon(bufferedImage);
//basePanel.add(new JButton(icon), BorderLayout.CENTER);
}
getContentPane().add(basePanel);
}
}
private void locateSlider(final JDialog jWindow) {
if (jWindow != null) {
final int x = getLocation().x + getWidth() + GAP;
final int y = getLocation().y + 10;
jWindow.setLocation(x, y);
}
}
private void enableUserInput() {
getGlassPane().setVisible(false);
}
private void disableUserInput() {
setGlassPane(glassPane);
}
public void slide(final int slideType) {
if (!isSlideInProgress) {
isSlideInProgress = true;
final Thread t0 = new Thread(new Runnable() {
#Override
public void run() {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
disableUserInput();
slide(true, slideType);
enableUserInput();
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
isSlideInProgress = false;
}
});
t0.setDaemon(true);
t0.start();
}
else {
Toolkit.getDefaultToolkit().beep();
}
}
private void slide(final boolean useLoop, final int slideType) {
synchronized (lock) {
for (int x = 0; x < w; x += 25) {
if (slideType == OPEN) {
jWindow.setSize(x, h);
}
else {
jWindow.setSize(getWidth() - x, h);
}
jWindow.repaint();
try {
Thread.sleep(42);
} catch (final Exception e) {
e.printStackTrace();
}
}
if (slideType == OPEN) {
jWindow.setSize(w, h);
}
else {
jWindow.setSize(MIN, h);
}
jWindow.repaint();
}
}
}

Categories

Resources