Ich versuche, eine benutzerdefinierte UI basierend auf einem JWindow
für den Zweck der Auswahl eines Bereichs des Bildschirms zur gemeinsamen Nutzung zu machen. Ich habe JWindow
erweitert und Code hinzugefügt, um es in der Größe zu ändern und die Mitte des Fensters unter Verwendung AWTUtilities.setWindowShape()
'auszuschneiden'.Wie kann eine Swing JWindow ohne Flimmern skaliert werden?
Wenn ich den Code ausführe, erfahre ich ein Flimmern, da das Fenster in negativer x- und y-Richtung geändert wird, d. H. Oben und links. Offensichtlich wird das Fenster skaliert und gezeichnet, bevor die Komponenten aktualisiert werden. Im Folgenden finden Sie eine vereinfachte Version des Codes. Wenn sie ausgeführt wird, kann das obere Fenster verwendet werden, um das Fenster nach oben und nach links zu vergrößern. Der Hintergrund des Fensters ist grün, um zu verdeutlichen, wo die Pixel sind, die ich nicht anzeigen möchte.
Edit: den Code Verbesserte das Fenster richtig ein ComponentListener
Verwendung zu formen und hinzugefügt unten eine Dummy-Komponente weiter zu flackern (ebenfalls aktualisiert Screenshots) veranschaulicht.
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) {}
}
Die überschriebene paint()
Verfahren kann als Haltepunkt verwendet werden, oder der es Thread.sleep()
uncommented sein kann, eine bessere Sicht auf die Aktualisierung zur Verfügung zu stellen, wie es geschieht.
Mein Problem scheint von der setBounds()
Methode zu stammen, die bewirkt, dass das Fenster auf den Bildschirm gemalt wird, bevor es ausgelegt wird.
Fenster vor Ändern der Größe, wie es aussehen soll:
Fenster während größerer Ändern der Größe (nach oben und links) zum Haltepunkt am überschriebene paint()
Methode gesehen):
Fenster während kleinere Größenänderung (unten und rechts), wie bei überschriebene paint()
Verfahren bei Unterbrechungspunkt aus gesehen):
Zugegeben Diese Screenshots während aggressive Maus ziehen Bewegungen aufgenommen werden, aber das Flimmern wird ziemlich nervig auch für gemäßigtere Maus-Drags.
Der grüne Bereich auf der Größe zu größer Bildschirmabbildung zeigt den neuen Hintergrund, der gezeichnet wird, bevor ein Bild/Layout gemacht wird, scheint es in der zugrunde liegenden ComponentPeer
oder native Window Manager passieren. Der blaue Bereich auf dem Bildschirm "Größe verkleinern zu" zeigt an, dass der Hintergrund von JPanel
in den Blickpunkt gerückt ist, aber nicht mehr aktuell ist. Dies geschieht unter Linux (Ubuntu) und Windows XP.
Hat jemand einen Weg gefunden, um eine Window
oder JWindow
Größe in einen Backbuffer zu bringen, bevor irgendwelche Änderungen am Bildschirm vorgenommen werden und so diesen Flimmereffekt vermeiden? Vielleicht gibt es eine java.awt....
Systemeigenschaft, die eingestellt werden kann, um dies zu vermeiden, konnte ich jedoch nicht finden.
Edit # 2: Kommentieren Sie den Aufruf an AWTUtilities.setWindowShape()
(und optional die Thread.sleep(10)
Linie in paint()
Kommentar-), dann die obere Abdeckung zieht um aggressiv, um klar zu der Art des Flimmerns zu sehen.
Bearbeiten # 3: Kann jemand dieses Verhalten unter Sun Java unter Windows 7 oder Mac OSX testen?
@willjcroz: +1, sehr schön formulierte Frage. Und an alle: bitte ** ** ** ** keine Antwort geben ohne ** AUCH ** die Frage zu beantworten. – SyntaxT3rr0r
Ich würde gerne die Antwort auf die gleiche Frage wissen - ich habe es seit Jahren gehasst, aber noch nie eine hacky Workaround gefunden. Getestet auf Windows 7 und obwohl ich nicht die gleichen Effekte wie du bekomme bekomme ich etwas sehr ähnliches. Größenänderungsartefakte werden für jede Java-Anwendung angezeigt, nicht nur für Ihr Beispiel. Seltsamerweise funktioniert das Verschieben eines Fensters in Ordnung, nur das Ändern der Größe ist nervtötend (wahrscheinlich, weil Windows 7 das Verschieben von Fenstern selbst übernimmt und nicht die Größenänderung in Java auslöst - im Gegensatz zu Windows XP, wenn ich mich richtig erinnere). – Domchi
@Domchi: danke für die Bestätigung, dass es unter Windows 7 passiert :-). Das Verschieben des Fensters auf XP scheint glatt ohne Neuanstriche. Vielleicht beziehen Sie sich auf Fenster "Reparatur" auf XP ist schlecht. Compositing WMs (wie in Vista/Win7, OSX und Compiz etc unter Linux) behandeln die "Reparatur" von zuvor verdeckten Teilen von Windows, ohne dass sie Repaints anfordern, hier könnte XP vielleicht herunterfallen. – willjcroz