2010-12-01 40 views
24

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:

alt text


ventana durante el cambio de tamaño grande (hasta e izquierda) como se ve en el punto de ruptura en sobrescrito método paint()):

alt text


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()):

alt text


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?

+2

@willjcroz: +1, pregunta muy bien formulada. Y a todos: por favor, ** NO **, votaron por ninguna respuesta sin ** TAMBIÉN ** devolviendo la pregunta. – SyntaxT3rr0r

+1

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

+0

@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

Respuesta

1

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

setSize(newBounds.width, newBounds.height); 

y vi que el parpadeo desapareció. Por lo tanto, le sugiero esta solución a menos que tenga algún motivo especial para usar setBounds.

+0

Gracias por su aporte. Para obtener el comportamiento de cambio de tamaño correcto (este ejemplo representa el caso de uso de cambio de tamaño hacia arriba y hacia la izquierda), necesito mover la ventana y cambiar su tamaño (el origen superior izquierdo de 'JWindow' debe cambiar), de ahí el uso de 'setBounds()'. – willjcroz

+1

@AlexR: ok, pero muchas otras plataformas AWTUtilities.setWindowShape (this, newShape); no está funcionando solo intentado y falló. Fedora Linux/Ubuntu y OpenJDK. – YumYumYum

1

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.

He notado que muchas aplicaciones con ventanas tomarán ese enfoque o se ocuparán del 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.

+0

sí Actualmente estoy considerando el enfoque de cuadro delimitador, el único problema es que el usuario necesita precisión (seleccionando un área exacta de la pantalla) y, por lo tanto, tener el cambio de interfaz entre dos estados no es deseable. En cuanto a otras aplicaciones, ¿ha intentado cambiar el tamaño en Eclipse? A pesar de su diseño complejo, no tiene parpadeo para mí, sí espasmódico pero sin parpadeos. Esto me lleva a pensar que puedo tener más suerte aprendiendo y usando SWT. – willjcroz

+2

Sí, supongo que espasmódico/parpadeo son uno para mí. De todos modos, no es deseable ver ningún repintado. Estaba cambiando el tamaño de mi IDE mientras escribía mi comentario, así que supongo que estábamos en la misma página. Trabajo en algunas aplicaciones SWT/RCP en el trabajo, ya que proporciona un marco bastante bueno pero deja mucho que desear en términos de extensibilidad. El modelo de evento que usa tampoco es muy amigable para trabajar con ninguno de ellos. ¿Has intentado consultar la plataforma NetBeans? Creo que es un marco basado en Swing que podría valer la pena investigar. –

+0

PD: Estaba usando una máquina antigua para probar la tuya y tratar de arreglarla (núcleo único 2.0 con Windows XP). Todavía podía ver parpadeo/sacudidas cuando cambiaba el tamaño. –

2

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

See, Swing hace todo su trabajo, menos de obtener un poco de espacio para extraer del sistema operativo. 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.

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

Con Swing, porque gestiona todo su 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 de base: el oscilación 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.

EDIT: Después de leer un poco más de sus comentarios, es evidente que usted comprende perfectamente cómo ocurre el enventanado, 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.

+0

gracias por tus pensamientos, no te preocupes, no me sentí condescendiente;). Sí SWT parece una forma de evitar esto para algún proyecto futuro. Pero dado que este proyecto tiene un plazo inminente y el hecho de que este aspecto de UI se encuentra dentro de un applet que requiere tiempos de carga rápidos, SWT no ofrece una solución que no me ofrezca en este momento. – willjcroz

+0

también parece que el problema realmente existe dentro de la capa AWT subyacente a 'JWindow' de Swing. Específicamente, el par nativo, 'ComponentPeer', y su código nativo correspondiente no ofrecen ninguna manera de pedir que el componente secundario se dibuje en un búfer proporcionado antes de cualquier actualización en el dispositivo de pantalla. Por lo tanto, esto parece ser un problema para todos los componentes de alto nivel AWT y (peso pesado) Swing. – willjcroz

+0

+1 para sus sugerencias – willjcroz

Cuestiones relacionadas