我正在为SWT实现一个Gantt组件,这需要重新绘制一下(例如,图表的整个可见部分为200 ms)。
现在,当我滚动时,我只重新绘制剪切矩形所需的内容。当我快速滚动时,这使得应用程序看起来非常糟糕,因为滚动后仍然可见的部分似乎首先被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 :(得分: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
答案 1 :(得分: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();
}
});
答案 2 :(得分:0)
此问题是由于SWT造成的。图形SWT组件中使用了NO_BACKGROUND。