user3433599
user3433599

Reputation:

How to move move a rectangle in JFrame using KeyListener?

I'm trying to get a rectangle in my JFrame to move when I press a given key on the keyboard, but I'm seemingly having a hard time doing that. Here's my code:

package TestPackage;

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.JComponent;

public class Mainframe extends JComponent implements KeyListener
{
    private static final long serialVersionUID = 1L;

    int x = 350;
    int y = 250;

    public static void main (String[] args)
    {
        JFrame frame = new JFrame ("Mainframe");
        frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
        frame.setSize (800, 600);
        frame.setFocusable (true);
        frame.getContentPane().setBackground (Color.WHITE);
        frame.getContentPane().add (new Mainframe());
        frame.addKeyListener (new Mainframe());
        frame.setVisible (true);
    }

    public void paint (Graphics graphics)
    {
        graphics.setColor (Color.BLACK);
        graphics.fillRect (x, y, 100, 100);
    }

    public void keyPressed (KeyEvent event)
    {
        if (event.getKeyCode() == KeyEvent.VK_A)
        {
            x++;
            repaint();
        }
        else if (event.getKeyCode() == KeyEvent.VK_D)
        {
            y++;
            repaint();
        }
    }

    public void keyReleased (KeyEvent event) {}
    public void keyTyped (KeyEvent event) {}
}

I'm sure it's a problem with my KeyListener, as everything else works perfectly. Does anybody know what I'm doing wrong? Thanks.

Upvotes: 2

Views: 1149

Answers (2)

MadProgrammer
MadProgrammer

Reputation: 347194

  1. You're creating two instances of MainFrame, one you add to the frame and one you use as the JFrame's KeyListener, this means that any modifications the KeyListener instance makes to the x/y values, won't be seen by the instance on the UI
  2. Don't use KeyListener, it has too many focus related issues, use the Key Bindings API which was designed to overcome these issues. See How to Use Key Bindings
  3. Don't override paint (and especially if you're not going to call super.paint), instead, you should override the paintComponent method (and call super.paintComponent). See Painting in AWT and Swing and Performing Custom Painting for more details
  4. Make sure you are creating and modifying the UI only from within the context of the Event Dispatching Thread, see Initial Threads for more details

For example...

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
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.JComponent;
import javax.swing.JFrame;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Mainframe extends JComponent {

    private static final long serialVersionUID = 1L;

    int x = 350;
    int y = 250;

    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("Mainframe");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setSize(800, 600);
                frame.setFocusable(true);
                frame.getContentPane().setBackground(Color.WHITE);
                frame.getContentPane().add(new Mainframe());
                frame.setVisible(true);
            }
        });
    }

    public Mainframe() {
        bindKeyWith("y.up", KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), new VerticalAction(-1));
        bindKeyWith("y.down", KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), new VerticalAction(1));
        bindKeyWith("x.left", KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), new HorizontalAction(-1));
        bindKeyWith("x.right", KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), new HorizontalAction(1));
    }

    protected void bindKeyWith(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
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
        g.setColor(Color.BLACK);
        g.fillRect(x, y, 100, 100);
    }

    public abstract class MoveAction extends AbstractAction {

        private int delta;

        public MoveAction(int delta) {
            this.delta = delta;
        }

        public int getDelta() {
            return delta;
        }

        protected abstract void applyDelta();

        @Override
        public void actionPerformed(ActionEvent e) {
            applyDelta();
        }

    }

    public class VerticalAction extends MoveAction {

        public VerticalAction(int delta) {
            super(delta);
        }

        @Override
        protected void applyDelta() {
            int delta = getDelta();
            y += delta;
            if (y < 0) {
                y = 0;
            } else if (y + 100 > getHeight()) {
                y = getHeight() - 100;
            }
            repaint();
        }

    }
    public class HorizontalAction extends MoveAction {

        public HorizontalAction(int delta) {
            super(delta);
        }

        @Override
        protected void applyDelta() {
            int delta = getDelta();
            x += delta;
            if (x < 0) {
                x = 0;
            } else if (x + 100 > getWidth()) {
                x = getWidth() - 100;
            }
            repaint();
        }

    }
}

Upvotes: 1

JRowan
JRowan

Reputation: 7104

make your main like this and it will work

 public static void main (String[] args)
    {
        JFrame frame = new JFrame ("Mainframe");
        JComponent test = new Mainframe();
        frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
        frame.setSize (800, 600);
        frame.setFocusable (true);
        frame.getContentPane().setBackground (Color.WHITE);
        frame.getContentPane().add (test);
        frame.addKeyListener ((KeyListener) test);
        frame.setVisible (true);
    }

Upvotes: 0

Related Questions