多线程Java使对象在按住按钮的同时移动 1 月,1 周 Questions & Answers 6585 按下按钮时如何使JPanel移动,松开按钮时如何停止。我试过使用线程。用Runnable和类似的方式启动()。我总是遇到错误。有人能帮我吗
# 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和MouseReleased方法中调用start()和stop()方法
# 2 楼答案 你需要考虑一些重要的因素 按钮不是为这种方式设计的。它们被设计成在被点击(按下和释放)时触发和操作事件,所以你不能使用普通的操作API。幸运的是,还有其他方法可以确定按钮的状态。本例在ButtonModel上使用ChangeListener,并根据模型的状态采取操作李> 组件通常由布局管理器控制。这意味着为了能够移动组件,我们需要关闭它(也称为null或absolute布局)。通常情况下,我不鼓励这样做,但这是唯一可行的方法。然而移除布局管理器后,您将负责确保组件的位置和大小正确。。。这项工作不能掉以轻心。更多地了解你想要实现的目标会得到更好的答案 当按钮被“按下”时,我们需要一种方法来确定移动组件的方式。本例使用一个简单的enum来确定移动组件的方向。您可以同样轻松地使用x/yDelta并直接修改组件x/y位置。两者都可以李> Swing是一个单线程环境。也就是说,对UI的所有交互和修改都将在事件调度线程的上下文中执行。但任何阻止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上使用Key Bindings。主要原因是KeyListener存在焦点问题,密钥绑定API有能力克服或控制这些问题 基本前提是,您希望在按键和按键释放时注册按键操作。使用密钥绑定API可以相对容易地实现这一点 注意事项:这个例子一次只允许一个方向。例如,如果您按下向上和向下,向下操作将获胜。这是因为我使用了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(); } } } }
# 3 楼答案 使用^{}移动面板,将mouseListener添加到面板,并覆盖mousePressed和mouseReleased方法 更新: 评论如下: The OP said I want to move the panel with Keyboard buttons. 你在问题中没有提到键盘,你只是说按钮,不管怎样,这本教程对你现在和将来都有帮助 如果您有问题,请回来,我将发布一个移动面板的示例,但现在,我不会发布它,因为我相信您不会阅读教程,您只需复制我的示例,然后离开阅读图茨
# 1 楼答案
像这样的事情可能会做你想做的事情:
有一个包含
以及一个包含
现在,从添加到按钮的MouseListener的mousePressed和MouseReleased方法中调用
start()
和stop()
方法# 2 楼答案
你需要考虑一些重要的因素
ButtonModel
上使用ChangeListener
,并根据模型的状态采取操作李>null
或absolute
布局)。通常情况下,我不鼓励这样做,但这是唯一可行的方法。然而移除布局管理器后,您将负责确保组件的位置和大小正确。。。这项工作不能掉以轻心。更多地了解你想要实现的目标会得到更好的答案enum
来确定移动组件的方向。您可以同样轻松地使用x/yDelta
并直接修改组件x/y
位置。两者都可以李>while-loop
,因为它永远不会结束(不会处理新事件)。相反,本例使用了一个javax.swing.Timer
,它在后台等待,并在EDT上下文中的每个滴答上引发一个ActionEvent
。如果出现滴答声,我们将修改面板的位置你可能想通读Concurrency in Swing了解更多细节
根据OP的输入进行更新
令人惊讶的是,使用按键代替按键也是同样的方法。你有一个开始动作和一个结束动作,你只需要弄清楚如何应用这些状态
强烈建议您在
KeyListener
上使用Key Bindings。主要原因是KeyListener
存在焦点问题,密钥绑定API有能力克服或控制这些问题基本前提是,您希望在按键和按键释放时注册按键操作。使用密钥绑定API可以相对容易地实现这一点
注意事项:这个例子一次只允许一个方向。例如,如果您按下向上和向下,向下操作将获胜。这是因为我使用了
enum
作为方向。您可以通过使用xDelta
和yDelta
值来轻松更改此设置,这将允许您同时修改垂直和水平方向。。。但不能为你做一切;)# 3 楼答案
使用^{} 移动面板,将
mouseListener
添加到面板,并覆盖mousePressed
和mouseReleased
方法更新:
评论如下:
你在问题中没有提到键盘,你只是说按钮,不管怎样,这本教程对你现在和将来都有帮助
如果您有问题,请回来,我将发布一个移动面板的示例,但现在,我不会发布它,因为我相信您不会阅读教程,您只需复制我的示例,然后离开阅读图茨