来自 JComponents 的鼠标事件

MouseEvents from JComponents

我用 swing 写了一个游戏,但鼠标事件有问题。

我有一个扩展 JFrame 的面板,它添加了一个板对象,该对象在列表中包含 "Paintable" 个对象。这些对象包含其他具有可绘制对象的列表,它会不断调用子对象,直到它到达实际绘制它的最低对象。

public interface Paintable {
   void paint(Graphics2D g);
}

我写了一个小例子来说明我的意思:

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.List;

public class MainPanel extends JPanel {

    List<Patch> patches = new ArrayList<>();

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        MainPanel mainPanel = new MainPanel();
        mainPanel.addPatches();

        mainPanel.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                System.out.println("out");

                // I want to access the clicked object here
            }
        });

        frame.add(mainPanel);
        frame.setSize(1000, 800);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private void addPatches() {
        Random random = new Random();

        for (int i = 0; i < 100; i++) {
            patches.add(new Patch(random.nextInt(1000), random.nextInt(800), 30, 30));
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        patches.forEach(patch -> patch.paintComponent(g));
    }
}

Class 绘制对象的位置:

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class Patch extends JComponent {

    int x;
    int y;
    int width;
    int height;

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        g.fillRect(x, y, width, height);
    }

    public Patch(int x, int y, int width, int height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;

        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                System.out.println("in");
            }
        });
    }
}

我想在 mouseClicked 事件中访问 MainPanel 中被点击的 Patch。我该怎么做?

我也试过给 Patch 添加一个监听器 class 但这也不起作用。

如果您检查 Patch 组件的大小,您可能会发现它们是 0、0 或 1,1,无论如何,它们都非常小,因为它们的 preferredSizes 非常小。我建议与其让 Patch 扩展 JComponent,不如使它成为一个逻辑 non-component 实体,并通过使用 Shape 派生对象(由 MainPanel 对象保存在 ArrayList 等集合中的对象)为其提供鼠标单击属性,并在添加到 MainPanel 的 MouseListener 中通知可能的鼠标按下。

例如,

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.List;

@SuppressWarnings("serial")
public class MainPanel extends JPanel {
    private static final Color[] COLORS = { Color.red, Color.orange, Color.yellow, Color.blue,
            Color.green, Color.cyan, Color.magenta };
    private List<Patch> patches = new ArrayList<>();
    Random random = new Random();

    public MainPanel() {
        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                // use mousePressed not mouseClicked

                // go backwards for correct handling of overlaps
                for (int i = patches.size() - 1; i >= 0; i--) {
                    if (patches.get(i).contains(e.getPoint())) {
                        System.out.println("pressed");
                        e.consume();
                        return;
                    }
                }
                System.out.println("out");
            }
        });
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        MainPanel mainPanel = new MainPanel();
        mainPanel.addPatches();

        frame.add(mainPanel);
        frame.setSize(1000, 800);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private void addPatches() {

        for (int i = 0; i < 100; i++) {
            Color c = COLORS[random.nextInt(COLORS.length)];
            patches.add(new Patch(random.nextInt(1000), random.nextInt(800), 30, 30, c));
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        patches.forEach(patch -> patch.draw(g));
    }
}

class Patch {

    private int x;
    private int y;
    private int width;
    private int height;
    private Color color;
    private Rectangle rectangle;

    public Patch(int x, int y, int width, int height, Color color) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        this.color = color;
        rectangle = new Rectangle(x, y, width, height);
    }

    public boolean contains(Point p) {
        return rectangle.contains(p);
    }

    public void draw(Graphics g) {
        Color oldColor = g.getColor();
        g.setColor(color);
        g.fillRect(x, y, width, height);
        g.setColor(oldColor);
    }

}