I want to write a program in which the user can move the ball on the window by pressing WASD keys. Yet, when the user presses the keys, nothing happens. Bellow are the codes of my program, can anyone tell me what's wrong or how can I improve my program? ( The ball can move if I delete the KeyListener and put super.x ++; in ball.move() )
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.swing.JApplet;
import javax.swing.JComponent;
import java.awt.geom.*;
public class MoveBall extends JApplet
{
public final int Width = 567;
public final int Height = 567;
public static PaintSurface canvas;
public void init()
{
canvas = new PaintSurface();
this.setSize(Width, Height);
this.add(canvas, BorderLayout.CENTER);
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(3);
executor.scheduleAtFixedRate(new Action(), 0L, 10L, TimeUnit.MILLISECONDS);
}
}
class Action implements Runnable
{
public void run()
{
MoveBall.canvas.repaint();
}
}
class PaintSurface extends JComponent
{
Ball ball = new Ball(20);
public PaintSurface()
{
addKeyListener(new Listener());
}
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
ball.move();
g2.setColor(Color.GREEN);
g2.fill(ball);
g2.setColor(Color.BLACK);
g2.drawString("W,A,S,D or arrow keys to move", 7, 17);
}
}
class Ball extends Ellipse2D.Float
{
public int xspeed, yspeed;
public Ball(int d)
{
super(370,370, d,d);
}
public void move()
{
if(super.x >567)
super.x -=567;
if(super.x <0)
super.x +=567;
if(super.y >567)
super.y -=567;
if(super.y < 0)
super.y +=567;
super.x += xspeed ;
super.y += yspeed ;
}
}
class Listener implements KeyListener
{
public void keyPressed(KeyEvent ev)
{
if(ev.getKeyCode() == KeyEvent.VK_W)
{
MoveBall.canvas.ball.xspeed = 0 ;
MoveBall.canvas.ball.yspeed = -1 ;
}
if(ev.getKeyCode() == KeyEvent.VK_A)
{
MoveBall.canvas.ball.xspeed = -1 ;
MoveBall.canvas.ball.yspeed = 0 ;
}
if(ev.getKeyCode() == KeyEvent.VK_S)
{
MoveBall.canvas.ball.xspeed = 0 ;
MoveBall.canvas.ball.yspeed = 1 ;
}
if(ev.getKeyCode() == KeyEvent.VK_D)
{
MoveBall.canvas.ball.xspeed = 1 ;
MoveBall.canvas.ball.yspeed = 0 ;
}
}
public void keyReleased(KeyEvent arg0){}
public void keyTyped(KeyEvent arg0){}
}
解决方案
Yet, when the user press the keys, nothing happens.
Don't use a KeyListener. A KeyListener only works if the component has focus, so I would guess your component doesn't have focus.
Instead use Key Bindings, then the keyboard will work even if the component doesn't have focus.
See Motion Using the Keyboard for more information about both approaches. As well as working code for both approaches.
Also:
Custom painting should be done by overriding paintComponent(...), not paint(...). You should invoke super.paintComponent(...) at the start. Although in this case because you are extending JComponent the background will not be cleared automatically, so instead you need to add a fillRect(...) statement to paint the background of your component before painting the ball.
Don't use a ScheduledThreadPoolExecutor for animation. Updates to the state of GUI components should be done on the Event Dispatch Thread. You should be using a Swing Timer.
Take a look at the Swing Tutorial. There are sections on Custom Painting and How to Use Swing Timers to get you started.