I am trying to make a simple animation using ActionListener and KeyListener
that will take in keyboard inputs, namely the arrow keys.
The problem is the program is not compiling with KeyListener. Can someone please
shed some light on why and possibly provide help with a solution.
Thanks!
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Planegame extends JPanel implements ActionListener, KeyListener
{ //^^ this line is giving me trouble^^^^
Timer tim = new Timer(20, this);
int x = 0, y = 0, velX = 0, velY = 0;
public Planegame()
{
tim.start(); //this will start my animation
addKeyListener(this); // will activate the keylistner to watch key press
setFocusable(true);
setFocusTraversalKeysEnabled(false); //disables shift and tab key
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillOval(x, y, 10, 10);
}
public void actionPerformed(ActionEvent e)
{
x = x + 10; //velX
y = y + velY;
repaint();
}
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode()==38)
{
velY = 1;
}
if(e.getKeyCode()==40)
{
velY = -1;
}
if (e.getKeyCode()==32) //booster power
{
velX = 3;
}
}
public void keyTyped(KeyEvent e) {}
public void keyReleased(keyEvent e) {}
public static void main(String[] args)
{
PlaneGame zed = new PlaneGame();
JFrame k = new JFrame();
k.setTitle("game");
k.setSize(600,400);
k.setVisible(true);
k.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
k.add(zed);
}
}
You have a typo in the declaration of keyReleased method...
public void keyReleased(keyEvent e) {
Remember, Java is case sensitive, it should be...
public void keyReleased(KeyEvent e) {
Note the uppercase K in KeyEvent
You wish to consider using the #Override annotation on methods that you think you are overriding, this will alert you when you've made a mistake of some kind, for example
#Override
public void keyReleased(KeyEvent e) {
There's also no reason why paintComponent should be public, you never want some one outside of your component to call it
As always, I'd advise using key bindings over KeyListener as they provide better control over the level of focus your component needs to be able to trigger a key event
Related
Hi this may be very stupid, but please try to understand that I am an absolute beginner at Java programming. I have been trying to learn it on my own from the internet.
I was trying to make a simple Applet that can move a square using the KeyListener.
First I made a version that can only detect one key at a time. So I looked up a tutorial on YouTube (https://www.youtube.com/watch?v=5UaEUrbpDPE). It used an ArrayList to somehow handle the issue with what was referred to as "Ghosting". The tutorial showed flawless results but my code resulted in some weird problems:
Initially the square moved smoothly in any direction for some time. The square could mainly be moved down and right. After pressing up or left a few times the square could no longer be moved!!!
Can anyone please guide me on what I am doing wrong or how else this could have been accomplished?
Here is the code that I began with (One button at a time detection):
package boxHero;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class boxGame extends Applet implements KeyListener {
private Rectangle rect;
private int x = 20, y = 20, recW = 50, recH = 50;
public void init() {
setSize(600, 500);
setBackground(Color.BLACK);
this.addKeyListener(this);
rect = new Rectangle (x, y, recW, recH);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setColor(Color.YELLOW);
g2.fill(rect);
}
#Override
public void keyPressed(KeyEvent e) {
// Can't detect more than one key at a time
if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
rect.setLocation(rect.x + 10, rect.y);
}
else if(e.getKeyCode() == KeyEvent.VK_LEFT) {
rect.setLocation(rect.x - 10, rect.y);
}
else if(e.getKeyCode() == KeyEvent.VK_UP) {
rect.setLocation(rect.x, rect.y - 10);
}
else if(e.getKeyCode() == KeyEvent.VK_DOWN) {
rect.setLocation(rect.x, rect.y + 10);
}
repaint();
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
}
Here's the code from the YouTube tutorial:
package boxHero;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
public class boxGame2 extends Applet implements KeyListener {
private Rectangle rect; // Declaring a rectangle object
private ArrayList<Integer> keysDown;
private int x = 20, y = 20, recW = 50, recH = 50;
public void init() {
setSize(600, 500); // Initial screen size
setBackground(Color.BLACK); // Setting background
this.addKeyListener(this); // Adding KeyListener
keysDown = new ArrayList<Integer>();
rect = new Rectangle (x, y, recW, recH); // Instantiating and Initializing(setting values) for our Rectangle
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
// Graphics for our rectangle
g2.setColor(Color.YELLOW);
g2.fill(rect);
}
public void moveRect() {
int x = rect.x;
int y = rect.y;
if(keysDown.contains(KeyEvent.VK_UP)) {
y -= 10;
}
if(keysDown.contains(KeyEvent.VK_DOWN)) {
y += 10;
}
if(keysDown.contains(KeyEvent.VK_LEFT)) {
x -= 10;
}
if(keysDown.contains(KeyEvent.VK_RIGHT)) {
x += 10;
}
rect.setLocation(x, y);
repaint();
}
#Override
public void keyPressed(KeyEvent e) {
if(!keysDown.contains(e.getKeyCode()) && e.getKeyCode() != 86) {
keysDown.add(new Integer(e.getKeyCode()));
}
moveRect();
}
#Override
public void keyReleased(KeyEvent e) {
keysDown.remove(e);
}
#Override
public void keyTyped(KeyEvent e) {
}
}
The diference between your code and the tutorial is, that the tutorial uses a list of keys that are currently pressed, while you only check out, WHICH key is pressed.
Make an ArrayList of keys like in the tutorial, then add each key when pressed and remove it, as soon as released. Then you can move your rectangle for every key that is contained in your list.
In your keyPressed() event, you add e.getKeyCode() to your keysDown list. But in your keyReleased() event, you only try to remove e from your list, which should result in nothing ever getting removed and all keys remaining pressed. So the reason it doesn't move after a few presses is that you are basically pressing left, right, up and down at once, causing them to cancel each other out.
Also, you should make a habit of using Integer.valueOf(x) instead of new Integer(x), since it caches values in the range of [-128;127], resulting in much better performance when creating wrappers for values in that range.
I'm trying to create a program where an object continuously falls down the screen unless the user is pressing space, in which case it moves up. So far I've gotten the object to initially start falling when I run the program. When I press space, the object moves up (like it should). But, when I release the key the object stops moving. I want it to continue falling when the key is released.
I've looked at using key bindings instead of keylistener but I've had trouble with it. I am a highschool student looking to major in computer science so forgive me for not knowing too many advanced coding terms/methods. However, I am one of the best in my class and a fast learner who is eager to solve this problem. Here is my code:
public class htestnew extends JPanel implements ActionListener, KeyListener {
Timer t = new Timer(5, this);
int x = 20, y = 20, vely = 1;
public htestnew() {
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
ImageIcon background = new ImageIcon("OBJECTEXAMPLE1");
background.paintIcon(this, g, 0, 0);
}
public void actionPerformed(ActionEvent e) {
if (y < 0)
{
vely = 0;
y = 0;
}
if (y > 305) //-70
{
vely = 0;
y = 305;
}
y += vely;
repaint();
}
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if (code == KeyEvent.VK_SPACE) {
vely = -1;
}
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
vely = 0;
//I have tried setting this value to 1 but it does not work
}
If it is easier to use keybindings a personal and easy to follow example would be amazing. Thank you.
So, changing...
public void keyReleased(KeyEvent e) {
vely=0;
//I have tried setting this value to 1 but it does not work
}
to...
public void keyReleased(KeyEvent e) {
vely=1;
//I have tried setting this value to 1 but it does not work
}
Basically made it work (after I added in something that actually paints)
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.awt.event.KeyListener;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test extends JPanel implements ActionListener, KeyListener {
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.add(new Test());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
Timer t = new Timer(5, this);
int x = 20, y = 20, vely = 1;
public Test() {
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(x, y, 5, 5);
}
public void actionPerformed(ActionEvent e) {
if (y < 0) {
vely = 0;
y = 0;
}
if (y > 305) //-70
{
vely = 0;
y = 305;
}
y += vely;
repaint();
}
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if (code == KeyEvent.VK_SPACE) {
vely = -1;
}
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
vely = 1;
//I have tried setting this value to 1 but it does not work
}
}
I would strongly recommend that you take a look at How to Use Key Bindings instead of using KeyListener, it will solve the core problem with KeyListener
Key Binding Example...
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.awt.event.KeyListener;
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 Test extends JPanel 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.add(new Test());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
Timer t = new Timer(5, this);
int x = 20, y = 20, vely = 1;
public Test() {
t.start();
addKeyBinding("space.pressed", KeyEvent.VK_SPACE, true, new ChangeYAction(-1));
addKeyBinding("space.released", KeyEvent.VK_SPACE, false, new ChangeYAction(1));
}
protected void addKeyBinding(String name, int virtualKey, boolean pressed, Action action) {
addKeyBinding(name, KeyStroke.getKeyStroke(virtualKey, 0, !pressed), action);
}
protected void addKeyBinding(String name, KeyStroke keyStroke, Action action) {
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(keyStroke, name);
am.put(name, action);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(x, y, 5, 5);
}
public void actionPerformed(ActionEvent e) {
if (y < 0) {
vely = 0;
y = 0;
}
if (y > 305) //-70
{
vely = 0;
y = 305;
}
y += vely;
repaint();
}
public class ChangeYAction extends AbstractAction {
private int changeYTo;
public ChangeYAction(int changeYTo) {
this.changeYTo = changeYTo;
}
#Override
public void actionPerformed(ActionEvent e) {
vely = changeYTo;
}
}
}
I can draw static things to the screen, but I want to make them move with user key input. I don't know what to do, I've been searching and searching and haven't come up with an answer yet. Please help!
package com.Game.game;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JFrame
{
final static int width = 500;
final static int height = 500;
public int x = 250;
public int y = 250;
public int changeX = 10;
public int changeY = 10;
public static void main(String[] args)
{
new Game();
}
public Game()
{
KeyListener listener = new KeyListening();
addKeyListener(listener);
setFocusable(true);
DrawingStuff drawingstuff = new DrawingStuff();
add(drawingstuff);
setSize(width, height);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public class DrawingStuff extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawString("Hey there!", 300, 300);
g.setColor(Color.RED);
g.fillRect(x, y, 50, 50);
}
}
public class KeyListening implements KeyListener
{
DrawingStuff drawingstuff = new DrawingStuff();
#Override
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_UP)
{
y = y + changeY;
System.out.println("Hey");
drawingstuff.repaint();
}
}
#Override
public void keyReleased(KeyEvent e)
{
}
#Override
public void keyTyped(KeyEvent e)
{
}
}
public void update()
{
}
}
EDIT: Fixed it. I took away the key listener stuff in the constructor method, added a command to focus on "drawingstuff" in the constructor method, and, most importantly, added this bit of code to the end of the constructor method:
while(true)
{
drawingstuff.repaint();
}
The problem is that your KeyListening object has a reference to a different DrawingStuff object than the one you added to your UI inside the Game constructor.
public class KeyListening implements KeyListener
{
DrawingStuff drawingstuff = new DrawingStuff();
...
You should pass a DrawingStuff reference to the KeyListening instance so that it can tell the right object to repaint itself.
Can someone please tell me how i can get the value of int x in my following code:
when i move the square left and right, the output from Test stays as 500.
I am wanting the Thread in Test to get the value of x every time it loops, but x is always 500 (from the thread) even though the value of x is changing in Drawing.java. please help me with this.
package block;
public class Test extends Drawing implements Runnable{
Thread collision = new Thread(this);
public Test() {
collision.start();
}
#Override
public void run() {
while(true) {
System.out.println(x);
}
}
}
package block;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Drawing extends Canvas implements Runnable, KeyListener{
Thread thread = new Thread(this);
int x = 500;
int y = 540;
public Drawing() {
setSize(1000, 800);
addKeyListener(this);
thread.start();
}
public void update(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, 1000, 700);
paint(g);
}
public void paint(Graphics g) {
g.setColor(Color.cyan);
g.fillRect(x, y, 50, 50);
}
#Override
public void run() {
while(true) {
repaint();
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Drawing.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
#Override
public void keyTyped(KeyEvent ke) {
}
#Override
public void keyPressed(KeyEvent ke) {
if(ke.getKeyCode() == KeyEvent.VK_LEFT) {
x -= 5;
}
if(ke.getKeyCode() == KeyEvent.VK_RIGHT) {
x += 5;
}
}
#Override
public void keyReleased(KeyEvent ke) {
}
}
EDIT: --------------------
package block;
import javax.swing.JFrame;
public class Block extends JFrame{
Test test = new Test();
public Block() {
setSize(1000, 700);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
add(new Drawing());
setVisible(true);
createBufferStrategy(2);
}
public static void main(String[] args) {
new Block();
}
}
You are creating multiple instances of x because you are not using inheritance correctly. When you call new Test(), that creates one instance of a Test (which contains a Drawing, which contains a member x), and then the call to new Drawing() creates another instance of a Drawing (which contains a different member x).
To fix this, you could pass a reference to a Drawing object to your Test constructor, and access the x member through the reference.
I have literally no idea why my program isn't recognizing keyboard input. I have places print statements throughout the program to determine the issue, and I have determined that the keyPressed method never activates. This is for a game which I am making for a class project, and yes I am a relatively beginner programmer. Thanks in advance! (Code below)
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.ImageIcon;
import javax.swing.JApplet;
public class Dodger extends JApplet implements Runnable, KeyListener {
Thread myThread;
public Image bg;
public Image pic;
public boolean loaded;
public int cx, cy, speed, x, y;
public void init(){
setSize(800,800);
loaded = false;
x = 2;
y = 400;
cx = 0;
cy = 0;
speed = 3;
myThread = new Thread(this);
myThread.start();
addKeyListener(this);
}
public void run(){
loadpic();
repaint();
while (myThread!=null)
{
try{
myThread.sleep(18);
}catch(InterruptedException e){
e.printStackTrace();
}
repaint();
}
}
public void upMotion(){
cy = cy + speed;
}
public void downMotion(){
cy = cy - speed;
}
public void leftMotion(){
cx = cx - speed;
}
public void rightMotion(){
cx = cx + speed;
}
#Override
public void keyPressed(KeyEvent k) {
if (k.getKeyCode() == KeyEvent.VK_LEFT) {
System.out.println("work");
leftMotion();
}
if (k.getKeyCode() == KeyEvent.VK_RIGHT) {
rightMotion();
}
if (k.getKeyCode() == KeyEvent.VK_UP) {
upMotion();
}
if (k.getKeyCode() == KeyEvent.VK_DOWN) {
downMotion();
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
public void loadpic(){
bg = new ImageIcon(getClass().getResource("back.png")).getImage();
pic = new ImageIcon(getClass().getResource("smile.png")).getImage();
loaded = true;
repaint();
}
public void paint(Graphics g){
g.drawImage(bg, 0, 0, this);
g.drawImage(pic, cx, cy, this);
}
}
The key events are detected just fine when the applet is focusable & has focus. Managing focus has always been a problem with applets. The problem is mostly that Sun never bothered to specify the focus behavior that should ideally apply to a mixed page of focusable HTML elements and applet(s).
As per Tom's advice, add to the end of init()
setFocusable(true);
To be safe, also override:
public void start() {
this.requestFocusInWindow();
}
As an aside, generally it is better to use key bindings in Swing. They also require the applet to have input focus.
First off, you probably want to separate out your class from your KeyListener, as it makes it a bit harder to read.
Next, you want to get rid of the bare Thread and wrap it in a timer.
import javax.swing.Timer;
class Dodger extends JApplet {
Timer imageUpdater; //replaces Thread
/*...*/
public void init() {
/*etc*/
loadpic();
int repaintInterval = 100;
imageUpdater = new Timer(repaintInterval,
new ActionListener() {
public void actionPerformed(ActionEvent e) {
repaint();
}
}
);
imageUpdater.start();
addKeyListener(new KeyHandler());
setFocusable(true);
}
/*...*/
private class KeyHandler extends KeyAdapter {
/* Note that with this implementation, you do not have to override
* unnecessary methods, as KeyAdapter is an abstract class that
* implements all of the methods of KeyListener.
*/
#Override
public void keyPressed(KeyEvent e) {
/*...*/
}
}
/*...*/
}
Most of this is just code cleanup - the actual problem may be fixed (according to Tom, see comments) with the setFocusable(true).
I'm not really sure if it applies to Applets, but your Thread may not allowing the event dispatching to occur.
Try to run your "work" with SwingWorker.