2011-05-16 75 views
0

我有一個擴展JPanel的類(下面),該面板位於JScrollPane中。它監聽(自己)鼠標事件並嘗試重新定位自己(拖動時)並重新調整自身(輪子上)以模擬鼠標移動和縮放。該面板還負責我的應用程序的主要視覺輸出。它存儲一個呈現在JScrollPane的可視區域(但在面板的圖形上)的BufferedImage。圖像的大小和形狀保持與可視區域相匹配。Java Swing繪畫和鼠標事件閃爍

我的問題是這樣的; 2)如果我用自己的繪畫方法重寫paint或paintComponent方法,這是可取的,以擺脫閃爍和其他繪畫問題,我仍然可以獲得相同的閃爍效果,並且從加載的圖像中獲取具有透明區域的圖形,然後將該區域着色爲黑色。當我手動調用我的paint方法而不重寫paint和paintComponent方法時,我仍然閃爍,但透明區域正確顯示。

我是新來的Swing繪畫,顯然做錯了什麼,誰能指出我在正確的方向來解決這個問題?

感謝

import jSim.simulation.Simulation; 
    import java.awt.Color; 
    import java.awt.Cursor; 
    import java.awt.Dimension; 
    import java.awt.Graphics; 
    import java.awt.Graphics2D; 
    import java.awt.Image; 
    import java.awt.Point; 
    import java.awt.Rectangle; 
    import java.awt.Toolkit; 
    import java.awt.event.MouseEvent; 
    import java.awt.event.MouseWheelEvent; 
    import java.awt.event.MouseWheelListener; 
    import java.awt.image.BufferStrategy; 
    import java.awt.image.BufferedImage; 
    import javax.swing.JPanel; 
    import javax.swing.JViewport; 
    import javax.swing.event.MouseInputListener; 

    public class SimPanel extends JPanel implements MouseWheelListener, MouseInputListener { 
     //Simulation 

     Simulation sim; 
     //Viewer 
     JViewport viewport; 
     Dimension viewSize; 
     BufferStrategy strat; 
     //Drawing 
     Image renderImage; 
     Graphics2D g2d; 
     boolean draw = true; 
     double scale = 1.0; 
     Object drawLock = new Object(); 
     //Mouse events 
     int m_XDifference, m_YDifference; 

     public SimPanel(JViewport viewport) { 
      this.viewport = viewport; 
      this.addMouseListener(this); 
      this.addMouseMotionListener(this); 
      this.addMouseWheelListener(this); 

      //this.setup(); 
     } 

     public SimPanel(Simulation sim, JViewport viewport) { 
      this.sim = sim; 
      this.viewport = viewport; 
      this.addMouseListener(this); 
      this.addMouseMotionListener(this); 
      this.addMouseWheelListener(this); 
      //this.setup(); 
     } 

     //Used to initialise the buffered image once drawing begins 
     private void setup() { 
      synchronized (drawLock) { 
       viewSize = viewport.getExtentSize(); 
       renderImage = new BufferedImage(viewSize.width, viewSize.height, BufferedImage.TYPE_INT_RGB); 
       g2d = (Graphics2D) renderImage.getGraphics(); 
      } 
     } 

    // @Override 
    // public void paint(Graphics g) 
    // { 
    //  synchronized(drawLock) { 
    //  //super.paintComponent(g); 
    //  paintSimulation(); 
    //  } 
    // } 
     //Paint the screen for a specific simulation 
     public void paintSimulation(Simulation sim) { 
      synchronized (drawLock) { 
       setSimulation(sim); 
       paintSimulation(); 
      } 
     } 

     //Paint the screen with the panels simulation 
     public void paintSimulation() { 
      synchronized (drawLock) { 
       //if no image, then init 
       if (renderImage == null) { 
        setup(); 
       } 
       //clear the screen 
       resetScreen(); 
       //draw the simulation if not null, to the image 
       if (sim != null) { 
        sim.draw(this); 
       } 
       //paint the screen with the image 
       paintScreen(); 
      } 
     } 

     private void resetScreen() { 
      Dimension newSize = viewport.getExtentSize(); 
      if (viewSize.height != newSize.height || viewSize.width != newSize.width || renderImage == null) { 
       //System.out.println("Screen Size Changed: " + viewSize + " " + newSize); 
       viewSize = newSize; 
       renderImage = new BufferedImage(viewSize.width, viewSize.height, BufferedImage.TYPE_INT_RGB); 
       g2d = (Graphics2D) renderImage.getGraphics(); 
      } else { 
       g2d.setBackground(Color.DARK_GRAY); 
       g2d.clearRect(0, 0, (int) (viewSize.width), (int) (viewSize.height)); 
      } 
     } 

     private void paintScreen() { 
      Graphics g; 
      Graphics2D g2; 
      try { 
       //g = viewport.getGraphics(); 
       g = this.getGraphics(); 
       g2 = (Graphics2D) g; 
       if ((g != null) && (renderImage != null)) { 
        g2.drawImage(renderImage, (int) viewport.getViewPosition().getX(), (int) viewport.getViewPosition().getY(), null); 
       } 
       Toolkit.getDefaultToolkit().sync(); // sync the display on some systems 
       g.dispose(); 
       g2.dispose(); 
       this.revalidate(); 
      } catch (Exception e) { 
       System.out.println("Graphics context error: " + e); 
      } 
     } 

     //Simulation makes calls to this method to draw items on the image 
     public void draw(BufferedImage image, int x, int y, Color colour) { 
      synchronized (drawLock) { 
       Rectangle r = viewport.getViewRect(); 
       if (g2d != null && draw) { 
        Point p = new Point((int) (x * scale), (int) (y * scale)); 
        if (r.contains(p)) { 
         if (scale < 1) { 
          Graphics2D g2 = (Graphics2D) image.getGraphics(); 
          Image test = image.getScaledInstance((int) (image.getWidth(null) * scale), (int) (image.getHeight(null) * scale), Image.SCALE_FAST); 
          g2d.drawImage(test, (int) ((x * scale - r.x)), (int) ((y * scale - r.y)), null); 
         } else { 
          g2d.drawImage(image, x - r.x, y - r.y, null); 
         } 
        } 
       } 
      } 
     } 

     public void setDraw(boolean draw) { 
      this.draw = draw; 
     } 

     public void setSimulation(Simulation sim) { 
      synchronized (drawLock) { 
       if (!(this.sim == sim)) { 
        this.sim = sim; 
       } 
      } 
     } 

     public void mouseWheelMoved(MouseWheelEvent e) { 
      synchronized (drawLock) { 
       updatePreferredSize(e.getWheelRotation(), e.getPoint()); 
      } 
     } 

     private void updatePreferredSize(int wheelRotation, Point stablePoint) { 
      double scaleFactor = findScaleFactor(wheelRotation); 
      if (scale * scaleFactor < 1 && scale * scaleFactor > 0.05) { 
       scaleBy(scaleFactor); 
       Point offset = findOffset(stablePoint, scaleFactor); 
       offsetBy(offset); 
       this.getParent().doLayout(); 
      } 
     } 

     private double findScaleFactor(int wheelRotation) { 
      double d = wheelRotation * 1.08; 
      return (d > 0) ? 1/d : -d; 
     } 

     private void scaleBy(double scaleFactor) { 
      int w = (int) (this.getWidth() * scaleFactor); 
      int h = (int) (this.getHeight() * scaleFactor); 
      this.setPreferredSize(new Dimension(w, h)); 
      this.scale = this.scale * scaleFactor; 
     } 

     private Point findOffset(Point stablePoint, double scaleFactor) { 
      int x = (int) (stablePoint.x * scaleFactor) - stablePoint.x; 
      int y = (int) (stablePoint.y * scaleFactor) - stablePoint.y; 
      return new Point(x, y); 
     } 

     private void offsetBy(Point offset) { 
      Point location = viewport.getViewPosition(); 
      //this.setLocation(location.x - offset.x, location.y - offset.y); 
      viewport.setViewPosition(new Point(location.x + offset.x, location.y + offset.y)); 
     } 

     public void mouseDragged(MouseEvent e) { 
      synchronized (drawLock) { 
       //Point p = this.getLocation(); 
       Point p = viewport.getViewPosition(); 
       int newX = p.x - (e.getX() - m_XDifference); 
       int newY = p.y - (e.getY() - m_YDifference); 
       //this.setLocation(newX, newY); 
       viewport.setViewPosition(new Point(newX, newY)); 
       //this.getParent().doLayout(); 
      } 
     } 

     public void mousePressed(MouseEvent e) { 
      synchronized (drawLock) { 
       setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 
       m_XDifference = e.getX(); 
       m_YDifference = e.getY(); 
      } 
     } 

     public void mouseReleased(MouseEvent e) { 
      synchronized (drawLock) { 
       setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 
      } 
     } 

     public void mouseClicked(MouseEvent e) { 
      //throw new UnsupportedOperationException("Not supported yet."); 
     } 

     public void mouseEntered(MouseEvent e) { 
      //throw new UnsupportedOperationException("Not supported yet."); 
     } 

     public void mouseExited(MouseEvent e) { 
      //throw new UnsupportedOperationException("Not supported yet."); 
     } 

     public void mouseMoved(MouseEvent e) { 
      //throw new UnsupportedOperationException("Not supported yet."); 
     } 
    } 
+0

爲什麼要同步所有的方法?我已與Swing合作多年,從未需要這樣做。 – Paul 2011-05-16 12:53:38

+0

對不起,我添加這些只是爲了看看它嘗試了很多其他的事情,並忘記帶出它們有什麼區別。我只是想檢查沒有什麼愚蠢的事情發生,像調整面板中等或類似的。 – James 2011-05-16 13:36:39

+0

擺動繪畫發生在EDT - 事件派發線程上。 「調整面板」事件將不會被處理,直到繪畫完成,因爲它們都在同一個線程上處理。這就是爲什麼如果一個事件導致一個漫長的過程,你應該在另一個線程中運行這個過程,這樣你的Swing UI仍然會響應並重新繪製自己。 – Paul 2011-05-16 13:47:45

回答

1

總之,查找雙緩衝。

較長的答案...

覆蓋paintComponent。創建一個屏幕外的圖形對象。在那個物體上做繪畫。將其複製到傳遞給paint方法的圖形對象中。

哦,並擺脫所有的同步。你不需要它。

+0

這就是我現在做的不是我嗎?我正在繪製圖像,然後我所有的繪製方法都會將該圖像的內容繪製到JPanel圖形對象。 – James 2011-05-16 13:34:07

+0

我錯過了上面...重寫paintComponent。同樣覆蓋更新如下: 'public void update(Graphics g) {paint(g); }' 更新會在默認情況下擦除面板 - 調用paint會阻止該操作。 – Paul 2011-05-16 13:42:52

+0

再次感謝評論,但我已經嘗試了上述無濟於事。還有什麼想法?它一直在推動我堅持這一點。我需要更多地瞭解Swing和繪畫。 – James 2011-05-16 15:04:59

0

如果添加上你通知視口的一個setOpaque(真)擺動,你會做所有的繪畫(尤其是背景)自己。這可能已經有所幫助了。

編輯

我張望了一下看着多了,覺得你應該重寫paintComponent。

你可以有2個圖像和四個參考:

  • imageToPaint = NULL
  • imageToWriteTo = NULL
  • bufferImageOne初始化成適當大小的BufferedImage
  • bufferImageTwo初始化成適當大小的BufferedImage

你會重寫paintComponent來繪製backgro然後drawImage(imageToPaint)(如果它不是null,它不應該是這樣)

您將有一個線程來執行imageToWriteTo的自定義繪製。 最後它交換了imageToPaint和imageToWriteTo。

然後你調用repaint()。這要求重繪,並且增加了一個優點,即Swing隊列上的所有重繪請求都合併在一起並生成單個繪圖。請勿重新驗證或同步。這個重新繪製是在你的第二個線程Event Event Dispatch Thread上自動完成的。

更新模擬圖像的方式與實際繪畫分離,繪製更新不需要等待模擬完成繪製。 它可能是由於圖像稍顯過時(使用緩衝時隱含一點)而產生的,但應該會產生更好的結果。

總之,對imageToWriteTo進行了昂貴的寫作。繪畫是使用imageToPaint完成的。昂貴的寫作以交換imageToWriteTo和imageToPaint結束。

+0

感謝您的回答,我已經嘗試過,但沒有任何區別。 – James 2011-05-16 13:32:58

+0

@James稍微更新了我的答案。 – extraneon 2011-05-16 18:59:30