Java Swing:将鼠标悬停在矩形上时,光标响应速度非常慢 [英] Java Swing: Awfully slow cursor response when hovering over rectangles

查看:22
本文介绍了Java Swing:将鼠标悬停在矩形上时,光标响应速度非常慢的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面是用户将鼠标光标移动到一堆矩形上的代码,并且光标应该在矩形上方时变为手形光标,并在不在给定矩形上方时恢复为默认的普通光标.

Below is code where a user is moving the mouse cursor over a bunch of rectangles and the cursor is supposed to change to a hand cursor while over the rectangles and revert to a default normal cursor when not on top of the given rectangles.

问题:该解决方案有效,但非常缓慢.它们有很多剥猫皮"的方法,因此我们可以对问题进行多种变体建模,并且解决方案可以是各种类型的,但是不变的是,我们必须循环使用迭代器遍历每个矩形并使用 contains() 方法确定 JPanel 上的给定点是否确实在矩形内,并相应地更改光标.

Problem: The solution works,but painfully slow.They are many ways "to skin a cat", therefore we can model the question in many variants and solutions could be of all kinds, but what is constant,is that we have to loop over each rectangle using an iterator and use the contain() method to ascertain if indeed a given point on the JPanel is within a rectangle or not,and change the cursor accordingly.

就像需要实现的声音一样简单,即在将鼠标悬停在特定矩形上时更改光标,当我添加不同的形状和更多的矩形时,程序开始显着变慢.

As simple as what is needed to be implemented sounds,i.e change cursor while hovering over specific rectangles, the program starts to slow considerably as I add different shapes and more rectangles.

下面的代码只是简单地显示了与x轴对齐的矩形和与y轴对齐的矩形.我将它们拆分(仍然可以将它们合并为一个列表)为两个矩形列表.I在另一个列表中使用 while 循环遍历两个列表,每个列表在适当的位置使用 break 关键字.

The code below simply shows rectangles aligned to the x-axis and rectangles aligned to the y-axis.I split them(still can combine them into one list) into two List of rectangles.I iterate over the two lists with a while loop within another,each with the break keyword at the appropriate place.

我避免只使用一个大列表来保存两种类型的矩形(或不同类型的形状),因为

I avoid using just one large list to hold both types of rectangle (or different types of shapes) because

  1. 我需要每隔一段时间添加不同的形状,将不同的形状分类在自己的列表中会更好、更易读.
  2. 我本能地试图通过为不同的形状使用不同的列表来缩短一个漫长的过程,并且如果可能,只迭代正确的列表而不是迭代其他不必要的形状.即一个大列表将线性增长并迭代***所有形状***以始终获得正确的形状.***一个大列表看起来不是一个如此聪明的实现吗?希望我尽量避免使用大列表的努力不是过早优化的情况!这是我认为使用线程的要点 并发或同时循环遍历不同的列表,但一个线程行为不当.
  1. I need to add different shapes every other time, and its better and more readable to have different shapes categorized in there own list.
  2. I am instinctively trying to shorten a long process by using different list for different shapes and if possible,iterate over the right list only and not iterate over other shapes that are unnecessary.i.e One large list will grow linearly and iterate over ***all shapes***to get to the right one ***all the time.***Doesn't the one large list seem not a so smart implementation? Hoping my best that efforts to avoid the One-Large-List is not a case of premature optimization!!!This the point I thought to use threads to loop over different list concurrently or simultaneously but one thread misbehaves.

那么,我对不同列表中的形状进行分类,因为下面的示例有两个列表.但是这个技巧也失败了,因为我必须按顺序迭代每个列表.所以我在另一个列表中有一个 while 循环.我没有避免迭代在不必要的列表上,因为一个循环必须在另一个循环内(或在另一个循环内开始),那么内部循环(或随后的循环)会付出不适当的性能开销,因为第一个循环是完全不必要的如果我们可以事先确定事先确定一个形状属于某个组.标记您,以确定鼠标光标是否悬停在属于圆形列表或矩形列表的形状上,这是我们需要事先了解em>!!以便我们迭代特定列表.它甚至会变得更好,如果此时您仍然可以按照我的推理,事先知道形状属于哪个列表必须在没有contains() 方法,因为在迭代内部 List 时要使用 contains() !!!

So then, I categorize shapes in different List, as the example below has two Lists.but this trick also fails as I have to iterate over each list sequentially.So I have a while loop within another.I have not avoided iterating over unnecessary list,as one loop must be within another(or start after another), then the inner loop (or the one that follows) pays an undue performance overhead since the first loop is totally unnecessary if we can ascertain before hand that a shape belongs to a certain group before hand.Mark you,to ascertain if a mouse cursor is hovering over a shape belonging to a List of circle,or a List of rectangles,is what we need to know beforehand !! so that we iterate over the specific list. It even gets better,if at this point you can still follow my reasoning, to know beforehand which List a shape belongs to has to been done without the contain() method, since contain() is to be used while iterating inside the List!!!

总而言之,下面的代码只是对两个列表的线性迭代.要访问第二个列表,您必须通过第一个.有没有办法先通过迭代第一个列表?

To summarize, the code below is simply a linear iteration over two list. To access the second List,you have to pass through the first.Is there a way one can by pass iterating the 1st list first?

如果我所有的解释和探索都是错误的,没有意义.问题.那么,如何改进下面代码的光标响应.

If all my explanations and explorations are all wrong and don't make sense. Question. Then,how do I improve the cursor response of the code below.

编辑

很抱歉发布了无法编译的代码,我已经从这个代码片段继续前进,并正在玩我的新玩具线程,直到我最终将自己绑在一个我无法摆脱的结中.真相我是否选择线程,因为我希望选择矩形并使用 setRect() 方法移动它们.我设想以预定的运动围绕各种形状移动,即仿射变换可能需要线程,因为绘画、重绘、搜索和各种困难工作,我从一些多线程中受益.无论如何,下面的代码编译并且光标响应实际上好!!!.哎哟!我有一个类似的实现,但它的缓慢可能是由绘制矩形的其他类引起的,不像在这个 SSCCE 中它们是由 for 循环绘制的.

Sorry for posting code that does not compile, i has so moved on from this code snippet and was playing with my new toys called threads,till i ended up tying myself in an knot which i could not disentangle myself from.The truth is i choose threads as I hope to select the rectangles and move them about using setRect() method.I envisioned that moving around shapes of all kinds in predetermined motions i.e affinetransformation may require threads due to painting, repainting , searching and all manner of hard work that my benefit from some multi-threading.Anyways, the code below compiles and the cursor response is actually good!!!.Ouch! I have a similar implementation but probably its slowness is being caused by other classes which draw the rectangles, unlike in this SSCCE where they are drawn by a for loop.

同时,如果有人有办法通过线程获得如此出色的性能,我们将不胜感激.提前致谢.

Mean while, if anyone has a way to get this good performance through threads, it would be highly appreciated.Thanks in advance.

    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());
                     }
                }                      
            }
        });
    }
    }        

推荐答案

正如 Thomas Kläger 所建议的,您应该取消嵌套循环.

As Thomas Kläger suggests, you should un-nest the loops.

如果仍然存在问题,可能与您过多地调用 setCursor() 有关.特别是,在嵌套循环示例中,如果光标不在矩形中,则调用 setCursor() 来设置默认光标数百次.每次调用 setCursor() 都会重新绘制游标,这是一个耗时的过程.

If there is still an issue, it probably has to do with the fact that you are calling setCursor() too much. In particular, in the nested loop example, if the cursor is not in a rectangle, you are calling setCursor() to set the default cursor hundreds of times. Each time you call setCursor(), the cursor is redrawn, which is a time consuming process.

每个 mousemoved 事件最多需要设置一次光标.执行此操作的一种方法是在遍历循环时设置有关所需光标类型的布尔值,然后仅在结束时设置光标,在两个循环都退出后,基于布尔值.为了进一步提高效率,您还可以检查当前光标是否已经是您想要的光标,只有在需要更改时才调用 setCursor().

You need to set the cursor at most once per mousemoved event. One way of doing this would be to set a boolean value about the desired cursor type while going through the loops, then setting the cursor only at the end, after both loops have exited, based on the boolean value. For further efficiency, you could also check whether the current cursor is already the one you want, and only call setCursor() if you need to change it.

这篇关于Java Swing:将鼠标悬停在矩形上时,光标响应速度非常慢的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆