que java swing resize flicker jwindow

java - que - ¿Cómo se puede redimensionar una Swing JWindow sin parpadear?



jframe (3)

Acabo de probar tu ejemplo. Realmente vi algunos chasquidos pequeños al cambiar el tamaño de la ventana. Intenté reemplazar el fragmento de código empezando por setBounds() y terminando en AWTUtilities.setWindowShape(this, newShape); por

setSize(newBounds.width, newBounds.height);

y vio que el chasquido desapareció. Entonces, te sugiero esta solución a menos que tengas algún motivo especial para usar setBounds .

Estoy tratando de crear una interfaz de usuario personalizada basada en una JWindow con el fin de seleccionar un área de la pantalla para compartir. He extendido JWindow y agregado un código para que se pueda redimensionar y para ''recortar'' el centro de la ventana usando AWTUtilities.setWindowShape() .

Cuando ejecuto el código, estoy experimentando un parpadeo a medida que la ventana cambia de tamaño en direcciones negativas xey, 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: se mejoró el código para dar forma a la ventana correctamente usando un ComponentListener y se agregó un componente ficticio en la parte inferior para ilustrar más el parpadeo (también capturas de pantalla 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 método paint() reemplazado se puede usar como un punto de interrupción o el Thread.sleep() se puede descomentar allí para proporcionar una vista más clara de la actualización a medida que ocurre.

Mi problema parece provenir del método setBounds() que hace que la ventana se setBounds() en la pantalla antes de ser distribuida.

Ventana antes de cambiar el tamaño, como debería verse:

Ventana durante el cambio de tamaño más grande (hacia arriba y hacia la izquierda) como se ve en el punto de interrupción en el método de paint() anulada paint() ):

Ventana durante el cambio de tamaño más pequeño (hacia abajo y hacia la derecha) como se ve en el punto de corte en el método de paint() anulada paint() ):

De acuerdo, estas capturas de pantalla se toman durante movimientos de arrastre agresivos del mouse, pero el parpadeo se vuelve bastante molesto incluso para arrastres de mouse más moderados.

El área verde en el cambio de tamaño a una 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 subyacente o en el administrador de ventanas nativo. El área azul en la captura de pantalla "redimensionar a menor" muestra el JPanel del JPanel está visualizando, pero ahora está desactualizado. Esto sucede bajo Linux (Ubuntu) y Windows XP.

¿Alguien ha encontrado la forma de hacer que una Window o JWindow su tamaño a un buffer posterior antes de que se realicen cambios en la pantalla y así evitar este efecto de parpadeo? Tal vez haya una propiedad del sistema java.awt.... que se pueda configurar para evitar esto, aunque no pude encontrar ninguno.

Editar # 2: Comente la llamada a AWTUtilities.setWindowShape() (y opcionalmente Thread.sleep(10) el comentario de la línea Thread.sleep(10) en paint() ) luego arrastre el panel superior agresivamente para ver claramente la naturaleza del parpadeo.

Edición n. ° 3: ¿Alguien puede probar este comportamiento bajo Sun Java en Windows 7 o Mac OSX?


Admito que esta no es una respuesta particularmente útil, pero entender qué es exactamente Swing puede ayudar.

Mira, Swing hace todo su trabajo, excepto el hecho de obtener un poco de espacio del SO. Todos los dibujos, widgets, etc. son códigos Java. No es tanto que esto se ejecute lentamente, sino que se ejecuta sin el beneficio de la aceleración de la tarjeta gráfica 2D y los trucos de representación del sistema operativo.

¿Recuerdas DirectDraw? Todo lo tiene hoy en día, y las ventanas son suaves como la mantequilla. Pero si alguna vez toma una computadora que no la tiene por alguna razón (por ejemplo, una instalación de XP sin controladores), observa exactamente este tipo de desaceleración.

Con Swing, porque maneja todo su propio espacio, el sistema operativo no puede hacer ninguno de estos trucos de renderizado para ayudarte.

Alguien puede venir con una optimización que corrige su problema en su computadora, pero me preocupa que no solucione realmente el problema básico: Swing es lento y no puede hacerse más rápido.

Debería buscar kits de herramientas nativos. AWT está bien, pero falta una gran cantidad de widgets / etc. Es nativo e incorporado, por lo que debería ser bastante rápido si eso es todo lo que necesitas. Soy parcial a SWT, que es lo que usan Eclipse, Vuze (entre otros). Combina la natividad de AWT con la facilidad y las características de Swing y, por supuesto, se ejecuta en todas partes.

EDITAR : Está bastante claro después de leer algunos más de tus comentarios que entiendes absolutamente cómo ocurre el efecto de ventana: no quiero parecer condescendiente. No solo eso, sino que estás más interesado en redimensionar, que mi comentario no tiene mucho que ver con eso. Todavía recomendaría SWT porque es un código nativo y más rápido, pero esa es una respuesta diferente a la anterior.


Otro enfoque podría ser esperar a que el usuario complete su arrastre / cambio de tamaño antes de pintar. Tal vez use un cuadro delimitador para mostrarle al usuario el tamaño de la ventana hasta que se complete su evento de cambio de tamaño.

Me he dado cuenta de que muchas aplicaciones con ventanas tomarán ese enfoque o lidiarán con el parpadeo. Prácticamente todas las aplicaciones en las que he probado un cambio de tamaño agresivo tienen el mismo problema (no Java), por lo que el problema puede recaer solo en el subsistema gráfico en lugar de Swing.