0

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

4

3 回答 3

2

您需要考虑许多重要的注意事项。

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

        }
    }

}

您可能想阅读Swing 中的并发以了解更多详细信息...

根据 OP 的输入更新

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

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

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

警告: 此示例一次只允许一个方向。例如,如果您按UpDown,则向下动作将胜出。这是因为我使用的enum是方向。xDelta您可以通过使用 a和值轻松更改此设置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();
            }
        }
    }
}
于 2013-08-14T00:00:53.090 回答
0

用于SwingTimer移动面板并添加mouseListener到面板和覆盖mousePressedmouseReleased方法。


更新:

来自评论:

OP 说我想用Keyboard buttons.

您在问题中没有提到键盘,只是说按钮,无论如何,请继续阅读本教程How to use Key Bindings,它会在现在和将来对您有所帮助。

如果您有问题,请回来,我会发布一个移动面板的示例,但现在,我不会发布它,因为我确定您不会阅读教程,您只需复制我的示例并继续阅读啧啧。

于 2013-08-13T22:01:45.167 回答
-1

像这样的事情可能会做你想做的事:

有一个包含

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

和一个包含

thread.start();

现在从添加到按钮的 MouseListener 的 mousePressed 和 mouseRelaesed 方法调用start()和方法。stop()

于 2013-08-13T22:08:03.303 回答