Java使对象在按住按钮的同时移动[英] Java Making objects move while buttons held

问题描述

如何让 JPanel 在按住按钮时移动并在释放按钮时停止.我尝试过使用带有 Runnable 的 thread.start() 和类似的方法.我总是遇到错误.有谁能帮帮我吗?

推荐答案

您需要考虑许多重要的考虑因素.

  1. 按钮并非设计为以这种方式工作.它们旨在在单击(按下和释放)时触发和操作事件,因此您不能使用正常的操作 API.幸运的是,还有其他方法可以确定按钮的状态.此示例在 ButtonModel 上使用 ChangeListener 并根据模型的状态执行操作.
  2. 组件通常受布局管理器的控制.这意味着为了能够移动组件,我们需要将其关闭(也称为 null 或 absolute 布局).通常,我不鼓励这样做,但这是唯一可行的方法.然而.删除布局管理器后,您将负责确保组件正确定位和调整大小……这不能掉以轻心.更多关于您想要达到的目标的背景信息会产生更好的答案
  3. 当按钮被"按下"时,我们需要一种方法来确定移动组件的方式.此示例使用一个简单的 enum 来确定移动组件的方向.您可以轻松地使用 x/yDelta 并直接修改组件的 x/y 位置.两者都应该可以正常工作.
  4. Swing 是一个单线程环境.也就是说,对 UI 的所有交互和修改都应该在 Event Dispatching Thread 的上下文中执行.但是任何阻止 EDT 的操作都会阻止 UI 开始更新或任何新事件开始处理.这意味着,为了移动组件,我们不能简单地使用 while-loop,因为它永远不会结束(不会处理新事件).相反,此示例使用 javax.swing.Timer,它在后台等待并在 EDT 上下文中的每个刻度上引发一个 ActionEvent.打勾出现,我们修改面板的位置

在此处输入图片描述

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

        }
    }

}

您可能希望阅读 Concurrency in Swing更多细节...

根据 OP 的输入更新

使用击键代替按钮出人意料地是相同的方法.你有一个开始动作和一个结束动作,你只需要弄清楚如何应用这些状态.

强烈建议您使用 键绑定超过KeyListener.主要原因是 KeyListener 存在焦点问题,键绑定 API 有能力克服或控制.

基本前提是,您要在按键和按键释放时注册一个按键动作.这使用键绑定 API 相对容易实现.

警告:此示例一次只允许一个方向.例如,如果您按下 UpDown,则向下操作将胜出.这是因为我使用 enum 作为方向.您可以通过使用 xDelta 和 yDelta 值轻松更改此设置,这将允许您同时修改垂直和水平方向......但不能为您做所有事情;)

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

本文地址:https://www.itbaoku.cn/post/978543.html