如何在按住按钮时进行JPanel移动,并在释放按钮时停止。我尝试过使用带有Runnable的thread.start()和类似的方法。我总是遇到错误。有人可以帮助我吗?
答案 0 :(得分:2)
您需要考虑许多重要因素。
ChangeListener
上使用ButtonModel
,并根据模型的状态执行操作。null
或absolute
布局)。通常,我会劝阻这一点,但这是唯一可行的方法。然而。删除布局管理器后,您将负责确保组件的正确定位和尺寸......这不是一件轻松的工作。更多关于你想要达到的目标会产生更好的答案enum
来确定移动组件的方向。您可以轻松使用x/yDelta
并直接修改组件x/y
位置。要么工作正常。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以了解更多详情......
使用击键而不是按钮令人惊讶地采用相同的方法。你有一个开始动作和一个结束动作,你只需要弄清楚如何应用这些状态。
强烈建议您使用Key Bindings而不是KeyListener
。主要原因是KeyListener
受到焦点问题的困扰,密钥绑定API有能力过度控制。
基本前提是,您希望在按键和按键释放时注册关键动作。使用密钥绑定API相对容易实现。
警告:此示例一次只允许单个方向。如果按,例如 Up 和 Down ,则向下操作将胜出。这是因为我使用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();
}
}
}
}
答案 1 :(得分:0)
使用SwingTimer
移动面板并将mouseListener
添加到面板并覆盖mousePressed
和mouseReleased
方法。
来自评论:
OP说我想用
Keyboard buttons
移动面板。
你在问题中没有提到键盘,你只是说按钮,无论如何,在How to use Key Bindings
上学习本教程,它会帮助你现在和将来。
如果您有问题,请回来,我会发布移动面板的示例,但是现在,我没有发布它,因为我确定您不会阅读教程,您只需复制我的示例离开读书。
答案 2 :(得分:-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()
方法。