【发布时间】:2018-02-27 16:07:41
【问题描述】:
下面是用户将鼠标光标移动到一堆矩形上的代码,并且光标应该在矩形上更改为手形光标,并在不在给定矩形顶部时恢复为默认的普通光标。
问题: 解决方案有效,但速度慢得令人痛苦。它们有很多“剥猫皮”的方法,因此我们可以对问题进行多种变体建模,解决方案可能多种多样,但不变的是,我们必须循环使用迭代器覆盖每个矩形,并使用 contains() 方法确定 JPanel 上的给定点是否确实在矩形内,并相应地更改光标。
就像需要实现的声音一样简单,即在将光标悬停在特定矩形上时更改光标,随着我添加不同的形状和更多的矩形,程序开始显着变慢。
下面的代码只是显示了与 x 轴对齐的矩形和与 y 轴对齐的矩形。我 将它们拆分(仍然可以将它们组合成一个列表)成两个矩形列表。我使用 while 循环在另一个列表中迭代这两个列表,每个列表在适当的位置使用 break 关键字。
我避免只使用一个大列表来保存两种类型的矩形(或不同类型的形状),因为
- 我需要每隔一段时间添加不同的形状,将不同的形状分类在自己的列表中会更好、更易读。
- 我本能地尝试通过为不同的形状使用不同的列表来缩短一个漫长的过程,如果可能,只迭代正确的列表而不迭代其他不必要的形状。即一个大列表将线性增长并迭代***所有形状***以始终找到正确的形状***。***不是一个大列表似乎不是一个那么聪明的实现吗?希望尽我所能避免 One-Large-List 的努力不是过早优化的情况!!!这是我想使用线程的一点to loop over different list concurrently or simultaneously but one thread misbehaves.
然后,我在不同的列表中对形状进行分类,因为下面的示例有两个列表。但是这个技巧也失败了,因为我必须按顺序迭代每个列表。所以我在另一个列表中有一个 while 循环。我没有避免迭代在不必要的列表上,因为一个循环必须在另一个循环中(或在另一个循环中开始),那么内部循环(或后面的循环)会支付不适当的性能开销,因为第一个循环是完全不必要的如果我们可以事先确定预先知道某个形状属于某个组。标记您,以确定鼠标光标是否悬停在属于圆形列表或矩形列表的形状上,这是我们需要事先了解的时间> !!这样我们就可以遍历特定的列表。 甚至会变得更好,如果此时您仍然可以按照我的推理,事先知道一个形状属于哪个列表必须在没有的情况下完成contains() 方法,因为在迭代 inside List 时会使用 contains() !!!
总而言之,下面的代码只是对两个列表的线性迭代。要访问第二个列表,您必须通过第一个。有没有办法可以先通过迭代第一个列表?
如果我所有的解释和探索都是错误的,没有意义。 问题。 那么,如何改善下面代码的光标响应。
编辑
很抱歉发布了无法编译的代码,我已经从这个代码 sn-p 继续前进,并且正在玩我的新玩具,称为线程,直到我最终把自己绑在一个我无法解开自己的结中。事实是我选择线程,因为我希望选择矩形并使用 setRect() 方法移动它们。我设想在预定运动中移动各种形状,即仿射变换可能需要线程由于绘画、重新绘画、搜索和所有方式我从一些多线程中受益的辛勤工作。无论如何,下面的代码编译并且光标响应实际上是 good!!!.Ouch!我有一个类似的实现,但它的缓慢可能是由绘制矩形的其他类引起的,不像在这个 SSCCE 中它们是由 for 循环绘制的。
同时,如果有人有办法通过线程获得这种良好的性能,我们将不胜感激。提前致谢。
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.*;
public class FlyingSaucerTwo extends JPanel {
Rectangle2D.Double rec;
Rectangle2D.Double rec1;
List<Rectangle2D.Double> recList;
List<Rectangle2D.Double> recList2;
Rectangle2D.Double mouseBoxx;
int f = 10;
int g = 0;
int s = 10;
int y = 5;
int z = 500;
public FlyingSaucerTwo(){
//FlyingSaucer needs to quickly identify specific points over given areas
//enclosed in rectangles.They use a 'divide and conquer' approach where
//different types of rectangles are first identified and a worker thread
//assigned to each category
mouseBoxx = new Rectangle.Double();
recList = new ArrayList<>();
recList2 = new ArrayList<>();
for(int i = 0; i < 15; i++){
rec = new Rectangle2D.Double(2+f,10+g,5,1000);
f +=50;
recList.add(rec);
}
f = 10;
for(int i = 0; i < 20; i++){
rec1 = new Rectangle2D.Double(2+y,10+s,1000,5);
s +=35;
recList2.add(rec1);
}
s = 10;
}
public static void main(String[] args) {
JFrame frame = new JFrame();
FlyingSaucerTwo fs = new FlyingSaucerTwo();
Laser laser = new Laser(fs);
fs.addMouseMotionListener(laser);
fs.addMouseListener(laser);
frame.getContentPane().add(fs);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(700,700);
frame.setVisible(true);
}
//@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
((Graphics2D)g).setColor(Color.RED);
int a = 10;
int b = 10;
for(int i = 0;i < recList.size();i++){
((Graphics2D)g).draw(recList.get(i));
}
for(int i = 0;i < recList2.size();i++){
((Graphics2D)g).draw(recList2.get(i));
}
}
}
class Laser implements MouseListener,MouseMotionListener{
Rectangle2D.Double mouseBox;
List<Rectangle2D.Double> recxList;
Rectangle2D.Double recx;
List<Rectangle2D.Double> recyList;
Rectangle2D.Double recy;
FlyingSaucerTwo fs;
public Laser(FlyingSaucerTwo fs){
this.fs = fs;
}
@Override
public void mouseClicked (MouseEvent e) { }
@Override
public void mousePressed (MouseEvent e) { }
@Override
public void mouseReleased(MouseEvent e) { }
@Override
public void mouseEntered (MouseEvent e) { }
@Override
public void mouseExited (MouseEvent e) { }
@Override
public void mouseDragged (MouseEvent e) { }
@Override
public void mouseMoved(MouseEvent e) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
Point p = e.getPoint();
recxList = fs.recList;
recyList = fs.recList2;
Iterator <Rectangle2D.Double> recX = recxList.iterator();
//FIRST LOOP over Y axis rectangles
while(recX.hasNext()){
recx = recX.next();
if( recx.contains(p)){
fs.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
break;
}
else if(recyList.size()>=0){
Iterator <Rectangle2D.Double> recY = recyList.iterator();
//SECOND LOOP over X axis rectangles
while(recY.hasNext()){
recy = recY.next();
if( recy.contains(p)){
fs.setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
break;
}
else{
fs.setCursor(Cursor.getDefaultCursor());
}
}
}
else{
fs.setCursor(Cursor.getDefaultCursor());
}
}
}
});
}
}
【问题讨论】:
-
不运行代码 - 立即考虑需要对每个
mouseMoved事件执行搜索 - 相反,您可以使用 SwingTimer来弹出一个小的延迟,例如 250-500 毫秒之间的移动,因此您基本上是在等待机会主义时刻进行搜索。还要考虑先比较最后一个已知的“好”结果,所以如果用户没有移动到最后找到的矩形之外,就不要再做搜索了 -
您也可以考虑研究一种算法,该算法根据与最后一个已知点的距离为您提供矩形列表,因此您在相对“接近”光标的范围内搜索 - 但我只有在你有一场体面的比赛后才会这样做
-
我不知道你的代码为什么这么慢。看起来你只有 35 个对象。当您将光标悬停在各种组件上时,Swing 可以毫无问题地处理光标的变化。而且我们无法测试您的代码,因为它无法编译。发布适当的minimal reproducible example 来演示问题。在我看来,您的分而治之方法太复杂了。尝试使用单个数组解决问题并仅遍历该数组。
-
或者使用基于图形的模型,这样可以让您更轻松地根据矩形潜入某个区域
-
线程用于在后台执行长时间运行的任务。在这种情况下,您希望光标立即更改,这意味着逻辑需要快速实时执行,而不是在后台执行。即使它是一个简单的线性搜索,我在搜索数百个对象的 ArrayList 时也从未遇到过问题。无论如何,您发布的代码不使用线程,并且 invokeLater(...) 是不必要的,因为从侦听器调用的代码已在 EDT 上执行。
标签: java swing arraylist jpanel java-2d