Java Swing:有没有一种方法可以检查绘制形状的 MouseOver,而无需在每次鼠标移动时都进行检查?
Java Swing: Is there a way to check for MouseOver for a drawn shape without checking every time the mouse moves?
我有一个可以使用的 JPanel。我绘制的形状是存储在列表中的一些对象。我想在这些绘制的对象上注册 MouseOver。我目前正在做的是添加一个 MouseMotionListener,它会在每次鼠标移动时检查对象列表是否命中。一旦有很多对象,这当然是非常低效的。 是否有比每次鼠标移动时检查所有对象更好的方法来检查鼠标悬停?
这是一个最小的例子:
class 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);
}
}
class 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;
}
}
class 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());
}
}
}
}
class 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());
}
}
}
我建议不要使用 SomeObject
class,而是使用 Shape
或 Area
。 SomeObject 的整个目的似乎无论如何都变成了 Shape ,对吧?不仅如此,使用 ArrayList of Shapes,您可以避免在每次 mouseMove 时为您的形状创建矩形。
顺便说一句,几年前我整理了一个包来处理像第一个 class 组件这样的区域。你可以在这里看到:https://sourceforge.net/p/tus/code/HEAD/tree/tjacobs/ui/shape/
(从 AreaManager 和 AreaModel 开始)。 Area 有一些优点和缺点:优点:作为一个组易于管理,如果它们重叠则相当容易测试。缺点:您会丢失有关区域构建方式的信息(例如多边形点、圆半径等)
您已经取得了长足的进步,非常感谢您。这个答案是 (a) 回答你关于效率的问题,但也指出了你可以选择的一些方式
There is more to SomeObject than just being drawn, so I think I can't just replace it with Shape,
然后您保留一个 Rectangle
实例作为 SomeObject
class 的一部分,而不是您的 Point 和 length/height 变量。
然后您将方法修改为 return 来自 Rectangle
的值。
这将阻止您不断创建新的 Rectangle 实例,提高进程的内存效率并减少垃圾回收。
但是,您还应该能够扩展 Rectangle class 并添加您的额外功能,就像扩展 JPanel 以添加自定义绘画逻辑一样。
我有一个可以使用的 JPanel。我绘制的形状是存储在列表中的一些对象。我想在这些绘制的对象上注册 MouseOver。我目前正在做的是添加一个 MouseMotionListener,它会在每次鼠标移动时检查对象列表是否命中。一旦有很多对象,这当然是非常低效的。 是否有比每次鼠标移动时检查所有对象更好的方法来检查鼠标悬停?
这是一个最小的例子:
class 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);
}
}
class 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;
}
}
class 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());
}
}
}
}
class 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());
}
}
}
我建议不要使用 SomeObject
class,而是使用 Shape
或 Area
。 SomeObject 的整个目的似乎无论如何都变成了 Shape ,对吧?不仅如此,使用 ArrayList of Shapes,您可以避免在每次 mouseMove 时为您的形状创建矩形。
顺便说一句,几年前我整理了一个包来处理像第一个 class 组件这样的区域。你可以在这里看到:https://sourceforge.net/p/tus/code/HEAD/tree/tjacobs/ui/shape/
(从 AreaManager 和 AreaModel 开始)。 Area 有一些优点和缺点:优点:作为一个组易于管理,如果它们重叠则相当容易测试。缺点:您会丢失有关区域构建方式的信息(例如多边形点、圆半径等)
您已经取得了长足的进步,非常感谢您。这个答案是 (a) 回答你关于效率的问题,但也指出了你可以选择的一些方式
There is more to SomeObject than just being drawn, so I think I can't just replace it with Shape,
然后您保留一个 Rectangle
实例作为 SomeObject
class 的一部分,而不是您的 Point 和 length/height 变量。
然后您将方法修改为 return 来自 Rectangle
的值。
这将阻止您不断创建新的 Rectangle 实例,提高进程的内存效率并减少垃圾回收。
但是,您还应该能够扩展 Rectangle class 并添加您的额外功能,就像扩展 JPanel 以添加自定义绘画逻辑一样。