2011-08-09 75 views
7

我正在爲SWT實現一個甘特圖組件,並且這需要一點時間重新繪製(例如,圖中整個可見部分的時間爲200毫秒)。滾動合成緩慢重新繪製內容看起來很醜

現在,當我滾動時,我只重畫關於剪切矩形所需的東西。當我滾動快時,這使得應用程序看起來非常糟糕,因爲那麼滾動後仍然可見的部分似乎先被OS移動,並且當我完成繪製剩餘部分(在滾動期間變得可見的部分)時,立即新的滾動步驟開始,將我的一半圖移到右側,讓我重新繪製另一半。這看起來好像我的圖在滾動過程中在中間閃爍。

這看起來不太好。有沒有辦法解決這個問題?這個問題可以理解嗎?

編輯:這裏是一個「小」的測試程序,完全顯示所描述的行爲。你只需要在類路徑中運行SWT。

package de.ikoffice.gui; 

import org.eclipse.swt.SWT; 
import org.eclipse.swt.custom.ScrolledComposite; 
import org.eclipse.swt.events.PaintEvent; 
import org.eclipse.swt.events.PaintListener; 
import org.eclipse.swt.graphics.Color; 
import org.eclipse.swt.graphics.GC; 
import org.eclipse.swt.graphics.Rectangle; 
import org.eclipse.swt.layout.GridData; 
import org.eclipse.swt.layout.GridLayout; 
import org.eclipse.swt.widgets.Canvas; 
import org.eclipse.swt.widgets.Composite; 
import org.eclipse.swt.widgets.Display; 
import org.eclipse.swt.widgets.Shell; 

public class SlowRepaintProblem { 

    public Color[] colors = new Color[501]; 

    public SlowRepaintProblem() { 
     Display display = Display.getDefault(); 
     for(int i=0; i<=500; i++) { 
      int r = (i * 10) % 256; 
      int g = (i * 20) % 256; 
      int b = (i * 30) % 256; 
      colors[i] = new Color(display,r,g,b); 
     } 

     Shell shell = new Shell(display); 
     shell.setText("SlowRepaintTest"); 
     ScrolledComposite comp = new ScrolledComposite(shell, 
       SWT.H_SCROLL | SWT.V_SCROLL | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND); 
     SlowRepaintingCanvas canvas = new SlowRepaintingCanvas(comp,SWT.NONE| SWT.NO_BACKGROUND); 
     comp.setContent(canvas); 
     canvas.setSize(5000,5000); 

     // Layouting 
     shell.setLayout(new GridLayout());   
     comp.setLayoutData(new GridData(GridData.FILL_BOTH)); 
     shell.setBounds(50, 50, 800, 600); 

     // Showing the control 
     shell.open(); 
     while (!shell.isDisposed()) { 
      try { 
       if (!shell.getDisplay().readAndDispatch()) { 
        shell.getDisplay().sleep(); 
       } 
      } catch (Throwable e) { 
       String message = e.getMessage(); 
       if(message == null || !e.getMessage().equals("Widget is diposed")) { 
        e.printStackTrace(); 
       } 
       break; 
      } 
     } 
    } 

    public static void main(String[] args) { 
     new SlowRepaintProblem(); // Evil constructor call to start main program flow. 
    } 

    class SlowRepaintingCanvas extends Canvas { 

     public SlowRepaintingCanvas(Composite parent, int style) { 
      super(parent, style); 

      addPaintListener(new PaintListener() { 
       @Override 
       public void paintControl(PaintEvent e) { 
        GC gc = e.gc; 
        Rectangle r = gc.getClipping(); 
        gc.setAlpha(255); 
//     gc.setBackground(ColorUtils.WHITE); 
//     gc.fillRectangle(r); 

        int x = r.x - (r.x % 10); 
        int width = (r.width + r.x - x) - (r.width + r.x - x) % 10 + 10; 
        int y = r.y - (r.y % 10); 
        int height = (r.height + r.y - y) - (r.height + r.y - y) % 10 + 10; 

        gc.setAlpha(128); 
        for(int i = x; i < x+width; i+= 10) { 
         gc.setBackground(colors[i/10]); 
         gc.fillRectangle(i, r.y, 10, r.height); 
        } 
        for(int j = y; j < y+height; j+= 10) { 
         gc.setBackground(colors[j/10]); 
         gc.fillRectangle(r.x, j, r.width, 10); 
        } 
       } 
      }); 
     } 

    } 

} 
+0

閱讀的問題和意見在第一個答案之後,實在沒有辦法怎麼幫你沒有[SSCCE(http://sscce.org/)。您不必公開您的甘特圖代碼,只需使用任何虛擬cg製作SSCCE並使用相同的滾動技術向我們顯示問題。 – Sorceror

+0

好的,我將嘗試創建一個顯示此問題的最小工作示例。 – Daniel

+0

您是否試過[double-buffering and page-flipping](http://download.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html)技巧,並且您是否說他們不適合您? –

回答

7

SWT繪畫速度非常快,缺乏UI通常可以追溯到減緩繪畫方法。因此,嘗試優化繪製圖的算法!一種方法可能是緩存 - 繪製圖表的內容爲圖片:

Image cache = new Image(Display.getCurrent(), width, height); 
GC gc = new GC(cache); 

和重繪只有必要的圖像部分滾動時:

gc.drawImage(cache, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight); 

一旦圖變化 - 然後才 - 重繪緩存使用複雜的塗料方法的圖像。

HTH

+0

好的,我會試試看。 – Daniel

+0

也不起作用。仍然看起來像以前一樣醜陋,因爲從圖像中複製看起來和手動渲染一樣慢。 – Daniel

+0

您是否使用drawImage()中的屬性同步裁剪區域,即是否只複製緩存圖像的必要子區域而不是繪製整個區域? – Henrik

0

我把Henrik的建議,使用Image緩衝繪畫,並在您SSCCE付諸實施。我現在在我的系統上看到的閃爍少得多。

 addPaintListener(new PaintListener() { 
      @Override 
      public void paintControl(PaintEvent e) { 
       GC gc = e.gc; 
       Rectangle r = gc.getClipping(); 

       int x = r.x - (r.x % 10); 
       int width = (r.width + r.x - x) - (r.width + r.x - x) % 10 + 10; 
       int y = r.y - (r.y % 10); 
       int height = (r.height + r.y - y) - (r.height + r.y - y) % 10 + 10; 

       Image image = new Image(gc.getDevice(),width,height); 
       GC igc = new GC(image); 

       // Without buffering, this code was necessary to prevent "ghost" 
       // scrollbars on window resize, but with buffering it is no longer 
       // required...it does affect the visual results however. 
       //igc.setAlpha(255); 
       //igc.setBackground(gc.getDevice().getSystemColor(SWT.COLOR_BLACK)); 
       //igc.fillRectangle(image.getBounds()); 

       igc.setAlpha(128); 
       for(int i = x; i < x+width; i+= 10) { 
        igc.setBackground(colors[i/10]); 
        igc.fillRectangle(i-x, 0, 10, height); 
       } 
       for(int j = y; j < y+height; j+= 10) { 
        igc.setBackground(colors[j/10]); 
        igc.fillRectangle(0, j-y, width, 10); 
       } 

       gc.drawImage(image, x, y); 
       igc.dispose(); 
       image.dispose(); 
      } 
     }); 
+0

糟糕,剛剛閱讀Henrik的答案下的評論歷史更接近,似乎你已經嘗試過一個圖像緩衝區,它沒有足夠的幫助?無論如何,我會在這裏留下我的答案,以防其他人發現它有用...... –

+0

但你說得對。這裏更少閃爍。狠狠地說,如果我將相同的技術應用於我的甘特圖,它會慢3倍。我將不得不看看我是否還有其他錯誤。 – Daniel