我的问题是,我应该创建一个新线程来等待 mouseClick 事件吗?
不,绝对不是。相反,您需要以某种方式将 GUI 的状态更改为等待鼠标单击模式,然后根据其状态更改 GUI 对鼠标单击的响应行为。通常状态由类的实例字段表示。因此,当您需要等待时,您可以更改这些状态字段之一,并在单击鼠标时检查该字段的状态并根据该状态改变发生的情况。例如,在回合制国际象棋游戏中,一个状态字段可以是private boolean blackTurn,然后根据鼠标的状态确定鼠标的行为。
以及如何获取鼠标点击的坐标?
在 MouseListener 中,MouseEvent 参数为您提供鼠标相对于被监听组件和屏幕的 x 和 y 位置。如果您的 MouseListener 附加到 JLabel,那么您可以通过 MouseEvent 的 getSource() 方法获取对单击的 JLabel 的引用,然后可以通过调用 getLocation() 获取 JLabel 相对于其容器 JPanel 的位置(如果需要)就可以了。
旁注:在您围绕精灵移动的 Swing GUI 中,通常最好不要将精灵放入 JLabels 中,而是直接在绘图 JPanel 的 paintComponent 方法中绘制它们。
作为我的意思的一个例子,这是一个绘制 4 个彩色圆圈的程序,这些圆圈是可拖动的,但只有在选择相应的 JRadioButton 且 JRadioButton 设置 GUI 的“状态”时才可拖动。这里状态由一个名为ColorState 的枚举表示,它包含4 种颜色和相应的文本。这是这个枚举:
import java.awt.Color;
public enum ColorState {
RED("Red", Color.RED),
GREEN("Green", Color.GREEN),
BLUE("Blue", Color.BLUE),
ORANGE("Orange", Color.ORANGE);
private String text;
private Color color;
private ColorState(String text, Color color) {
this.text = text;
this.color = color;
}
public String getText() {
return text;
}
public Color getColor() {
return color;
}
}
然后我们创建一个绘图 JPanel,它在 Map 中保存四个 Ellipse2D Shape 对象,
private Map<ColorState, Shape> colorStateMap = new EnumMap<>(ColorState.class);
在 for 循环中,我们创建 JRadioButton,为它们提供设置对象状态的 ActionListener,并使用 Ellipse2D Shape 对象填充地图
for (final ColorState state : ColorState.values()) {
// create the JRadioButton
JRadioButton radioButton = new JRadioButton(state.getText());
add(radioButton); // add to GUI
buttonGroup.add(radioButton); // add to ButtonGroup
// give it an ActionListener that changes the object's state
radioButton.addActionListener(e -> {
colorState = state;
});
// create a randomly placed Ellipse2D and place into Map:
double x = Math.random() * (W - CIRCLE_WIDTH);
double y = Math.random() * (H - CIRCLE_WIDTH);
Ellipse2D ellipse = new Ellipse2D.Double(x, y, CIRCLE_WIDTH, CIRCLE_WIDTH);
colorStateMap.put(state, ellipse);
}
我们在paintComponent中绘制椭圆:
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// make for smooth graphics
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// iterate through the enum, extracting the ellipse and drawing it
for (ColorState state : ColorState.values()) {
Shape shape = colorStateMap.get(state);
if (shape != null) {
g2.setColor(state.getColor());
g2.fill(shape); // draw the ellipse
}
}
}
最后在 MouseAdapter(MouseListener 和 MouseMotionListener)中,我们监听鼠标按下,如果左键被点击和如果它被点击在适当的范围内,我们就注册成功形状:
private class MyMouse extends MouseAdapter {
private Shape selectedShape = null;
private Point2D offset = null;
@Override
public void mousePressed(MouseEvent e) {
// check that correct button pressed
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
// has our colorState been set yet? If not, exit
if (colorState == null) {
return;
}
// is an appropriate Shape held by the Map? If so, get it
Shape shape = colorStateMap.get(colorState);
if (shape == null) {
return;
}
// does this shape contain the point where the mouse was pressed?
if (!shape.contains(e.getPoint())) {
return;
}
// Get the selected shape, get the mouse point location relative to this shape
selectedShape = shape;
double x = e.getX() - shape.getBounds2D().getX();
double y = e.getY() - shape.getBounds2D().getY();
offset = new Point2D.Double(x, y);
}
@Override
public void mouseDragged(MouseEvent e) {
// drag shape to new location
if (selectedShape != null) {
double x = e.getX() - offset.getX();
double y = e.getY() - offset.getY();
Rectangle2D bounds = selectedShape.getBounds2D();
bounds.setFrame(new Rectangle2D.Double(x, y, bounds.getWidth(), bounds.getHeight()));
((Ellipse2D) selectedShape).setFrame(bounds);
repaint();
}
}
@Override
public void mouseReleased(MouseEvent e) {
selectedShape = null;
}
}
注意鼠标拖动代码感谢 MadProgrammer 的回答here。请对此答案投票。
整个类是这样的:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.EnumMap;
import java.util.Map;
import javax.swing.*;
@SuppressWarnings("serial")
public class StateDependentMouseListener extends JPanel {
private static final int W = 800;
private static final int H = 650;
private static final double CIRCLE_WIDTH = 60.0;
private ButtonGroup buttonGroup = new ButtonGroup();
private ColorState colorState = null;
private Map<ColorState, Shape> colorStateMap = new EnumMap<>(ColorState.class);
public StateDependentMouseListener() {
setPreferredSize(new Dimension(W, H));
for (final ColorState state : ColorState.values()) {
// create the JRadioButton
JRadioButton radioButton = new JRadioButton(state.getText());
add(radioButton); // add to GUI
buttonGroup.add(radioButton); // add to ButtonGroup
// give it an ActionListener that changes the object's state
radioButton.addActionListener(e -> {
colorState = state;
});
// create a randomly placed Ellipse2D and place into Map:
double x = Math.random() * (W - CIRCLE_WIDTH);
double y = Math.random() * (H - CIRCLE_WIDTH);
Ellipse2D ellipse = new Ellipse2D.Double(x, y, CIRCLE_WIDTH, CIRCLE_WIDTH);
colorStateMap.put(state, ellipse);
}
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// make for smooth graphics
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// iterate through the enum, extracting the ellipse and drawing it
for (ColorState state : ColorState.values()) {
Shape shape = colorStateMap.get(state);
if (shape != null) {
g2.setColor(state.getColor());
g2.fill(shape); // draw the ellipse
}
}
}
private class MyMouse extends MouseAdapter {
private Shape selectedShape = null;
private Point2D offset = null;
@Override
public void mousePressed(MouseEvent e) {
// check that correct button pressed
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
// has our colorState been set yet? If not, exit
if (colorState == null) {
return;
}
// is an appropriate Shape held by the Map? If so, get it
Shape shape = colorStateMap.get(colorState);
if (shape == null) {
return;
}
// does this shape contain the point where the mouse was pressed?
if (!shape.contains(e.getPoint())) {
return;
}
// Get the selected shape, get the mouse point location relative to this shape
selectedShape = shape;
double x = e.getX() - shape.getBounds2D().getX();
double y = e.getY() - shape.getBounds2D().getY();
offset = new Point2D.Double(x, y);
}
@Override
public void mouseDragged(MouseEvent e) {
// drag shape to new location
if (selectedShape != null) {
double x = e.getX() - offset.getX();
double y = e.getY() - offset.getY();
Rectangle2D bounds = selectedShape.getBounds2D();
bounds.setFrame(new Rectangle2D.Double(x, y, bounds.getWidth(), bounds.getHeight()));
((Ellipse2D) selectedShape).setFrame(bounds);
repaint();
}
}
@Override
public void mouseReleased(MouseEvent e) {
selectedShape = null;
}
}
private static void createAndShowGui() {
StateDependentMouseListener mainPanel = new StateDependentMouseListener();
JFrame frame = new JFrame("StateDependentMouseListener");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}