Hat jemand jemals versucht, Swing zu verwenden, um eine richtige Multi-gepufferte Rendering-Umgebung zu erstellen, auf der Swing-Benutzeroberflächenelemente hinzugefügt werden können?JTextFields über aktive Zeichnung auf JPanel, Threading-Probleme
In diesem Fall habe ich ein animiertes rotes Rechteck auf einen Hintergrund gezeichnet. Der Hintergrund muss nicht für jeden Frame aktualisiert werden, daher rende ich ihn auf ein BufferedImage und zeichne nur den Teil neu, der zum Löschen der vorherigen Position des Rechtecks erforderlich ist. Sehen Sie sich den vollständigen Code unten an. Dies erweitert das Beispiel von @trashgod in einem früheren Thread, here.
So weit so gut; glatte Animation, geringe CPU-Auslastung, kein Flackern.
Dann füge ich ein JTextField zum Jpanel hinzu (indem ich auf irgendeine Position auf dem Bildschirm klicke), und konzentriere mich darauf, indem ich in das Textfeld klicke. Das Löschen der vorherigen Position des Rechtecks schlägt bei jedem blinkenden Cursor fehl, siehe Abbildung unten.
Ich bin neugierig, wenn jemand eine Idee davon hat, warum dies passieren könnte (Swing nicht thread-safe? Das Bild wird asynchron gemalt?) Und in welche Richtung nach möglichen Lösungen zu suchen.
Dies ist auf 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
}
}
Vielen Dank für Ihre Antwort. Wenn Sie super.paintComponent (g) hinzufügen und die von mir bereitgestellte Testanwendung ausführen, werden Sie feststellen, dass dies leider keine Lösung ist. Die standardmäßige paintComponent-Methode von JPanel löscht ihren Hintergrund, was es notwendig macht, das gesamte Hintergrundbild neu zu zeichnen, wobei der Zweck des Vorrenderns des Hintergrundbilds größtenteils zunichte gemacht wird. Ich habe eine Ahnung, dass das Neuzeichnen nur der notwendigen Bereich möglich sein sollte, aber wie es scheint, gibt es ein Problem mit dem Neuzeichnen des Textfelds in einem anderen Thread als Neuzeichnen des Hintergrundbildes, das diese Zeichnungsartefakte verursacht .. – Mattijs
In Bezug auf das Addendum: vielen dank nochmals, dass du dir die zeit genommen hast, in diese einzutauchen. Ich verstehe, dass Ihr Punkt, dass es keine Möglichkeit gibt, dass die automatisch neu gezeichneten JTextFields korrekt zusammen mit dem aktiven Neuzeichnen * ohne * mit super.paintComponent (g) funktionieren, korrekt ist. Nichtsdestotrotz impliziert die Verwendung von super.paintComponent (g), dass das gesamte Fenster in jedem Animationsframe neu gezeichnet werden muss, was die CPU-Nutzung von der Fenstergröße abhängig macht, was ich gerne verhindern möchte. So wird meine Frage beantwortet, aber mein Problem steht immer noch. Ich werde einen neuen Beitrag dazu machen. – Mattijs
@ Mattijs: Verwenden von SetOpaque (false) wird die Füllung vermeiden. Ich habe das Beispiel aktualisiert, um die Malzeit anzuzeigen. Der Unterschied ist beträchtlich, aber es muss gegen den Aufwand abgewogen werden, Updates zu verwalten. – trashgod