mojego rozeznania, gdy dzwonisz paintImmediately, wywołać następujący kod:
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);
}
Więc, chyba że nie jest to JComponent
, skończyć się nazywając _paintImmediately()
który kończy się wywołaniem paint(Graphics)
jak sugeruje ślad stosu poniżej (zrobione z kawałka kodu będę pisać na końcu tego postu):
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
Ale jeśli spróbujesz również zadzwonić repaint()
jednocześnie (z innego wątku), widać, że zarówno uruchomione w tym samym czasie (próbowałem wkleić kod z debugerem i malowanie nigdy nie przestało występować w drugim wątku) wydaje się, że na poziomie kodu Java nie ma dużej synchronizacji (przynajmniej nie mogłem niczego wykryć). Jeśli więc zmienisz stan komponentu w EDT, uważam, że wyniki są dość nieprzewidywalne i powinieneś unikać takiej sytuacji za wszelką cenę.
Żeby zilustrować mój punkt widzenia, próbowałem modyfikując stan zmiennej w metodzie paint
dodaj sleep
zwiększa ryzyko kolizji między 2 Nitki (EDT i drugi) i obvisouly wydaje się, że nie ma synchronizacja między dwoma wątkami (System.err.println()
wyprowadzana od czasu do czasu).
Teraz zastanawiałam się, dlaczego musisz wykonać farbę od razu. O ile nie blokujesz EDT, nie ma aż tak wielu ważnych powodów, aby to zrobić.
Poniżej znajduje się kod użyty do przetestowania tych rzeczy (całkiem blisko tego, który zamieszczono w pytaniu). Kod ma na celu jedynie zrozumienie tego, co się dzieje, a nie pokazanie, jak wykonać właściwe malowanie, ani jakiejkolwiek dobrej praktyki Swinga.
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 przygwoździliście to. Zastanawiam się tylko, dlaczego 'dokumentacja JComponent' odwołuje się do' paintImmediately() 'i' repaint (..) 'gdzie jako źródło jest inne. Muszę jednak zapytać, czy w twoim urywku nigdy byśmy nie nadpisali 'paint (..)' 'JComponent ', dlaczego więc nie użyłeś' paintComponent' (nadal wydaje się odtwarzać wyniki)? I jeszcze jedno pytanie, dlaczego zwiększenie "snu (int milis)" nie zwiększy kolizji? A także dlaczego nazywacie 'paintImmediately' z wątku innego niż EDT (ponieważ widzę raz wywołany na EDT nie pojawi się null)? –
Dzięki @Guillaume za wypróbowanie tego i podzielenie się swoimi badaniami.Zgadzam się, że nie ma wielu powodów, aby używać paintImmediately(), ale próbowałem zrozumieć "jak to działa?" Ponieważ javadoc mówi ... "Ta metoda jest przydatna, jeśli trzeba zaktualizować wyświetlacz podczas wysyłania bieżącego zdarzenia." Ta metoda jest również publiczna i nie ma absolutnie żadnych ostrzeżeń nigdzie wspomnianych o jej zastrzeżeniach; przynajmniej javadoc wprowadza w błąd. Teraz rozumiem paintImmediately() musi być wywoływany w EDT i nie jest bezpieczny dla wątków w przeciwieństwie do repaint(). – Learner
@DavidKroukamp Absolutnie nie ma żadnego powodu, aby przesłonić 'paint' zamiast' paintComponent', po prostu myślałem, że przy próbie zobaczenia jak 'paintImmediately' ostatecznie wywołuje' paint' dodanie dodatkowego wywołania 'paintComponent' w wywołaniu stosu było niepotrzebne a zaobserwowane wyniki pozostaną takie same. Tak więc było po prostu usunięcie wszelkich "szumów" z informacji, a jak powiedziałem przed moim urywkiem, nie ilustruje to właściwego sposobu użycia Swinga. Twoje zdrowie ;-) –