Estoy tratando de hacer una interfaz de usuario personalizada basada en un JWindow
con el fin de seleccionar un área de la pantalla para compartir. He extendido JWindow
y he agregado un código para hacerlo redimensionable y para 'cortar' el centro de la ventana usando AWTUtilities.setWindowShape()
.¿Cómo se puede cambiar el tamaño de una ventana J Swing sin parpadeo?
Al ejecutar el código, estoy experimentando un parpadeo a medida que la ventana cambia de tamaño en direcciones x e y negativas, es decir, hacia arriba y hacia la izquierda. Lo que parece estar sucediendo es que la ventana se redimensiona y dibuja antes de que se actualicen los componentes. A continuación hay una versión simplificada del código. Cuando se ejecuta, el panel superior se puede usar para cambiar el tamaño de la ventana hacia arriba y hacia la izquierda. El fondo de la ventana está configurado en verde para que quede claro dónde están los píxeles que no quiero mostrar.
Editar: mejorado el código para dar forma a la ventana correctamente usando un ComponentListener
y se añadió un componente maniquí en la parte inferior para ilustrar adicionalmente parpadeo (capturas de pantalla también actualizadas).
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) {}
}
El anulado paint()
método puede ser utilizado como un punto de interrupción o el Thread.sleep()
puede ser uncommented allí para proporcionar una visión más clara de la actualización como sucede.
Mi problema parece provenir del método setBounds()
que hace que la ventana se pinte en la pantalla antes de ser distribuida.
ventana antes de cambiar el tamaño, ya que debe buscar:
ventana durante el cambio de tamaño grande (hasta e izquierda) como se ve en el punto de ruptura en sobrescrito método paint()
):
ventana durante el cambio de tamaño más pequeño (abajo ya la derecha) como se ve en el punto de ruptura en sobrescrito método paint()
):
concedieron estas imágenes se toman durante los movimientos de arrastre de ratón agresivas pero el parpadeo se vuelve bastante molesto incluso para roces de ratón más moderados.
El área verde en el cambio de tamaño a la captura de pantalla más grande muestra el nuevo fondo que se dibuja antes de realizar cualquier pintura/diseño, parece suceder en el ComponentPeer
o gestor de ventanas nativo subyacente. El área azul en la captura de pantalla "redimensionar a más pequeño" muestra el fondo JPanel
que se muestra a la vista, pero ahora está desactualizado. Esto sucede bajo Linux (Ubuntu) y Windows XP.
Alguien ha encontrado una manera de causar una Window
o JWindow
para cambiar el tamaño de una memoria intermedia de vuelta antes de que se realicen cambios a la pantalla y así evitar este efecto de parpadeo? Tal vez haya una propiedad del sistema java.awt....
que se puede configurar para evitar esto, aunque no pude encontrar ninguno.
editar # 2: comentario la llamada a AWTUtilities.setWindowShape()
(y opcionalmente elimine la línea Thread.sleep(10)
en paint()
) a continuación, arrastre el panel superior alrededor de agresivamente con el fin de ver claramente la naturaleza de la parpadeo.
Edit # 3: ¿Alguien puede probar este comportamiento bajo Sun Java en Windows 7 o Mac OSX?
@willjcroz: +1, pregunta muy bien formulada. Y a todos: por favor, ** NO **, votaron por ninguna respuesta sin ** TAMBIÉN ** devolviendo la pregunta. – SyntaxT3rr0r
También me gustaría saber la respuesta a la misma pregunta: lo he odiado durante años, pero nunca encontré una solución alternativa. Probado en Windows 7 y aunque no obtengo los mismos efectos que yo obtengo algo muy similar. Los artefactos de tamaño se ven para cada aplicación de Java, no solo su ejemplo. Curiosamente, mover una ventana funciona bien, solo cambiar el tamaño de succión (probablemente porque Windows 7 maneja las ventanas en movimiento y no provoca el cambio de tamaño en Java, a diferencia de Windows XP, si mal no recuerdo). – Domchi
@Domchi: gracias por la confirmación de que está sucediendo en Windows 7 :-). Mover la ventana en XP parece sin problemas y sin repintes. Tal vez te estás refiriendo a la ventana de 'reparación' en XP siendo pobre. La composición de WM (como se ve en Vista/Win7, OSX y Compiz, etc. en Linux) maneja la 'reparación' de partes previamente oscurecidas de Windows sin solicitar repintados, aquí es donde creo que XP puede caerse. – willjcroz