Java Making objects move while buttons held

限于喜欢 提交于 2019-12-01 14:42:34

There are a number of important considerations that you need to take into consideration.

  1. Buttons aren't designed to work this way. They are designed to trigger and action event when they are clicked (pressed and released), so you can't use the normal action API. Lucky for us, there are other ways to determine the state of buttons. This example uses a ChangeListener on the ButtonModel and takes actions based on the state of the model.
  2. Components are normally under the control of layout managers. This means in order to be able to move the component, we need to turn this off (also known as null or absolute layouts). Normally, I would discourage this, but this is the only way this will work. However. Once you remove the layout manager, you become responsible for ensuring the component(s) are properly positioned and sized...this is not work to be taken lightly. More context of what you're trying to achieve would produce better answers
  3. While the button is "pressed", we need a way to determine which way to move the component. This example uses a simple enum to determine which direction to move the component. You could just as easily use a x/yDelta and modify the components x/y position directly. Either should work fine.
  4. Swing is a single thread environment. That is, all interactions and modifications to the UI are expected to be executed within the context of the Event Dispatching Thread. But any actions that block the EDT will prevent the UI from begin update or any new events from begin processed. This means, in order to move the component, we can't simply use a while-loop, because it will never end (no new events will be processed). Instead, this example uses a javax.swing.Timer, which waits in the background and raises an ActionEvent on each tick within the context of the EDT. The a tick occurs, we modify the location of the panel

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class MovePane {

    public static void main(String[] args) {
        new MovePane();
    }

    public MovePane() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public enum Direction {

        None, Up, Down, Left, Right;
    }

    public class TestPane extends JPanel {

        private JPanel mobby;
        private Timer moveTimer;
        private Direction moveDirection = Direction.None;

        public TestPane() {
            mobby = new JPanel();
            mobby.setBackground(Color.RED);
            mobby.setSize(50, 50);;
            setLayout(new BorderLayout());
            JPanel pool = new JPanel(null);
            pool.add(mobby);
            add(pool);
            JPanel buttons = new JPanel(new GridBagLayout());

            JButton up = new JButton("Up");
            JButton dwn = new JButton("Down");
            JButton lft = new JButton("Left");
            JButton rgt = new JButton("Right");

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.gridx = 1;
            gbc.gridy = 0;
            buttons.add(up, gbc);
            gbc.gridx = 1;
            gbc.gridy = 2;
            buttons.add(dwn, gbc);
            gbc.gridx = 0;
            gbc.gridy = 1;
            buttons.add(lft, gbc);
            gbc.gridx = 2;
            gbc.gridy = 1;
            buttons.add(rgt, gbc);

            add(buttons, BorderLayout.SOUTH);

            up.getModel().addChangeListener(new ChangeHandler(Direction.Up));
            dwn.getModel().addChangeListener(new ChangeHandler(Direction.Down));
            lft.getModel().addChangeListener(new ChangeHandler(Direction.Left));
            rgt.getModel().addChangeListener(new ChangeHandler(Direction.Right));

            moveTimer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    Container parent = mobby.getParent();
                    Rectangle bounds = mobby.getBounds();
                    switch (moveDirection) {
                        case Up:
                            bounds.y--;
                            break;
                        case Down:
                            bounds.y++;
                            break;
                        case Left:
                            bounds.x--;
                            break;
                        case Right:
                            bounds.x++;
                            break;
                    }

                    if (bounds.x < 0) {
                        bounds.x = 0;
                    } else if (bounds.x + bounds.width > parent.getWidth()) {
                        bounds.x = parent.getWidth() - bounds.width;
                    }
                    if (bounds.y < 0) {
                        bounds.y = 0;
                    } else if (bounds.y + bounds.height > parent.getHeight()) {
                        bounds.y = parent.getHeight() - bounds.height;
                    }

                    mobby.setBounds(bounds);

                }
            });
            moveTimer.setInitialDelay(0);

        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        public class ChangeHandler implements ChangeListener {

            private Direction direction;

            public ChangeHandler(Direction direction) {
                this.direction = direction;
            }

            @Override
            public void stateChanged(ChangeEvent e) {
                ButtonModel b = (ButtonModel) e.getSource();
                if (b.isPressed()) {
                    moveDirection = direction;
                    moveTimer.start();
                } else {
                    moveTimer.stop();
                }
            }

        }
    }

}

You might like to have a read through Concurrency in Swing for more details...

Updated based on input from the OP

Use key strokes instead of buttons is surprisingly the same approach. You have a start action and a end action, you just need to figure out how to apply those states.

It is highly recommended that you use Key Bindings over KeyListener. The main reason is KeyListener suffers from focus issues, which the key bindings API has the ability to over come or control.

The basic premise is, you want to register a key action on key press and key release. This is relatively easy to accomplish with the key bindings API.

Caveat: This example will only allow for a single direction at a time. If you press, for example, Up and Down, the down action will win out. This is because I'm using a enum for the direction. You can easily change this by using a xDelta and yDelta value instead, which would allow you to modify the vertical and horizontal directions simultaneously...but can't do everything for you ;)

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.ButtonModel;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class MovePane {

    public static void main(String[] args) {
        new MovePane();
    }

    public MovePane() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public enum Direction {

        None, Up, Down, Left, Right;
    }

    public class TestPane extends JPanel {

        private JPanel mobby;
        private Timer moveTimer;
        private Direction moveDirection = Direction.None;

        public TestPane() {
            mobby = new JPanel();
            mobby.setBackground(Color.RED);
            mobby.setSize(50, 50);;
            setLayout(new BorderLayout());
            JPanel pool = new JPanel(null);
            pool.add(mobby);
            add(pool);

            moveTimer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    Container parent = mobby.getParent();
                    Rectangle bounds = mobby.getBounds();
                    switch (moveDirection) {
                        case Up:
                            bounds.y--;
                            break;
                        case Down:
                            bounds.y++;
                            break;
                        case Left:
                            bounds.x--;
                            break;
                        case Right:
                            bounds.x++;
                            break;
                    }

                    if (bounds.x < 0) {
                        bounds.x = 0;
                    } else if (bounds.x + bounds.width > parent.getWidth()) {
                        bounds.x = parent.getWidth() - bounds.width;
                    }
                    if (bounds.y < 0) {
                        bounds.y = 0;
                    } else if (bounds.y + bounds.height > parent.getHeight()) {
                        bounds.y = parent.getHeight() - bounds.height;
                    }

                    mobby.setBounds(bounds);

                }
            });
            moveTimer.setInitialDelay(0);
            InputMap im = pool.getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = pool.getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "UpPressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "UpReleased");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "DownPressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "DownReleased");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "LeftPressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "LeftReleased");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "RightPressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "RightReleased");

            KeyUpAction keyUpAction = new KeyUpAction();
            am.put("UpReleased", keyUpAction);
            am.put("DownReleased", keyUpAction);
            am.put("LeftReleased", keyUpAction);
            am.put("RightReleased", keyUpAction);

            am.put("UpPressed", new MoveAction(Direction.Up));
            am.put("DownPressed", new MoveAction(Direction.Down));
            am.put("LeftPressed", new MoveAction(Direction.Left));
            am.put("RightPressed", new MoveAction(Direction.Right));

        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        public class KeyUpAction extends AbstractAction {

            @Override
            public void actionPerformed(ActionEvent e) {
                moveTimer.stop();
                moveDirection = Direction.None;
            }
        }

        public class MoveAction extends AbstractAction {

            private Direction direction;

            public MoveAction(Direction direction) {
                this.direction = direction;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                moveDirection = direction;
                moveTimer.start();
            }
        }
    }
}

Use SwingTimer for moving the panel and add mouseListener to the panel and override mousePressed and mouseReleased methods.


UPDATE:

From Comments:

The OP said I want to move the panel with Keyboard buttons.

You didn't mentioned Keyboard in the question, you just said buttons, anyway, Take this tutorial on How to use Key Bindings, it'll help you for now and future.

Come back if you had a question, I will post an example for moving the panel, but right now, I am not posting it because I am sure you'll not read the tutorial, you'll just copy my example and leave reading the tuts.

Something like this might do what you want to do:

Have a class which contains

private JPanel movingJPanel = new JPanel(); // Declare this however

public void paint(Graphics g) {
    //draw background first
    Point drawAt;
    syncronised (sync) {
        drawAt = this.drawAt
    }
    Dimension size = movingJPanel.getPreferredSize();
    Graphics paintWith = g.create(movingJPanel);
    movingJPanel.paint(paintWith);
}

private Point moveFrom = new Point(0, 0);
private Point moveTo = new Point(100, 100);    
private Point drawAt = new Point(0, 0);
private int steps = 35;
private int step = 0;
private long timeBetweenSteps = 50L;
private Object sync = new Object();
private boolean moving = false;

private Thread thread = new Thread(new Runnable() {
    public void run() {
        while (!Thread.interrupted()) {
            synchronized(sync) {
                if (moving && step < steps) {
                    step++;
                    drawAt = new Point((moveTo.x - moveFrom.x) * step / steps,
                            (moveTo.y - moveFrom.y) * step / steps)
                    drawMovingPanelIn.repaint();
                    sync.wait(timeBetweenSteps);
                }
            }
        }
    }
});

public void start() {
    synchronized(sync) {
        moving = true;
        sync.notify();
    }
}

public void start() {
    synchronized(sync) {
        moving = false;
    }
}

public void reset() {
    syncronized(sync) {
        steps = 0;
    }
}

and a constructor which contains

thread.start();

Now call the start() and stop() methods from the mousePressed and mouseRelaesed methods of a MouseListener that you add to a button.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!