¿Alguien ha intentado alguna vez utilizar Swing para construir un entorno de representación de almacenamiento múltiple adecuado sobre el cual se pueden agregar elementos de la interfaz de usuario de Swing?JTextFields en la parte superior del dibujo activo en JPanel, problemas de subprocesamiento
En este caso, tengo un rectángulo rojo animado dibujado sobre un fondo. El fondo no necesita actualizarse en cada cuadro, por lo que lo renderizo en una Imagen Buffered y redibujo solo la parte necesaria para borrar la ubicación anterior del rectángulo. Vea el código completo a continuación, esto extiende el ejemplo dado por @trashgod en un hilo anterior, here.
Hasta ahora todo bien; animación suave, bajo uso de la CPU, sin parpadeos.
Luego agrego un JTextField al Jpanel (haciendo clic en cualquier posición en la pantalla), y lo enfoco haciendo clic dentro del cuadro de texto. La eliminación de la ubicación anterior del rectángulo ahora falla en cada parpadeo del cursor, vea la imagen a continuación.
Tengo curiosidad por si alguien tiene una idea de por qué esto podría suceder (Swing no es seguro para subprocesos? ¿La imagen se pinta de forma asíncrona?) Y en qué dirección buscar posibles soluciones.
Esto es en Mac OS 10.5, Java 1,6
JPanel redraw fail http://www.arttech.nl/javaredrawerror.png
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
public class NewTest extends JPanel implements
MouseListener,
ActionListener,
ComponentListener,
Runnable
{
JFrame f;
Insets insets;
private Timer t = new Timer(20, this);
BufferedImage buffer1;
boolean repaintBuffer1 = true;
int initWidth = 640;
int initHeight = 480;
Rectangle rect;
public static void main(String[] args) {
EventQueue.invokeLater(new NewTest());
}
@Override
public void run() {
f = new JFrame("NewTest");
f.addComponentListener(this);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
createBuffers();
insets = f.getInsets();
t.start();
}
public NewTest() {
super(true);
this.setPreferredSize(new Dimension(initWidth, initHeight));
this.setLayout(null);
this.addMouseListener(this);
}
void createBuffers() {
int width = this.getWidth();
int height = this.getHeight();
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
buffer1 = gc.createCompatibleImage(width, height, Transparency.OPAQUE);
repaintBuffer1 = true;
}
@Override
protected void paintComponent(Graphics g) {
int width = this.getWidth();
int height = this.getHeight();
if (repaintBuffer1) {
Graphics g1 = buffer1.getGraphics();
g1.clearRect(0, 0, width, height);
g1.setColor(Color.green);
g1.drawRect(0, 0, width - 1, height - 1);
g.drawImage(buffer1, 0, 0, null);
repaintBuffer1 = false;
}
double time = 2* Math.PI * (System.currentTimeMillis() % 5000)/5000.;
g.setColor(Color.RED);
if (rect != null) {
g.drawImage(buffer1, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, this);
}
rect = new Rectangle((int)(Math.sin(time) * width/3 + width/2 - 20), (int)(Math.cos(time) * height/3 + height/2) - 20, 40, 40);
g.fillRect(rect.x, rect.y, rect.width, rect.height);
}
@Override
public void actionPerformed(ActionEvent e) {
this.repaint();
}
@Override
public void componentHidden(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void componentMoved(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void componentResized(ComponentEvent e) {
int width = e.getComponent().getWidth() - (insets.left + insets.right);
int height = e.getComponent().getHeight() - (insets.top + insets.bottom);
this.setSize(width, height);
createBuffers();
}
@Override
public void componentShown(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseClicked(MouseEvent e) {
JTextField field = new JTextField("test");
field.setBounds(new Rectangle(e.getX(), e.getY(), 100, 20));
this.add(field);
repaintBuffer1 = true;
}
@Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
Gracias por su respuesta. Si agrega super.paintComponent (g) y ejecuta la aplicación de prueba que proporcioné, verá que, lamentablemente, esta no es una solución. El método paintComponent predeterminado de JPanel borra su fondo, lo que hace que sea necesario volver a dibujar toda la imagen de fondo, en su mayoría para evitar el preprocesamiento de la imagen de fondo. Tengo la corazonada de que volver a dibujar solo el área necesaria debería ser posible, pero como parece, hay un problema con volver a dibujar el campo de texto en un hilo diferente que volver a dibujar la imagen de fondo que causa estos artefactos de dibujo .. – Mattijs
Acerca del apéndice: muchas gracias de nuevo por tomarse el tiempo para sumergirse en esto. Me doy cuenta de eso, ya que no hay forma de que el rediseño automático de JTextFields funcione correctamente junto con el redibujado activo * sin * usando super.paintComponent (g), es correcto. Sin embargo, el uso de super.paintComponent (g) implica que toda la ventana tiene que volver a dibujarse en cada cuadro de animación, lo que hace que el uso de la CPU dependa del tamaño de la ventana, que es lo que me gustaría evitar. Entonces mi pregunta es respondida, pero mi problema sigue en pie. Haré una nueva publicación sobre esto. – Mattijs
@Mattijs: El uso de setOpaque (falso) evitará el relleno. Actualicé el ejemplo para mostrar el tiempo de pintura. La diferencia es considerable, pero debe sopesarse contra el esfuerzo de administrar las actualizaciones. – trashgod