Search code examples
javaswingkey-bindingskeylistener

What method is used for Keyboard keys to move stuff in Java instead of ActionListener


Hello I created a little application that moves a ball that I've painted in Java but uses ActionListener, now I am new to Java and I would like to use mey keyboard to do this. What method should be used for this? I have seen people saying KeyBindings and some people say KeyListener I tried to figure out something myself but it doesn't work. the code I've wroten is as followed:

@Override
public void keyPressed(KeyEvent e) {
    System.out.println("keyPressed");

    char c = e.getKeyChar();

    if( c == KeyEvent.VK_LEFT  ) {

        pos -= 10;

    }

    repaint();
}

@Override
public void keyReleased(KeyEvent arg0) {
    // TODO Auto-generated method stub

}

@Override
public void keyTyped(KeyEvent arg0) {
    System.out.println("keyTyped");
    char v = arg0.getKeyChar();
    if( v == KeyEvent.KEY_LOCATION_RIGHT ) {

        pos -= 10;

    }
    repaint();

}

Solution

  • Okay, took a little longer then I'd hoped (fighting fires ;))

    This basically uses a combination of Actions, key bindings and buttons...

    Now I separated the ball rendering out to a separate class, this just allows me the chance to change how the ball is rendered without effecting the container...You obviously will have your own rendering process.

    MAIN

    public class BallsUp {
    
        public static void main(String[] args) {
            new BallsUp();
        }
    
        public BallsUp() {
            JFrame frame = new JFrame();
            frame.setTitle("Balls up");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(400, 400);
            frame.setLocationRelativeTo(null);
    
            BallPane ballPane = new BallPane();
            JPanel mainPane = new JPanel(new BorderLayout());
            mainPane.add(ballPane);
    
            JPanel north = new JPanel(new GridBagLayout());
            north.add(new JButton(new BallPane.UpAction(ballPane)));
            mainPane.add(north, BorderLayout.NORTH);
    
            JPanel south = new JPanel(new GridBagLayout());
            south.add(new JButton(new BallPane.DownAction(ballPane)));
            mainPane.add(south, BorderLayout.SOUTH);
    
            JPanel east = new JPanel(new GridBagLayout());
            east.add(new JButton(new BallPane.RightAction(ballPane)));
            mainPane.add(east, BorderLayout.EAST);
    
            JPanel west = new JPanel(new GridBagLayout());
            west.add(new JButton(new BallPane.LeftAction(ballPane)));
            mainPane.add(west, BorderLayout.WEST);
    
            frame.add(mainPane);
            frame.setVisible(true);
    
        }
    
    }
    

    The Ball Pane

    public class BallPane extends JPanel {
    
        protected static final int DISTANCE = 10;
    
        private Ball ball;
    
        private Timer resizeTimer;
        private ComponentListener componentListener;
    
        public BallPane() {
    
            setBall(new Ball());
    
            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = getActionMap();
    
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "goDown");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "goUp");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "goLeft");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "goRight");
    
            am.put("goDown", new DownAction(this));
            am.put("goUp", new UpAction(this));
            am.put("goLeft", new LeftAction(this));
            am.put("goRight", new RightAction(this));
    
            setFocusable(true);
            requestFocusInWindow();
    
        }
    
        public void setBall(Ball ball) {
            this.ball = ball;
        }
    
        public Ball getBall() {
            return ball;
        }
    
        @Override
        public void addNotify() {
    
            super.addNotify();
    
            componentListener = new ComponentAdapter() {
                @Override
                public void componentResized(ComponentEvent e) {
                    resizeTimer.restart();
                }
            };
    
            resizeTimer = new Timer(250, new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    removeComponentListener(componentListener);
                    Point p = new Point(getWidth() / 2, getHeight() / 2);
                    getBall().setLocation(p);
                    resizeTimer.stop();
                    resizeTimer = null;
                    repaint();
                }
            });
            resizeTimer.setRepeats(false);
            resizeTimer.setCoalesce(true);
    
            addComponentListener(componentListener);
    
        }
    
        @Override
        protected void paintComponent(Graphics g) {
    
            super.paintComponent(g);
    
            Ball ball = getBall();
            if (ball != null) {
    
                Graphics2D g2d = (Graphics2D) g;
                ball.paint(g2d);
    
            }
    
        }
    
        public static abstract class AbstractBallAction extends AbstractAction {
    
            private BallPane pane;
    
            public AbstractBallAction(BallPane pane) {
                this.pane = pane;
            }
    
            public BallPane getBallPane() {
                return pane;
            }
    
            public int getXDistance() {
                return 0;
            }
    
            public int getYDistance() {
                return 0;
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
    
                BallPane pane = getBallPane();
                Ball ball = pane.getBall();
    
                Point location = ball.getLocation();
                location.y += getYDistance();
                location.x += getXDistance();
                if (location.y < (ball.getWidth() / 2)) {
                    location.y = (ball.getWidth() / 2);
                } else if (location.y > pane.getHeight() - 1 - ball.getHeight()) {
                    location.y = pane.getHeight() - ball.getHeight();
                }
                if (location.x < (ball.getHeight() / 2)) {
                    location.x = (ball.getHeight() / 2);
                } else if (location.x > pane.getWidth() - 1 - ball.getWidth()) {
                    location.x = pane.getWidth() - ball.getWidth();
                }
                ball.setLocation(location);
                pane.repaint();
            }
    
        }
    
        public static class UpAction extends AbstractBallAction {
    
            public UpAction(BallPane pane) {
                super(pane);
                putValue(NAME, "Up");
            }
    
            @Override
            public int getYDistance() {
                return -DISTANCE;
            }
    
        }
    
        public static class DownAction extends AbstractBallAction {
    
            public DownAction(BallPane pane) {
                super(pane);
                putValue(NAME, "Down");
            }
    
            @Override
            public int getYDistance() {
                return DISTANCE;
            }
    
        }
    
        public static class LeftAction extends AbstractBallAction {
    
            public LeftAction(BallPane pane) {
                super(pane);
                putValue(NAME, "Left");
            }
    
            @Override
            public int getXDistance() {
                return -DISTANCE;
            }
    
        }
    
        public static class RightAction extends AbstractBallAction {
    
            public RightAction(BallPane pane) {
                super(pane);
                putValue(NAME, "Right");
            }
    
            @Override
            public int getXDistance() {
                return DISTANCE;
            }
    
        }
    
    }
    

    The Ball

    public class Ball {
    
        private Shape shape;
        private Point p;
    
        public Ball() {
            shape = new Ellipse2D.Float(0, 0, 10, 10);
        }
    
        public void setLocation(Point p) {
            this.p = p;
        }
    
        public Point getLocation() {
            return p;
        }
    
        public Shape getShape() {
            return shape;
        }
    
        public void paint(Graphics2D g2d) {
            Point p = getLocation();
            if (p != null) {
                g2d = (Graphics2D) g2d.create();
                g2d.setColor(Color.BLUE);
                Shape shape = getShape();
                int x = (int) p.x - (shape.getBounds().width / 2);
                int y = (int) p.y - (shape.getBounds().height / 2);
                g2d.translate(x, y);
                g2d.fill(shape);
                g2d.dispose();
            }
        }
    
        public int getWidth() {
            return getShape().getBounds().width;
        }
    
        public int getHeight() {
            return getShape().getBounds().width;
        }
    }
    

    I appologise, I got a little carried away, but this basic premise works ;)