0

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();

}
Robin
  • 36,233
  • 5
  • 47
  • 99
Reshad
  • 2,570
  • 8
  • 45
  • 86
  • it doesn't do anything.. it won't react on key presses and as you see i tried several ways but nothing seems to work. – Reshad Oct 01 '12 at 20:32
  • Have you assigned the KeyListener to the component you are working on and does this component have the keyboard focus? – Stefan Neubert Oct 01 '12 at 20:33
  • i've implemented both actionlistener and keylistener to my panel and assigned it like this but not sure if its correct goLeft = new JButton("Naar links"); goLeft.addActionListener(this); goLeft.addKeyListener(this); – Reshad Oct 01 '12 at 20:40
  • In this case I would use a combination of key bindings, Actions and buttons. When I get back to a PC I'll see if I can put together an example – MadProgrammer Oct 01 '12 at 21:28
  • Thank you @MadProgrammer i'll appreciate that! – Reshad Oct 01 '12 at 21:45
  • 1
    [`LinePanel`](http://stackoverflow.com/a/5797965/230513) is a related example. – trashgod Oct 02 '12 at 03:22

2 Answers2

3

Attaching your listener to the button has is probably not what you want. You probably want to add it to the JPanel that has the focus. And their is the catch ... the one that has the focus. Seems to be different on different platforms, and very unreliable to get your listener triggered.

Better is to opt for key bindings. The Swing keybindings tutorial contains enough sample code to illustrate how to use them.

The rest of your logic seems in order. Changing the position when the action is triggered and scheduling a repaint.

Robin
  • 36,233
  • 5
  • 47
  • 99
  • hmm so if i would change the KeyListener to this.addKeyListener(this); it would work right? cause now i get in my console the keyTyped and KeyPressed warnings or echo's whatever they are called so i think that is a good thing.. but i still can't make it work with the keybindings. could you give me an normal clean example – Reshad Oct 01 '12 at 21:34
3

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 ;)

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thanks for the example, its a little different then what i made and there has been used different functions( some i didn't work with as still learning Java) but it is a good example :) I'll try to use it in my own application – Reshad Oct 02 '12 at 07:48
  • The basic premise is, you can use a single `Action` to bind to a control (a button in this case) and a key stroke. Generally speaking, if you can get away with it, this is the netter approach. You might find [How to use Actions](http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html) and [How to use Key Bindings](http://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html) of some use. Good luck :D – MadProgrammer Oct 02 '12 at 09:09
  • hmm i tried for hours to let it work but it seems my Java skills are not ready yet.. ( just started 4 weeks ago with Java ) here is what i tried till now https://github.com/reshadf/Java/blob/master/DeBasis/src/h03Bal/Bal.java – Reshad Oct 02 '12 at 13:31