2009-05-18 36 views
5

he hecho un componente encargo (derivada de JComponent) que representa un arrastrable Bezier curva.
(se parece a un cable que cuelga, alguien podría saber que de Bender o Cubase)¿Cómo puedo repintar de manera eficiente cuando uso un gran componente personalizado en Swing?

Mi problema es: La curva puede convertirse muy largo, digamos desde la parte superior izquierda a esquinas inferior derecha de la escritorio.

Esto hace que la oscilación repinte funcionalidad ineficiente: El área de la curva es quizás unos cientos de píxeles, pero el área de el componente (siendo en su mayoría 'transparente') es millones de píxeles grandes.

Mi impresión es sometimiento:
Cuanto más larga sea la curva, más de parpadeo cuando me sale arrastrándolo.

Espero haber aclarado el problema.

Quizás ayudaría cuando alguna manera pudiera elegir por mí mismo, qué regiones del componente necesita volver a pintar en absoluto.

EDIT:
Tal lío! Estoy perfilando la aplicación usando Netbeans, que ayuda a a encontrar el código ineficiente normalmente, pero este marco Swing está haciendo cientos de llamadas anidadas. Simplemente no puedo entender, qué es lento y por qué.
Por cierto, deshabilitar super.paint(...) o super.paintComponent(...) no ayuda.

+1

Lo negrita extraña que haya que hacer allí. – Malfist

+0

¿Puedes publicar tu método paintComponent? Esto podría ayudarnos a encontrar problemas concretos y sugerir buenas soluciones. –

Respuesta

1

Para volver a dibujar una porción más pequeña de la pantalla utilizando repaint(Rectangle r)

http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JComponent.html#repaint(java.awt.Rectangle)

A continuación, menciona el parpadeo. Como usa swing, que usa doble buffer, su parpadeo debe provenir de otra cosa. ¿Está borrando la pantalla en paintComponent(...)? Es decir. llamar al fillRect(...)? No hagas eso, no es necesario (IIRC).

+0

Gracias, intentaré esto. No, no estoy despejando nada solo, no tengo idea de dónde viene el parpadeo. Evitar –

2

No hay manera eficaz de crear un montón de pequeños rectángulos de clip para una estructura diagonal que le deja con dos estrategias para evitar el parpadeo:

  • Doblebuffering. Esto requiere una gran cantidad de memoria, pero la copia de la memoria es muy rápida (generalmente ocurre en el momento en que el "haz de electrones" retrocede desde la esquina inferior derecha a la esquina superior izquierda ... si todavía hay una barra en la LCD).

  • No llame a super.paint() (que draws or "erases" the background) y dibuje la curva una segunda vez con el color de fondo para borrarla.

Para obtener más información, consulte this document.

[EDITAR] Si fillRect() no era abstracto, puede establecer un punto de ruptura :) Establezca un punto de interrupción en paint(), verifique quién lo llama y si el fondo se borró en ese momento. Debería ser así ya que la representación sería completamente incorrecta. Luego, configure los puntos de quiebre más arriba en la cadena de llamadas.

+0

super.paint llamando al (...) Es una buena idea –

+0

No, no ayuda :( –

+0

No se olvide para anular la actualización(). El documento he mencionado también contiene mucha información de cómo mejorar la velocidad de renderizado. –

4

Consulte Filthy Rich Clients por Chet Haase y Romain Guy. Abordan estas mismas optimizaciones, entre otras, a lo largo del camino para producir una interfaz de usuario gráfica e impresionante.

1

¿Qué método usas para pintar tu curva? pintar o pintarComponente?

+0

es paintComponent, pero el dolor da mismos resultados;. Por cierto, no sé quién es usted downvoted, fue no me –

+0

yo no era yo, sino alguien pelos división sería decir que se supone que downvote cuándo y respuesta " no es útil "(revise el texto alternativo). Diría que esta respuesta pertenece como un comentario, y si el usuario tuviera suficiente representante para dejar un comentario, lo habría rechazado. Pero no lo hacen, así que no lo hice. . – Malfist

+0

no puedo añadir comentarios, así que ... –

3

Hacer todas tus matemáticas más bonitas en el hilo de la pintura cada vez que se actualiza el componente es (como habrás deducido) una mala idea. ¿Tu curva cambia a menudo? Si no es así, ¿por qué no pintarlo en una Imagen Buffered cuando cambia y cambiar el código de pintura() simplemente para dibujar la imagen almacenada en el componente.

class CurveComponent extends JComponent { 
    private BufferedImage image; 

    @Override 
    public void paintComponent(Graphics g) { 
     if (image == null) { 
      return; 
     } 
     g.drawImage(image, 0, 0, this); 
    } 

    private void updateCurve() { 
     image = new BufferedImage(getWidth(), getHeight(), BufferedImage.ARGB); 
     Graphics g = image.getGraphics(); 
     // draw the curve onto image using g. 
     g.dispose(); 
    } 
} 

única llamada updateCurve() cuando es necesario y todo lo que las matemáticas caros no se repetirán innecesariamente. La pintura debe ser muy receptiva, incluso para una ventana de pantalla completa. drawImage() hará una copia de memoria directa y debería ser muy rápida.

+0

En realidad, no hago nada: las matemáticas bezier se hacen por Java CubicCurve2D;) –

+0

Y la curva cambia todo el tiempo, nunca se arrastra por entero, pero siempre arrastrando uno de los dos puntos finales. –

3

Intenta escribir una pequeña aplicación de prueba, que consiste en nada más que lo que necesitas para reproducir este problema. Esto facilitará la creación de perfiles. Luego publique esa aplicación aquí, para que podamos ver las posibles soluciones.

Encontré su pregunta interesante, así que escribí una aplicación de prueba. Esto dibuja una curva de Bezier que se cambia de tamaño continuamente a medida que arrastra. Creé un fondo degradado para asegurarme de que funciona bien con un fondo desagradable. Obtuve un buen rendimiento y un bajo parpadeo, aunque utilizo una máquina de primera categoría.

Vale la pena leer "Filthy Rich Clients" para aprender todos los trucos de escribir componentes Swing personalizados que funcionan muy bien.

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import java.awt.geom.CubicCurve2D; 
import java.awt.geom.Point2D; 

public class CustomComponent extends JComponent { 

    private Point2D start = new Point2D.Double(0, 0); 
    private Point2D end = new Point2D.Double(300, 200); 

    private CustomComponent() { 
     this.setOpaque(true); 
     final MouseAdapter mouseAdapter = new MouseAdapter() { 

      @Override 
      public void mouseDragged(MouseEvent e) { 
        setEnd(e.getPoint()); 
      } 

     }; 
     this.addMouseListener(mouseAdapter); 
     this.addMouseMotionListener(mouseAdapter); 
    } 

    public void setStart(Point2D start) { 
     this.start = start; 
     repaint(); 
    } 

    public void setEnd(Point2D end) { 
     this.end = end; 
     repaint(); 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 

     final Graphics2D g2 = (Graphics2D) g; 
     g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 

     // draw gradient background 
     final int width = getWidth(); 
     final int height = getHeight(); 
     g2.setPaint(new GradientPaint(0, 0, Color.WHITE, width, height, Color.YELLOW)); 
     g2.fillRect(0, 0, width, height); 

     // draw Bezier curve 
     final Shape shape = new CubicCurve2D.Double(start.getX(), start.getY(), start.getX(), end.getY(), end.getX(), start.getY(), end.getX(), end.getY()); 
     g2.setColor(Color.BLACK); 
     g2.draw(shape); 

     g2.drawString("Click and drag to test for flickering", 100, 20); 
    } 

    public static void main(String[] args) { 
     final CustomComponent component = new CustomComponent(); 
     final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 
     final Dimension size = new Dimension(screenSize.width - 20, screenSize.height - 100); 
     component.setPreferredSize(size); 

     final JFrame frame = new JFrame(); 
     frame.add(component); 
     frame.pack(); 
     frame.setLocationRelativeTo(null); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setVisible(true); 
    } 

} 

Algunas cosas para tomar nota:

  • única sobrescribir paintComponent (Graphics g), no los otros métodos paintXXX()
  • componente personalizado conjunto a opaco si es posible
  • sólo uso repinte () para solicitar el repintado. Nunca ordene directamente un repintado directamente en su código. Esto le permite a Swing manejarlo bien.
0

Mi solución fue un parcial rediseño:

Ahora no representan cada uno -elemento "cable" de un componente.
Ahora, los cables son solo dummy objetos (sin JComponent involucrado).
El repintado tiene lugar "globalmente", en el panel de contenido del JFrame padre.

Ahora es eficiente y parpadea menos.

0

sólo tiene que utilizar getVisibleRect(); dentro paintComponent(Graphics g) para obtener el área que realmente se necesita para volver a trazar

Cuestiones relacionadas