[英]Java Swing: Awfully slow cursor response when hovering over rectangles
下面是代碼,其中用戶將鼠標光標移到一堆矩形上,並且當光標移到矩形上方時,光標應更改為手形光標,當光標不在給定矩形上方時,將恢復為默認的普通光標。
問題:解決方案有效,但速度很慢。它們有許多種“給貓咪剝皮”的方法,因此我們可以對問題進行多種建模,並且解決方案可以是各種各樣的,但是恆定的是,我們必須循環每個矩形都使用迭代器,並使用contain()方法確定JPanel上的給定點是否確實在矩形內,並相應地更改光標。
就像實現聲音所需要的一樣簡單,即在將鼠標懸停在特定矩形上的同時更改光標,隨着我添加不同的形狀和更多的矩形,程序開始明顯變慢。
下面的代碼簡單地顯示了與x軸對齊的矩形和與y軸對齊的矩形。我將它們拆分 (仍然可以將它們組合成一個列表)成兩個矩形列表。我在其中的一個while循環內遍歷兩個列表另一個,在適當的位置分別帶有break關鍵字。
我避免只使用一個大列表來容納兩種類型的矩形(或不同類型的形狀),因為
因此,我將形狀分類到不同的List中,如下面的示例中有兩個Lists,但是此技巧也失敗了,因為我必須順序地遍歷每個列表,所以我在另一個列表中有一個while循環。 ,因為一個循環必須在另一個循環之內(或在另一個循環之后開始),所以內部循環(或隨后的循環)會付出不必要的性能開銷,因為如果我們可以事先確定形狀屬於一個循環,則完全不需要第一個循環我們需要事先知道!標記您,以確保鼠標光標懸停在屬於圓形列表或矩形列表的形狀上! 這樣我們就可以遍歷特定列表。 甚至會變得更好,如果此時您仍可以按照我的推理進行操作,則無需使用contain()方法就可以事先知道形狀屬於哪個列表,因為在列表內部進行迭代時將使用contain()! !!
總而言之,下面的代碼只是兩個列表的線性迭代。 要訪問第二個列表,您必須先通過第一個列表。有人可以通過首先迭代第一個列表來進行訪問嗎?
如果我所有的解釋和探索都是錯誤的,那是沒有道理的。 題。 然后,如何改善下面代碼的游標響應。
編輯
很抱歉張貼無法編譯的代碼,我已經離開了這段代碼,開始玩我稱為線程的新玩具,直到最后我結成了一個無法解開的結。事實是我選擇了我希望選擇矩形並使用setRect()方法移動它們。我設想以預定的運動(即仿射變換)移動各種形狀,由於繪畫,重繪,搜索和所有艱苦的工作方式可能需要螺紋受益於一些多線程。無論如何,下面的代碼可以編譯,並且光標響應實際上是很好的!!! 。哎喲! 我有一個類似的實現,但是它的緩慢可能是由其他繪制矩形的類引起的,這與此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());
}
}
}
});
}
}
恕我直言,您的內部代碼應如下所示:
Cursor cursor = Cursor.getDefaultCursor();
Iterator <Rectangle2D> recs = rowBuffY.iterator();
//FIRST LOOP over Y axis rectangles
while(recs.hasNext()){
selectRec = recs.next();
if( selectRec.contains(p)){
cursor = Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
dragging = false;
moveLine = true;
break;
}
}
Iterator <Rectangle2D> recX = rowBuffX.iterator();
//SECOND LOOP over X axis rectangles
while(recX.hasNext()){
selectRec = recX.next();
if( selectRec.contains(p)){
cursor = Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR);
dragging = false;
moveLine = true;
break;
}
tpp.setCursor(cursor);
該代碼只對每個矩形檢查一次,並優先使用X軸矩形,而您的代碼將檢查每個X軸矩形N次(對於鼠標沒有懸停的每個Y軸矩形)。
正如ThomasKläger所建議的那樣,您應該取消嵌套循環。
如果仍然存在問題,則可能與您對setCursor()的調用過多有關。 特別是,在嵌套循環示例中,如果光標不在矩形中,則調用setCursor()來設置默認光標數百次。 每次調用setCursor()時,都會重新繪制光標,這是一個耗時的過程。
每個鼠標移動事件最多需要設置一次光標。 一種方法是在遍歷循環時為所需的游標類型設置一個布爾值,然后根據布爾值在兩個循環都退出后僅在最后設置游標。 為了提高效率,您還可以檢查當前光標是否已經是所需的光標,並且僅在需要更改時才調用setCursor()。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.