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:

Wie kann ein Swing JWindow in der Größe verändert werden, ohne zu flackern?


Fenster beim skalieren größer (oben und Links), gesehen am Haltepunkt am überschrieben paint() - Methode):

Wie kann ein Swing JWindow in der Größe verändert werden, ohne zu flackern?


Fenster beim skalieren kleiner (unten und rechts), gesehen am Haltepunkt am überschrieben paint() - Methode):

Wie kann ein Swing JWindow in der Größe verändert werden, ohne zu flackern?


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.
InformationsquelleAutor willjcroz | 2010-12-01
Schreibe einen Kommentar