开发者

Scrolled composite with slow to repaint content looks ugly

开发者 https://www.devze.com 2023-03-27 04:32 出处:网络
I am implementing a Gantt component for SWT and this takes a bit to repaint (like, 200 ms for the whole visible part of the diagram).

I am implementing a Gantt component for SWT and this takes a bit to repaint (like, 200 ms for the whole visible part of the diagram).

Now, when I scroll, I only repaint what is needed regarding the clipping rectangle. This makes the application look very bad when I scroll fast, because then the still visible part after scrolling seems to be moved by the OS first, and when I finished painting the remaining part (the part which has become visible during scrolling), immediatly a new scrolling step begins, moving half of my diagram to the right and lets me repaint the other half. This effectively looks like my diagram flickers in the middle during scrolling.

This doesn't look really nice. Is there a way to get around this? Is this question understandable?

EDIT: Here is a "small" test program that shows exactly the behaviour described. You only need SWT in the classpath to run it.

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(开发者_Python百科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);  
                    }
                }
            });
        }

    }

}


SWT painting is very fast and lacking UI can be usually tracked down to slow paint methods. Hence, try to optimize the algorithm that draws your diagram! One approach could be caching - draw the diagram contents into an Image:

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

and repaint only the necessary image parts when scrolling:

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

Once the diagram changes - and only then - repaint the cache image using your complex paint method.

HTH


I took Henrik's suggestion to use an Image to buffer the painting and implemented it in your SSCCE. I see much less flickering now on my system.

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


This issue is due to SWT.NO_BACKGROUND is used in your graphical SWT components.

0

精彩评论

暂无评论...
验证码 换一张
取 消