Wie kann ein Swing JWindow in der Größe verändert werden, ohne zu flackern?
Ich versuche, eine benutzerdefinierte Benutzeroberfläche basiert auf einem JWindow
für die Zwecke der Auswahl einer Fläche des Bildschirms geteilt werden. Ich habe erweiterte JWindow
und zusätzlichen code, um es der Größe veränderbar und 'cut out' - das Zentrum des Fensters mit AWTUtilities.setWindowShape()
.
Wenn der code ausgeführt wird, erlebe ich ein flimmern, wie das Fenster in der Größe geändert wird, in negative x-und y-Richtung, d.h. nach oben und nach Links. Was scheint zu geschehen, ist, dass das Fenster in der Größe verändert und gezeichnet, bevor die Komponenten aktualisiert werden. Unten ist eine vereinfachte version des Codes. Bei der Ausführung der Deckplatte kann verwendet werden, um die Größe des Fensters nach oben und nach Links. Der hintergrund des Fensters wird auf grün gesetzt, um deutlich zu machen, wo die Pixel, die will ich nicht zeigen werden.
Edit: Verbessert den code, um die Form der Fenster ordnungsgemäß mit einem ComponentListener
und fügte hinzu, eine dummy-Komponente am unteren Rand, um zu zeigen, flimmern (auch aktualisiert, screenshots).
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Area;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
import javax.swing.border.LineBorder;
import com.sun.awt.AWTUtilities;
public class FlickerWindow extends JWindow implements MouseListener, MouseMotionListener{
JPanel controlPanel;
JPanel outlinePanel;
int mouseX, mouseY;
Rectangle windowRect;
Rectangle cutoutRect;
Area windowArea;
public static void main(String[] args) {
FlickerWindow fw = new FlickerWindow();
}
public FlickerWindow() {
super();
setLayout(new BorderLayout());
setBounds(500, 500, 200, 200);
setBackground(Color.GREEN);
controlPanel = new JPanel();
controlPanel.setBackground(Color.GRAY);
controlPanel.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
controlPanel.addMouseListener(this);
controlPanel.addMouseMotionListener(this);
outlinePanel = new JPanel();
outlinePanel.setBackground(Color.BLUE);
outlinePanel.setBorder(new CompoundBorder(new EmptyBorder(2,2,2,2), new LineBorder(Color.RED, 1)));
add(outlinePanel, BorderLayout.CENTER);
add(controlPanel, BorderLayout.NORTH);
add(new JButton("Dummy button"), BorderLayout.SOUTH);
setVisible(true);
setShape();
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
setShape();
}});
}
public void paint(Graphics g) {
//un-comment or breakpoint here to see window updates more clearly
//try {Thread.sleep(10);} catch (Exception e) {}
super.paint(g);
}
public void setShape() {
Rectangle bounds = getBounds();
Rectangle outlineBounds = outlinePanel.getBounds();
Area newShape = new Area (new Rectangle(0, 0, bounds.width, bounds.height));
newShape.subtract(new Area(new Rectangle(3, outlineBounds.y + 3, outlineBounds.width - 6, outlineBounds.height - 6)));
setSize(bounds.width, bounds.height);
AWTUtilities.setWindowShape(this, newShape);
}
public void mouseDragged(MouseEvent e) {
int dx = e.getXOnScreen() - mouseX;
int dy = e.getYOnScreen() - mouseY;
Rectangle newBounds = getBounds();
newBounds.translate(dx, dy);
newBounds.width -= dx;
newBounds.height -= dy;
mouseX = e.getXOnScreen();
mouseY = e.getYOnScreen();
setBounds(newBounds);
}
public void mousePressed(MouseEvent e) {
mouseX = e.getXOnScreen();
mouseY = e.getYOnScreen();
}
public void mouseMoved(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
Überschriebenen paint()
Methode kann verwendet werden, als ein Haltepunkt oder die Thread.sleep()
können auskommentiert werden, da, um einen klareren Blick auf das update, wie es geschieht.
Mein problem scheint stammen aus der setBounds()
Methode verursacht die Fenster gestrichen werden, um den Bildschirm, bevor Sie gelegt.
Fenster, bevor Sie die Größe ändern, wie es Aussehen sollte:
Fenster beim skalieren größer (oben und Links), gesehen am Haltepunkt am überschrieben paint()
- Methode):
Fenster beim skalieren kleiner (unten und rechts), gesehen am Haltepunkt am überschrieben paint()
- Methode):
Gewährt diese screenshots genommen werden, während aggressive ziehen mit der Maus Bewegungen, aber das flackern ist ziemlich ärgerlich, auch für weitere moderate Maus zieht.
Grünen Bereich auf die Größe zu größeren screenshot zeigt den neuen hintergrund, der gezogen wird, bevor alle Gemälde/das layout ist fertig, es scheint zu passieren, in der zugrunde liegenden ComponentPeer
oder native-window-manager. Den blauen Bereich auf "Größe ändern, um kleinere' screenshot zeigt die JPanel
's hintergrund gedrängt, in der Ansicht aber ist nun veraltet. Dies geschieht unter Linux(Ubuntu) und Windows XP.
Hat jemand einen Weg gefunden, um die Ursache für Window
oder JWindow
Größe ändern, um eine back-buffer, bevor änderungen vorgenommen werden, um den Bildschirm und vermeiden damit, dieses flimmern-Effekt? Gibt es vielleicht eine java.awt....
system-Eigenschaft, die gesetzt werden können, um dies zu vermeiden, konnte ich nicht finden, obwohl.
Edit #2: Kommentieren Sie den Aufruf von AWTUtilities.setWindowShape()
(und Optional kommentieren Sie die Thread.sleep(10)
Linie in paint()
), dann ziehen Sie den oberen Bedienfeld, um aggressiv um klar zu sehen, der Natur der flicker.
Edit #3: Ist jemand in der Lage zu testen, die dieses Verhalten unter Sun Java auf Windows 7 oder Mac OSX ?
- sehr schön formulierte Frage. Und an alle: bitte NICHT upvote keine Antwort ohne AUCH upvoting die Frage.
- Ich würde gerne wissen, die Antwort auf die gleiche Frage, wie gut - ich habe es gehasst für Jahre, aber nie gefunden, noch einen hacky Abhilfe. Getestet auf Windows 7 und obwohl ich nicht ganz die gleichen Effekte, wie Sie mir etwas sehr ähnliches. Größe Artefakte zu sehen sind, für jede Java-Anwendung, nicht nur in deinem Beispiel. Seltsamerweise ist das verschieben eines Fensters funktioniert gut, nur Größenänderung nervt (wahrscheinlich, weil Windows 7 Griffe verschieben von windows selbst und nicht-trigger-Größe in Java - im Gegensatz zu Windows XP, wenn ich mich richtig erinnere).
- danke für die Bestätigung, es passiert unter Windows 7 :-). Das verschieben der Fenster auf XP scheint glatt mit keine repaints. Vielleicht verweisen Sie auf Fenster 'Reparatur' auf XP, arm zu sein. Compositing-WMs (wie in Vista/Win7, OSX und Compiz etc auf Linux) Griff die "Reparatur" von zuvor verdeckt Teile von windows ohne Anforderung repaints, das ist, wo ich denke XP kann nach unten fallen.
- Ja, Sie sind richtig - ich bezog mich auf die Tatsache, dass Win7 caches aktuellen Fenster ein Bild, so dass, wenn EDT stecken, aus welchem Grund auch immer, können Sie immer noch bewegen Sie Ihre Fenster herum und Innenseiten sichtbar, anstatt graues Rechteck, sobald Sie das Fenster verschieben wie unter XP.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich gebe zu das ist keine besonders hilfreiche Antwort, aber verstehen, was genau die Schaukel kann helfen.
Sehen, Schwingen nicht alle Ihre Arbeit selbst, kurz, eigentlich immer ein wenig Platz zu ziehen, auf der OS. Alle Zeichnungen, widgets, etc sind Java-code. Es ist nicht so viel, dass dieser läuft zu langsam, als dass es läuft, ohne den Vorteil der 2D-Grafik-Karte, die Beschleunigung und die OS-rendering-tricks.
Erinnern DirectDraw? Alles ist es heutzutage, die Fenster und die ops sind butter-glätten. Aber wenn Sie jemals schnappen Sie sich einen computer, die nicht über es aus irgendeinem Grund (sagen, ein XP zu installieren, keine Treiber) Sie bemerken genau diese Art der Verlangsamung.
Mit Swing, weil es ihm gelingt alle einen eigenen Raum, das OS kann nicht jede dieser rendering-tricks, um Ihnen zu helfen.
Jemand kommen kann, zusammen mit einige Optimierungen, die Ihr problem behebt, die auf Ihrem computer, aber ich bin besorgt, dass es einfach nicht wirklich zu beheben, das Basis-Problem - Swing ist langsam und kann nicht schneller werden.
Sollten Sie schauen, in native toolkits. AWT ist OK, aber fehlt eine Menge widgets/etc. Es ist native und gebaut-in, so sollte es sein, ausreichend schnell, wenn das ist alles, was Sie brauchen. Ich bin teilweise zu SWT, das ist das, was Eclipse, Vuze (unter anderem) verwenden. Es vereint die native-ness der AWT mit der Benutzerfreundlichkeit und Funktionen von Swing und natürlich läuft überall.
BEARBEITEN: Es ist ziemlich klar, nach der Lektüre einige mehr von Ihren Kommentaren, dass Sie völlig verstehen, wie die Fenster passiert - ich will nicht kommen aus wie herablassend. Nicht nur das, sondern Sie sind mehr daran interessiert, die Größenänderung, das mein Kommentar nicht so viel zu tun. Ich würde noch empfehlen, SWT, weil es native code und schneller, aber das ist eine andere Antwort als die obige.
JWindow
. Insbesondere die nativen peerComponentPeer
auf und der entsprechende native code bietet keine Möglichkeit zu Fragen, für eine der untergeordneten Komponente zu malen, sich selbst in einen bereitgestellten Puffer, bevor alle updates, um das Gerät Bildschirm. Dies scheint daher ein Problem zu sein für alle top-level-AWT-und (schwere) Swing-Komponenten.Ich habe eben versucht dein Beispiel. Ich sah wirklich einige kleine schnippen, wenn die Größe des Fensters. Ich habe versucht ersetzen Sie das code-fragment ab
setBounds()
und finishing inAWTUtilities.setWindowShape(this, newShape);
durchund sah, dass der Finger verschwunden. Also, ich würde vorschlagen, Sie diese Lösung, es sei denn, Sie haben Besondere Gründe für die Verwendung
setBounds
.JWindow
's top-Links origin ändern muss) daher die Verwendung vonsetBounds()
.Ein weiterer Ansatz könnte sein, zu warten, bis der Benutzer auf Ihre komplette Drag/Größenänderung vor dem Lackieren. Vielleicht verwenden Sie einen Begrenzungsrahmen um dem Benutzer zu zeigen, die Größe des Fensters erst nach der resize-Ereignis abgeschlossen ist.
Habe ich bemerkt, dass sich viele im Fenstermodus Anwendungen wird entweder das Konzept oder Angebot mit dem flackern. Nur über alle apps, die ich habe versucht, einen aggressiven Größe wie dass mit hat das gleiche problem (non-Java), so kann das problem nur liegen mit dem Grafik-subsystem statt Swing selbst.