Per la mia comprensione, quando si chiama paintImmediately, si chiama il seguente codice:
Component c = this;
Component parent;
if(!isShowing()) {
return;
}
JComponent paintingOigin = SwingUtilities.getPaintingOrigin(this);
if (paintingOigin != null) {
Rectangle rectangle = SwingUtilities.convertRectangle(
c, new Rectangle(x, y, w, h), paintingOigin);
paintingOigin.paintImmediately(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
return;
}
while(!c.isOpaque()) {
parent = c.getParent();
if(parent != null) {
x += c.getX();
y += c.getY();
c = parent;
} else {
break;
}
if(!(c instanceof JComponent)) {
break;
}
}
if(c instanceof JComponent) {
((JComponent)c)._paintImmediately(x,y,w,h);
} else {
c.repaint(x,y,w,h);
}
Quindi, a meno che questa non è una JComponent
, si finisce per chiamare _paintImmediately()
che finisce per chiamare paint(Graphics)
come suggerisce l'analisi dello stack qui di seguito (catturato da un pezzo di codice mi post alla fine di questo post):
Thread [pool-1-thread-1] (Suspended)
TestPaint$1.paint(Graphics) line: 23
TestPaint$1(JComponent).paintToOffscreen(Graphics, int, int, int, int, int, int) line: 5221
RepaintManager$PaintManager.paintDoubleBuffered(JComponent, Image, Graphics, int, int, int, int) line: 1482
RepaintManager$PaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1413
RepaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1206
TestPaint$1(JComponent)._paintImmediately(int, int, int, int) line: 5169
TestPaint$1(JComponent).paintImmediately(int, int, int, int) line: 4980
TestPaint$1(JComponent).paintImmediately(Rectangle) line: 4992
TestPaint$3.run() line: 50
ThreadPoolExecutor.runWorker(ThreadPoolExecutor$Worker) line: 1110
ThreadPoolExecutor$Worker.run() line: 603
Thread.run() line: 722
Ma se si tenta di chiamare anche repaint()
contemporaneamente (da un altro thread), si vede che entrambi eseguito allo stesso tempo (ho provato a inserire il codice con il debugger e la pittura non si è mai interrotta nell'altro thread), sembra che a livello di codice Java non ci sia molta sincronizzazione (almeno non ho potuto individuare nulla). Quindi, se si finisce per modificare lo stato del componente nell'EDT, credo che i risultati siano alquanto imprevedibili e si dovrebbe evitare tale situazione con tutti i mezzi.
solo illustrare il mio punto, ho provato modificare lo stato di una variabile all'interno del metodo paint
, aggiungere un sleep
per aumentare il rischio di collisione tra le 2 Fili (EDT e l'altra) e sembra obvisouly che non c'è sincronizzazione tra i due thread (il numero System.err.println()
ha emesso il numero null
di volta in volta).
Ora mi chiedo perché è necessario eseguire una verniciatura immediatamente. A meno che non si stia bloccando l'EDT, non ci sono molti validi motivi per eseguire tale operazione.
Di seguito è riportato il codice che ho utilizzato per testare quelle cose (molto vicino a quello pubblicato nella domanda). Il codice è pensato solo per cercare di capire cosa sta succedendo, non per mostrare come eseguire la pittura appropriata né alcuna buona pratica Swing.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import java.util.concurrent.Executors;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TestPaint {
protected void initUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle(TestPaint.class.getSimpleName());
final Random rand = new Random();
final JPanel comp = new JPanel() {
private String value;
@Override
public void paint(Graphics g) {
value = "hello";
super.paint(g);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g.setColor(new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256)));
g.fillRect(0, 0, getWidth(), getHeight());
if (SwingUtilities.isEventDispatchThread()) {
System.err.println("Painting in the EDT " + getValue());
} else {
System.err.println("Not painting in EDT " + getValue());
}
value = null;
}
public String getValue() {
return value;
}
};
frame.add(comp);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Timer t = new Timer(1, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
comp.repaint();
}
});
t.start();
Executors.newSingleThreadExecutor().execute(new Runnable() {
@Override
public void run() {
while (true) {
comp.paintImmediately(comp.getBounds());
}
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new TestPaint().initUI();
}
});
}
}
+1 lo hai inchiodato. Mi chiedo solo perché i documenti 'JComponent' fanno riferimento a' paintImmediately() 'e' repaint (..) 'dove la fonte è diversa. Devo però chiedere nel tuo frammento di non sovrascrivere 'paint (..)' di un 'JComponent', quindi perché non hai usato' paintComponent' (sembra ancora riprodurre i risultati)? E un'altra domanda perché aumentare il "sonno (int milis)" non aumenta le collisioni? E anche perché chiami 'paintImmediately' da un thread diverso da EDT, (perché vedo una volta chiamato EDT non si verificherà nulla)? –
Grazie a @Guillaume per provarlo tu stesso e condividere la tua ricerca.Sono d'accordo che non ci sono molti motivi per usare paintImmediately() ma stavo solo cercando di capire "come funziona?" Perché javadoc dice ... "Questo metodo è utile se è necessario aggiornare il display mentre viene inviato l'evento corrente." Inoltre, questo metodo è pubblico e non vi sono assolutamente avvertenze menzionate in alcuno dei suoi avvertimenti; almeno il javadoc è fuorviante. Ora capisco che paintImmediately() deve essere chiamato all'interno di EDT e non è thread-safe a differenza di repaint(). – Learner
@DavidKroukamp Assolutamente nessuna buona ragione per sovrascrivere 'paint' piuttosto che' paintComponent', ho solo pensato che nel tentativo di vedere come 'paintImmediately' alla fine richiama' paint' aggiungendo la chiamata aggiuntiva a 'paintComponent' nella chiamata stack non era necessaria e i risultati osservati rimarrebbero gli stessi. Quindi era più semplicemente rimuovere ogni "rumore" dalle informazioni, e come ho detto prima del mio frammento non illustra il modo corretto di usare Swing. Saluti ;-) –