java - Compuesto desplazado con contenido lento para volver a pintar se ve feo
scroll swt (2)
La pintura SWT es muy rápida y, por lo general, la IU que falta puede rastrearse hasta métodos de pintura lentos. ¡Por lo tanto, intente optimizar el algoritmo que dibuja su diagrama! Un enfoque podría ser el almacenamiento en caché: dibujar los contenidos del diagrama en una Imagen:
Image cache = new Image(Display.getCurrent(), width, height);
GC gc = new GC(cache);
y volver a pintar solo las partes necesarias de la imagen al desplazarse:
gc.drawImage(cache, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight);
Una vez que el diagrama cambia, y solo entonces, vuelva a pintar la imagen del caché utilizando su método de pintura complejo.
HTH
Estoy implementando un componente de Gantt para SWT y esto tarda un poco en volver a pintar (como, 200 ms para toda la parte visible del diagrama).
Ahora, cuando me desplazo, solo vuelvo a pintar lo que se necesita con respecto al rectángulo de recorte. Esto hace que la aplicación se vea muy mal cuando me desplazo rápidamente, porque la parte todavía visible después del desplazamiento parece ser movida primero por el sistema operativo, y cuando terminé de pintar la parte restante (la parte que se ha vuelto visible durante el desplazamiento), inmediatamente comienza un nuevo paso de desplazamiento, moviendo la mitad de mi diagrama hacia la derecha y me permite volver a pintar la otra mitad. De hecho, parece que mi diagrama parpadea en el medio durante el desplazamiento.
Esto no se ve muy bien. ¿Hay alguna forma de evitar esto? ¿Es comprensible esta pregunta?
EDITAR : Aquí hay un programa de prueba "pequeño" que muestra exactamente el comportamiento descrito. Solo necesita SWT en el classpath para ejecutarlo.
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);
}
}
});
}
}
}
Tomé la sugerencia de Henrik de usar una Image
para amortiguar la pintura y la implementé en tu SSCCE. Veo mucho menos parpadeo ahora en mi sistema.
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();
}
});