0

我有一个可以使用的 JPanel。我绘制的形状是一些存储在列表中的对象。我想在这些绘制的对象上注册 MouseOvers。我目前正在做的是添加一个 MouseMotionListener,它在每次鼠标移动时检查对象列表是否命中。一旦有很多对象,这当然是非常低效的。有没有比每次鼠标移动时检查所有对象更好的方法来检查 MouseOvers?

这是一个最小的例子:

类ui.java:

public class Ui {
    private static JFrame frame;
    private static DrawPanel drawPanel;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> { createGui();
        });
    }
    private static void createGui() {
        frame = new JFrame("Test");
        drawPanel = new DrawPanel();
        frame.setContentPane(drawPanel);
        frame.pack();
        frame.setVisible(true);
        frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
    }
}

SomeObject.java 类:

public class SomeObject {
    //class that represents some object that will be drawn. note that this is 
    //just a minmal example and that in my actual application, there are      
    //other aspects and functions to this class that have nothing to do with drawing. 
    //This is just kept small for the sake being a minimal example
    private String id;
    private Point2D origin;
    private int length;
    private int height;

    public SomeObject(String id, Point2D origin, int length, int height) {
        this.id = id;
        this.origin = origin;
        this.length = length;
        this.height = height;
    }

    public String getId() {
        return id;
    }

    public Point2D getOrigin() {
        return origin;
    }

    public int getLength() {
        return length;
    }

    public int getHeight() {
        return height;
    }
}

类 CustomMouseMotionListener.java:

public class CustomMouseMotionListener implements java.awt.event.MouseMotionListener {
    public CustomMouseMotionListener() { }

    @Override
    public void mouseDragged(MouseEvent e) {

    }

    @Override
    public void mouseMoved(MouseEvent e) {
        for (SomeObject object: DrawPanel.objectsToDraw) {
            Shape s = new Rectangle((int)object.getOrigin().getX(), (int)object.getOrigin().getY(), object.getLength(), object.getHeight());
            if (s.contains(e.getPoint())) {
                System.out.println("hit: " + object.getId());
            }
        }
    }
}

类 DrawPanel.java:

public class DrawPanel extends JPanel {
    public static List<SomeObject> objectsToDraw = new ArrayList<>();
    public DrawPanel() {
        objectsToDraw.add( new SomeObject("a", new Point2D.Float(20,1), 20,20));
        objectsToDraw.add( new SomeObject("b", new Point2D.Float(20,45), 20,20));

        addMouseMotionListener(new CustomMouseMotionListener());
        setFocusable(true);
        setVisible(true);
        grabFocus();
    }

    protected void paintComponent(Graphics g) {
        Graphics2D g2D = (Graphics2D) g;
        super.paintComponent(g2D);
        for (SomeObject object: objectsToDraw) {
            g.drawRect((int)object.getOrigin().getX(), (int)object.getOrigin().getY(), object.getLength(), object.getHeight());

        }
    }
}
4

2 回答 2

2

SomeObject我建议不要使用您的课程,而是使用Shapeor Area。SomeObject 的全部目的似乎是无论如何都要变成一个 Shape ,对吧?不仅如此,使用 ArrayList of Shapes,您可以避免在每次 mouseMove 中为您的形状创建矩形。

顺便说一句,几年前我整理了一个包来处理像第一类组件这样的区域。你可以在这里看到:https ://sourceforge.net/p/tus/code/HEAD/tree/tjacobs/ui/shape/

(从 AreaManager 和 AreaModel 开始)。区域有一些优点和缺点: 优点:易于作为一个组进行管理,如果它们重叠则相当容易测试。缺点:您会丢失有关区域如何构建的信息(例如多边形点、圆半径等)

你已经走了很长一段路,所以向你致敬。这个答案是(a)回答你关于效率的问题,但也指出你可以选择的一些方式

于 2021-01-04T15:42:20.003 回答
1

SomeObject 不仅仅是被绘制出来的,所以我认为我不能只用 Shape 替换它,

然后你保留一个Rectangle实例作为你的SomeObject类的一部分,而不是你的 Point 和长度/高度变量。

然后修改方法以从Rectangle.

这将阻止您不断创建新的 Rectangle 实例,使进程更有效地使用内存并减少垃圾收集。

但是,您还应该能够扩展 Rectangle 类并添加额外的功能,就像扩展 JPanel 以添加自定义绘画逻辑一样。

于 2021-01-04T16:08:22.903 回答