Reputation:
I'm trying to call the repaint method from the run() in the code. If I repaint() from the MouseDragged or MouseMoved It works fine. But I need to do it from the run().The following code does not make a call to repaint method from run().
I'm new in JAVA. can anyone fix the code and paste the code? please excuse any silly mistakes. :). BTW i saw SwingUtilities.invokelater may fix this. but i don't know how to do that. please fix the code.
Thanks in advance.
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
public class Tester {
public static int x,y;
public static void main(String[] args) {
x = 10; y= 10;
Draw d = new Draw();
new Thread(d).start();
}
public static class Draw extends JFrame implements Runnable,MouseMotionListener
{
public Draw()
{
super("Title");
setSize(500,500);
addMouseMotionListener(this);
setVisible(true);
}
@Override
public void run() {
for(int i = 0 ; i < 10 ; i++)
{
System.out.println("Multithreaded");
repaint();
}
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseMoved(MouseEvent e) {
}
public void paint(Graphics g)
{
System.out.println("repaint called");
}
}
}
Upvotes: 0
Views: 850
Reputation: 347184
Swing employees a passive repaint engine. That is, it will only update when it is required to. The RepaintManager
is also optimised to consolidate multiple repaints down to as few repaint events as it thinks is required.
This means that you can make a request for a repaint
, but there is no guarantee to when or if that repaint will occur.
This is done mostly for performance optimisation.
Check out Painting in AWT and Swing for more details.
Because of it's nature, repaint
is thread safe. repaint
asks the RepaintManager
to post a paint event onto the event queue. This queue is the processed by the Event Dispatching Thread, meaning, you don't have to synchronise repaint
with the EDT yourself.
The following example demonstrates that idea. It provides a simple slider which resets the paint counters and sets the delay between repaint
requests.
Thread delay of 0 milliseconds...
Thread delay of 2 seconds
As you can see. At 0 milliseconds (which is effectively what you are doing in you loop), the number of actual paints no where matches the number of repaint requests, but at a delay of 2 seconds, the actual paints and the paint requests are almost equal (the extra one comes from the repaint made by the slider I think).
In fact, in my testing, at about 100 milliseconds, I was able to get it to just about equal out. I even tried 5 milliseconds and got it to balance.
What the actual paint is doing and the load on the EDT will also effect these results...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class RepaintTest {
public static void main(String[] args) {
new RepaintTest();
}
public RepaintTest() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private int paintRequests;
private int paints;
private int delay = 0;
public TestPane() {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
paintRequests++;
try {
Thread.sleep(delay);
} catch (InterruptedException exp) {
}
System.out.println("tick");
repaint();
}
}
});
t.setDaemon(true);
setLayout(new BorderLayout());
final JSlider slider = new JSlider();
slider.setMinimum(0);
slider.setMaximum(2000);
slider.setPaintTicks(true);
slider.setMajorTickSpacing(100);
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
delay = slider.getValue();
paintRequests = 0;
paints = 0;
}
});
slider.setValue(0);
add(slider, BorderLayout.SOUTH);
t.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
paints++;
String text = "Paints = " + paints + "; Paint Requests = " + paintRequests;
Graphics2D g2d = (Graphics2D) g.create();
FontMetrics fm = g2d.getFontMetrics();
int x = (getWidth() - fm.stringWidth(text)) / 2;
int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
g2d.drawString(text, x, y);
g2d.dispose();
}
}
}
Upvotes: 2
Reputation: 128
Try implementing ActionListener
, then add this bit of code:
import javax.swing.Timer;
private final int DELAY = 60;
private Timer t;
public Draw() {
//your code
t = new Timer(DELAY, this);
t.start();
}
//implemented method
@Override
public void actionPerformed(ActionEvent e) {
repaint();
}
Pretty self explanatory, whatever is in the actionPerformed
method is called every DELAY
milliseconds.
Upvotes: 0