Est-ce que quelqu'un a déjà essayé d'utiliser Swing pour construire un environnement de rendu multi-tampon approprié sur lequel des éléments d'interface utilisateur Swing peuvent être ajoutés?JTextFields au-dessus du dessin actif sur JPanel, problèmes de thread
Dans ce cas, j'ai un rectangle rouge d'animation dessiné sur un arrière-plan. L'arrière-plan n'a pas besoin d'être mis à jour à chaque image, donc je le rends sur une image tamponnée et je redessine seulement la partie nécessaire pour effacer l'emplacement précédent du rectangle. Voir le code complet ci-dessous, cela prolonge l'exemple donné par @trashgod dans un thread précédent, here.
Jusqu'ici tout va bien; animation fluide, faible utilisation du processeur, pas de scintillement.
Puis j'ajoute un JTextField au Jpanel (en cliquant sur n'importe quelle position sur l'écran), et je me concentre dessus en cliquant dans la zone de texte. Effacer l'emplacement précédent du rectangle échoue maintenant à chaque clignotement du curseur, voir l'image ci-dessous. Je suis curieux si quelqu'un a une idée de la raison pour laquelle cela pourrait arriver (Swing n'étant pas thread-safe? L'image étant peinte de façon asynchrone?) Et dans quelle direction chercher des solutions possibles.
C'est sous 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
}
}
Merci pour votre réponse. Si vous ajoutez super.paintComponent (g) et exécutez l'application de test fournie, vous verrez que ce n'est malheureusement pas une solution. La méthode paintComponent par défaut de JPanel efface son arrière-plan, ce qui oblige à redessiner l'image d'arrière-plan en entier, ce qui annule le but du pré-rendu de l'image d'arrière-plan. J'ai l'impression que redessiner seulement la zone nécessaire devrait être possible, mais comme il semble, il y a un problème avec redessiner le champ de texte dans un fil différent de redessiner l'image de fond qui provoque ces artefacts de dessin .. – Mattijs
En ce qui concerne l'addenda: Merci beaucoup d'avoir pris le temps de plonger dans ce domaine. Je réalise que votre point, étant qu'il n'y a aucun moyen que le JTextFields automatiquement redessiner fonctionnera correctement avec le redessin actif * sans * en utilisant super.paintComponent (g), est correct. Néanmoins, l'utilisation de super.paintComponent (g) implique que la fenêtre entière doit être redessinée sur chaque image d'animation, ce qui rend l'utilisation du processeur dépendante de la taille de la fenêtre, ce que je voudrais éviter. Donc ma question est répondue, mais mon problème est toujours debout. Je vais faire un nouveau post à ce sujet. – Mattijs
@Mattijs: L'utilisation de setOpaque (false) permet d'éviter le remplissage. J'ai mis à jour l'exemple pour montrer le temps de peinture. La différence est considérable, mais elle doit être mise en balance avec l'effort de gestion des mises à jour. – trashgod