2011-08-09 20 views
7

私はSWT用のGanttコンポーネントを実装していますが、これは再描画するのに少し時間がかかります(図の目に見える部分全体に対して200msのように)。スクロールされたコンテンツの再描画が遅い

私がスクロールすると、クリッピング矩形に関して必要なものだけが再描画されます。これは、スクロール後にまだ目に見える部分がOSによって最初に移動されたように見えるので、アプリケーションを非常に悪く見せてしまいます。残りの部分(スクロール中に見える部分)をペイントすると、すぐに新しいスクロールステップが始まり、私のダイアグラムの半分を右に動かし、残りの半分を再描画させます。これはスクロール中に私のダイアグラムが途中でちらつくように見えます。

これは本当にうまく見えません。これを回避する方法はありますか?この質問は理解できますか?

EDIT:ここに記載された動作を正確に示す「小さな」テストプログラムです。 SWTを実行するには、クラスパス内に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/)なしであなたを助ける方法は実際にはありません。あなたはGanttのダイアグラムコードを公開する必要はありません。SSCCEをダミーCgにして、同じスクロールテクニックを使って問題を正確に示してください。 – Sorceror

+0

問題を示す最小限の作業例を作成しようとします。 – Daniel

+0

[ダブルバッファリングとページ反転](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

OK、試してみます。 – Daniel

+0

も機能しません。イメージからコピーを行うと、手動でレンダリングするのと同じくらい遅いように見えるので、これまでと同じように醜いように見えます。 – Daniel

+0

クリッピング領域をdrawImage()の属性と同期させましたか?つまり、キャッシュされたイメージの必要な部分領域のみをペイントするのではなくコピーしましたか? – Henrik

0

私は絵をバッファリングするために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

関連する問題