Note that there are some explanatory texts on larger screens.

plurals
  1. POHow can a Swing JWindow be resized without flickering?
    primarykey
    data
    text
    <p>I am trying to make a custom UI based on a <code>JWindow</code> for the purpose of selecting an area of the screen to be shared. I have extended <code>JWindow</code> and added code to make it resizable and to 'cut out' the centre of the window using <code>AWTUtilities.setWindowShape()</code>.</p> <p>When running the code I am experiencing a flicker as the window is resized in negative x and y directions, i.e. up and left. What appears to be happening is that the window is resized and drawn before the components are updated. Below is a simplified version of the code. When run the top panel can be used to resize the window up and to the left. The background of the window is set to green to make it clear where the pixels I do not want showing are.</p> <p><strong>Edit:</strong> Improved the code to shape the window correctly using a <code>ComponentListener</code> and added a dummy component at bottom to further illustrate flicker (also updated screenshots).</p> <pre><code>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) {} } </code></pre> <p>The overridden <code>paint()</code> method can be used as a breakpoint or the <code>Thread.sleep()</code> can be uncommented there to provide a clearer view of the update as it happens.</p> <p>My problem seems to stem from the <code>setBounds()</code> method causing the window to be painted to the screen before being laid out. </p> <hr> <p>Window before resizing, as it should look:</p> <p><img src="https://i.stack.imgur.com/Pw0Gi.png" alt="alt text"> </p> <hr> <p>Window during resizing larger (up and left) as seen at breakpoint at overridden <code>paint()</code> method):</p> <p><img src="https://i.stack.imgur.com/1OhHl.png" alt="alt text"> </p> <hr> <p>Window during resizing smaller (down and right) as seen at breakpoint at overridden <code>paint()</code> method):</p> <p><img src="https://i.stack.imgur.com/VYFNd.png" alt="alt text"> </p> <hr> <p>Granted these screenshots are taken during aggressive mouse drag movements but the flicker becomes quite annoying even for more moderate mouse drags. </p> <p>The green area on the resize to larger screenshot shows the new background that gets drawn before any painting/layout is done, it seems to happen in the underlying <code>ComponentPeer</code> or native window manager. The blue area on the 'resize to smaller' screenshot shows the <code>JPanel</code>'s background being pushed into view but is now out of date. This happens under Linux(Ubuntu) and Windows XP.</p> <p>Has anyone found a way to cause a <code>Window</code> or <code>JWindow</code> to resize to a back buffer before any changes are made to the screen and thus avoid this flickering effect? Maybe there is a <code>java.awt....</code> system property that can be set to avoid this, I could not find one though.</p> <hr> <p><strong>Edit #2:</strong> Comment out the call to <code>AWTUtilities.setWindowShape()</code> (and optionally uncomment the <code>Thread.sleep(10)</code> line in <code>paint()</code>) then drag the top panel around aggressively in order to clearly see the nature of the flicker.</p> <p><strong>Edit #3:</strong> Is anyone able to test this behaviour under Sun Java on Windows 7 or Mac OSX ? </p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload