paintComponent() gets called only when Minimizing-Maximizing Screen - java

I am trying to develop a game in which I need to draw a grid. For that I am using the paintComponent(Graphics g) method which is being called by repaint() method.
The problem is that the repaint method is inside the infinite While loop and it never calls the paintComponent() method unless I minimize and maximize the screen. After that it works fine and calls the paintComponent() perfectly in the while loop.
So in short, I need to trigger it by Minimizing-Maximizing the screen.
Can anybody help me out?
In the code you can see 3 classes namely, Frame.java, MenuHandler.java & Screen.java. Code starts from the main method in Frame class and it adds the Screen class to itself as Screen extends JPanel. However, the control goes to the Screen class only when user selects "Create Map" on the menu. Then MenuHandler class passes the control to the Screen class where the run method is called in the createMap method and I invoking the repaint method inside this run method.
package so;
public class Screen extends JPanel implements Runnable {
Frame frame;
public Screen(Frame frame) {
this.frame = frame;
}
public void createMap() {
thread.start();
}
public void paintComponent(Graphics g) {
g.setColor(Color.BLUE);
}
#Override
public void run() {
System.out.println("Success******");
long lastFrame = System.currentTimeMillis();
int frames = 0;
running = true;
scene = 0;
// the map grid would be refreshed every 2 ms so that we don't get the
// flickering effect
while (running) {
frames++;
if (System.currentTimeMillis() - 1000 >= lastFrame) {
fps = frames;
frames = 0;
lastFrame = System.currentTimeMillis();
}
// to draw stuff all the time on the screen : goes around 2 millions
// frames per second. Which is of no use.
repaint();
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.exit(0);
}
}
Timer code in the run method:
public void run() {
System.out.println("Success");
long lastFrame = System.currentTimeMillis();
int frames = 0;
running = true;
scene = 0;
// the map grid would be refreshed every 2 ms so that we don't get the
// flickering effect
while (running) {
frames++;
if (System.currentTimeMillis() - 1000 >= lastFrame) {
fps = frames;
frames = 0;
lastFrame = System.currentTimeMillis();
}
System.out.println("before repaint");
// to draw stuff all the time on the screen : goes around 2 millions
// frames per second. Which is of no use.
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
repaint();
}
};
new Timer(200, taskPerformer).start();
System.out.println("after repaint");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.exit(0);
}

I figured out the issue. it was just one line that I had to add to trigger the paintComponent method instead of doing it by minimizing-maximizing the window.
Frame was my top level container and I was adding Screen component (that extends JPanel and has the implementation of paintComponent) to the frame. So while adding, earlier I was doing
frame.add(screen);
I changed this to:
frame.getContentPane().add(screen);
frame.getContentPane().validate();
Calling the validate method after adding it did it for me. I don't know if it makes sense but yes that was the only line that worked for me.
Hope it helps.

Related

Movement through a timer

I am trying to make several small boxes move around a page based on several properties. The movement itself is handled by the method private void direction_move_creature(){. which i then call in as:
public class Listener implements ActionListener {
public void actionPerformed(ActionEvent e){
//Looping 200 time steps, each time updating the coordinates of the creatures.
for(int i = 0; i < 20; i++){
direction_move_creature();
repaint();
}
System.out.println("HERE");
}
}
and my main method is:
public static void main(String[] args){
JFrame frame = new JFrame("Draw");
frame.setSize(550, 550);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(new Draw());
frame.setVisible(true);
System.out.println("final");
}
}
new Draw just fills the structures that hold the coordinates for the moving shapes. When i try to run the program i get no movement what so ever. I knwo these are only fragments of code but any help would be greatly appreciated.
You're misunderstanding how a Swing Timer works. The Timer replaces the for loop. Get rid of the loop and increment a counter within the timer's ActionListener.
e.g.,
class MyActionListener implements ActionListener {
private int counter = 0;
#Override
public void actionPerformed(ActionEvent e) {
// assuming MAX_COUNTER is 20
if (counter < MAX_COUNTER) {
direction_move_creature();
repaint();
counter++;
} else {
// stop the Timer
((Timer) e.getSource()).stop();
}
}
}

Why my GUI won't update even tho repaint() is being called?

I am having some difficulties using swing workers, timers, and I am actually a little confused.
As far as my understanding goes, I have to put on a timer to set-up recurring tasks that have to be called by the EDT.
I'm trying to make a program that shows graphically a sorting alghoritm (like this : https://www.youtube.com/watch?v=kPRA0W1kECg )
I just don't understand why the GUI won't refresh. I am quite sure the repaint method is being called since I put a sysout showing me the ordered values and it seems to work , but the GUI just... doesn't change.
Here's my code:
public class MainWindow {
private JFrame frame;
JPanel panel;
public final static int JFRAME_WIDTH = 800;
public final static int JFRAME_HEIGHT = 600;
public final static int NELEM = 40;
ArrayList<Double> numbers;
ArrayList<myRectangle> drawables = new ArrayList<myRectangle>();
Lock lock = new ReentrantLock();
Condition waitme = lock.newCondition();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MainWindow window = new MainWindow();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public MainWindow() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, JFRAME_WIDTH + 20, JFRAME_HEIGHT + 40);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new myPanel();
frame.getContentPane().add(panel, BorderLayout.CENTER);
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
lock.lock();
try{
//Updating the gui
panel.repaint();
panel.revalidate();
//Giving the OK to the sorting alghoritm to proceed.
waitme.signal();
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
});
timer.start();
SwingWorker<Integer, String> sw = new SwingWorker<Integer, String>(){
#Override
protected Integer doInBackground() throws Exception {
mapAndCreate();
bubbleSort();
return null;
}
};
sw.execute();
}
private void bubbleSort() throws InterruptedException{
for(int i=0; i < NELEM; i++){
for(int j=1; j < (NELEM-i); j++){
if(drawables.get(j-1).wid > drawables.get(j).wid){
//swap the elements!
myRectangle temp = drawables.get(j-1);
drawables.set(j-1, drawables.get(j));
drawables.set(j, temp);
lock.lock();
try{
//Wait for the GUI to update.
waitme.await();
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
}
}
/***
* Function that maps values from 0 to 1 into the rectangle width.
*/
private void mapAndCreate() {
double max = 0;
numbers = new ArrayList<Double>(NELEM);
//Finding maximum.
for(int i = 0; i < NELEM; i++){
Double currElem = Math.random();
if(currElem > max) max = currElem;
numbers.add(currElem);
}
//Mapping process
int offset = 0;
for(int j = 0; j < NELEM; j++){
Integer mapped = (int) (( JFRAME_WIDTH * numbers.get(j) ) / max);
myRectangle rect = new myRectangle(offset , mapped);
drawables.add(rect);
offset += JFRAME_HEIGHT / NELEM;
}
}
private class myRectangle{
int myy , wid , colorR,colorG,colorB;
public myRectangle(int y , int wid){
this.myy = y;
this.wid = wid;
Random r = new Random();
colorR = r.nextInt(255);
colorG = r.nextInt(255);
colorB = r.nextInt(255);
}
}
private class myPanel extends JPanel{
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for(myRectangle rectan : drawables){
Graphics2D graphics2D = (Graphics2D)g;
System.out.println(rectan.wid);
Rectangle2D.Double rect = new Rectangle2D.Double(0,rectan.myy,rectan.wid,JFRAME_HEIGHT / NELEM);
graphics2D.setColor(new Color(rectan.colorR,rectan.colorG,rectan.colorB));
graphics2D.fill(rect);
}
System.out.println("====================================================================================================");
}
}
}
Most OSs (or rather the UI frameworks which they use) don't support concurrent access. Simply put, you can't render two strings of text at the same time.
That's why Swing runs all rendering operations in the UI thread. Calling rendering functions (like paint()) outside of the UI thread can cause all kinds of problems. So when you do it, Swing will just remember "I should repaint" and return (instead of doing any actual work). That way, Swing protects you but most people would prefer to get an error with a useful message.
A timer always also means that there is a thread somewhere which executes when the timer runs out. This is not the UI thread of Swing. So any paing operations there must be wrapped with EventQueue.invokeLater() or similar.
Another common bug is to hog the UI thread (so no rendering happens because you do complex calculations there). That's what the SwingWorker is for. Again, in most methods of the SwingWorker, calling methods which would render something is forbidden (-> use invokeLater()).
So my guess is that the UI thread waits for the lock and the lock simply isn't unlocked early or often enough. See this demo how to do a simple animation in Swing.
public class TimerBasedAnimation extends JPanel implements ActionListener {
public void paint(Graphics g) {
// setup
// do some first-run init stuff
// calculate the next frame
// render frame
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public static void main(String[] args) {
JFrame frame = new JFrame("TimerBasedAnimation");
frame.add(new TimerBasedAnimation());
...
}
}
As you can see in the code doesn't lock. Instead, you just send "render now" events from actionPerformed to Swing. Some time later, Swing will call paint(). There is no telling (and no way to make sure or force Swing) when this will happen.
So good animation code will take the current time, calculate the animation state at that time and then render it. So it doesn't blindly step through N phases in M seconds. Instead, it adjusts for every frame to create the illusion that the animation is smooth when it really isn't.
Related:
Java: Safe Animations with Swing
How to Use Swing Timers

Java repaint() method doesn't always work

There is a problem with the repaint() method in Java. I made a new thread that constantly repaints the screen. When I release the spacebar I want my player to fall smoothly by setting its position and then waiting for 50 milliseconds and looping that 20 times. Instead, it waits the whole amount of time in the loop, then repaints. I am wondering why it doesn't constantly repaint the changes in the players co-ordinates. Thank you.
(Edit) Thanks everyone for the help. This is my first time using stack overflow, and I am only 13 and still learning java, so I probably will go back to the tutorials again.
My 'a' class (main):
public class a {
public static void main(String[] args) {
JFrame frame = new JFrame("StickFigure Game");
frame.setSize(740, 580);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
b board = new b();
frame.add(board);
frame.addKeyListener(board);
}
}
My 'b' class (JPanel/drawing):
public class b extends JPanel implements KeyListener {
c player = new c();
public class MyRunnable implements Runnable {
public void run() {
while (true)
repaint();
}
}
MyRunnable run = new MyRunnable();
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(player.getImage(), player.getX(), player.getY(), 80, 140,
null);
}
public b() {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
public static void slow(int n) {
long t0, t1;
t0 = System.currentTimeMillis();
do {
t1 = System.currentTimeMillis();
} while (t1 - t0 < n);
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_D) {
player.setPos(player.getX() + 6, player.getY());
}
if (e.getKeyCode() == KeyEvent.VK_A) {
player.setPos(player.getX() - 6, player.getY());
}
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
player.setPos(player.getX(), player.getY() - 60);
}
}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
for (int i = 0; i < 20; i++) {
slow(50);
player.setPos(player.getX(), player.getY() + 2);
}
}
}
public void keyTyped(KeyEvent e) {
}
}
my 'c' class (player):
public class c {
private ImageIcon i = new ImageIcon("guy.png");
private Image img = i.getImage();
private int x = 0;
private int y = 100;
public void wait(int what) {
try {
Thread.sleep(what);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public c() {
}
public Image getImage() {
return img;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setPos(int mx, int my) {
x = mx;
y = my;
}
}
I haven't gone through all the code here but here are some pointers:
Swing has its own concurrency mechanisms which allow you to handle UI updates. You can use a Swing Timer rather than a raw Thread. Related is the use of Thread.sleep - don't do this, it only blocks the EDT and prevents UI updates.
The Swing paint chain mechanism requires you to override paintComponent rather than paint.
Always use Key Bindings rather than KeyListeners in Swing. KeyListeners require component focus to work to interact with the KeyEvents. Key Bindings do not have this limitation.
"There is a problem with the repaint() method in java." Did you consider that perhaps the problem is with your code instead? You are blocking the event thread and giving the system no time to do the intermediate repaints. In particular, this method:
public static void slow (int n){
long t0,t1;
t0=System.currentTimeMillis();
do{
t1=System.currentTimeMillis();
}
while (t1-t0<n);
}
and this loop:
for(int i = 0;i<20;i++){
slow(50);
player.setPos(player.getX(), player.getY()+2);
}
do not relinquish control to the system so that repaints can actually happen. Rewrite those using Swing timers. Look at this tutorial for an introduction on how to use these.
Also, your thread that constantly calls repaint() in a tight loop:
public void run(){
while(true) repaint();
}
is a terrible idea. You don't need to call repaint() at full CPU speed. Once every 30 milliseconds or so is fine for animation. Again, consider using Swing utilities to do this rather than writing your own looping thread.
The repaint is only a "request" to paint as soon as possible. so when you call it it causes a call to the paint method as soon as possible.
from here
So basically you just flooding the scheduled calls of paint or update with while(true) repaint();.
Oracle's stance on painting in AWT and Swing
One way you could do it, or should I say how I would do it, is to make your c class implement KeyListener, so that when a key is pressed (and only when it is pressed) you update it's location.
So move your KeyListener methods to class c, in your class b constructor you can add the call this.addKeyListener(player) or make a method void addPlayer(c player) that adds it.

I am trying to move a ball in applet using thread but its not moving

I am trying to move a ball in applet using thread but its not moving. Can anyone help me out as m new to applet and proceeding for game development..for reference here is my code
public class ballGame extends JApplet implements Runnable
{
int x_pos=50;
int y_pos=100;
int rad=10;
Thread t;
public void start()
{
super.start();
t=new Thread("t");
t.start();
}
public void paint(Graphics g)
{
super.paint(g);
g.setColor(Color.red);
setBackground(Color.BLACK);
g.drawOval(x_pos,y_pos,2*rad,2*rad);
while(true)
{
x_pos++;
//validate();
repaint();
try
{
Thread.sleep(100);
}
catch(Exception e)
{
e.printStackTrace();
}
}//end of while
}//end of paint()
}
Swing is a single thread environment. That is, all updates and interactions are executed within a single thread. Swing is also NOT thread safe. This means that all updates to the UI MUST be executed within the context of that thread (the Event Dispatching Thread or ETD).
Any code that blocks the EDT will prevent it from (amongst other things), repainting the UI and responding to input from the user.
You're paint code will NEVER update the screen, in fact it will make your application appear to "hang", as the paint method isn't being allowed to complete and is blocking the ETD.
It is an exception that the paint method will return quickly after been called and may be called repeatedly in quick succession.
Generally speaking, a Thread is probably a little over kill, something like a javax.swing.Timer would be more then suitable under these circumstances.
public class AnimatedBoat {
public static void main(String[] args) {
new AnimatedBoat();
}
public AnimatedBoat() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new AnimationPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class AnimationPane extends JPanel {
private BufferedImage boat;
private int xPos = 0;
private int direction = 1;
public AnimationPane() {
try {
boat = ImageIO.read(new File("boat.png"));
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += direction;
if (xPos + boat.getWidth() > getWidth()) {
xPos = getWidth() - boat.getWidth();
direction *= -1;
} else if (xPos < 0) {
xPos = 0;
direction *= -1;
}
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return boat == null ? super.getPreferredSize() : new Dimension(boat.getWidth() * 4, boat.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int y = getHeight() - boat.getHeight();
g.drawImage(boat, xPos, y, this);
}
}
}
As a side note. You should rarely need to override the paint method of a top level container like JApplet or JFrame, while there are a number of good reasons for this, the one that you're going to most interested in is the fact that they're not double buffered, meaning you are likely to see flickering as the screen is updated.
It's better to use something like JPanel and override it's paintComponent method instead.
Take a look at
Performing Custom Painting
Concurrency in Swing
Painting in AWT and Swing
For more information
nb
While I've used a JFrame for my example, it would be a simple matter to take the animation panel and put it into a JApplet, this is another reasons why you don't need/want to extend from top level containers ;)
Having an infinite loop in paint means that not a single pass of the method can complete.
Also you should never call Thread.sleep(100) in the paint method. This blocks the EDT and degrades performance.
Instead use a Swing Timer to do the update and repainting work. Also I would sub-class a JComponent and override paintComponent.
You can not invoke repaint() method inside paint(). And you can not organize infinitely loop inside paint() method - doing so, you are blocking drawing in your applet.
x_posis an int value, thus it is passed to methods by value, not by reference. That is why, when you change its value, the value inside of your circle is not updated...
You create a Thread without a run() method. This method should contain the runnable code... Furthermore, the paint() method is to paint stuff, not update stuff!
So move your while loop from the paint() method into the run() method of your thread:
t=new Thread("t") {
#Override
public void run()
{
while(true)
{
x_pos++;
//validate();
repaint();
try
{
Thread.sleep(100);
}
catch(Exception e)
{
e.printStackTrace();
}
}//end of while
}
};
Note that ballGame does not require the implement Runnable part. As the thread you created will provide it.
Have the while loop inside the run method of Runnable.
UPDATE:
Have this in the start method.
t=new Thread(this);
t.start();

Java - repaint JPanel 2 times in one method

This is simple version of my problem.
I have 3 classes:
public class TopographyFrame extends JFrame - simple JFrame with JPAnel and button
public class TopograpyPanel extends JPanel - JPanel to fill Rectangles
public class Siec - class to perform calculations and call repaint on JPAnale
in JPanel i overided paintComponent() method
public void paintComponent (Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
System.out.println(rectangles.length);
for(int i = 0 ; i < rectangles.length ; i++){
g2.setPaint(neurony[i].winner);
g2.fillRect((int)rectangles[i].x,(int)rectangles[i].y,(int)rectangles[i].width, (int)rectangles[i].height);
}
}
neurony - array of objects with field public Color winner
in class Siec i have reference to JPanel to repaint it
in class JFrame i have a button with private action listener:
class MyListener implements ActionListener{
Siec s;
public MyListener(Siec s){
this.s = s;
}
public void actionPerformed(ActionEvent arg0) {
try {
s.forPaint();
} catch (Exception e) {
e.printStackTrace();
}
}
method forPaint() in Siec looks like:
public void forPaint(){
setTopography();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
setTopography();
}
public void setTopography() {
for (int i = 0; i < vector.colors.length; i++) {
neurony[i].winner = vector.colors[(int)(random() * 900 % vector.colors.length)];
}
panel.repaint();
}
vector.color is array of Colors
So my problem is: when i click a button i would like to JPanel repaint immediately and then after 3 second repaint one more time. Insted JPanel repaints only one time after 3s delay.
}
You can't sleep, wait, or otherwise pause on the event handling thread, ever. Doing so blocks all events from being processed, including painting events. Your first painting can't occur because you're sleeping on the event thread, preventing it from happening.
The right way to do any kind of animation -- even simple stuff like this -- is to create your own thread. That second thread can call repaint(), sleep for 3 seconds, then call repaint() again. The SwingWorker class is nominally a simpler way to do this, but in all honesty, beginners always find creating their own thread to be easier.
You are scheduling a repaint on the UI thread and then sleeping (blocking) the UI thread for 3seconds and then requesting another repaint again. Those two will either happen really close to each other after this method has finished (after 3 seconds) or be merged into one update (afterwards as well).
Instead of sleep(3000) and then calling your setTopography again you could schedule a setTopography call on the UI thread to happen after 3 seconds.
Have a look at the Swing Timer for example:
http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
So something along the lines of:
javax.swing.Timer timer = new javax.swing.Timer(3000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
setTopography();
}
});
timer.setRepeats(false);
timer.start();
Since your sleep is being performed on the Event Dispatch Thread, the repaint() event cannot be performed until the end of the wait. Do this instead:
private Timer timer = new Timer(); // use java.util.Timer
public void forPaint() {
setTopography();
timer.schedule(new TimerTask() {
#Override
public void run() {
setTopography();
}
}, 3000);
}
public void setTopography() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
for (int i = 0; i < vector.colors.length; i++) {
neurony[i].winner = vector.colors[(int)(random() * 900 % vector.colors.length)];
}
panel.repaint();
}
});
}
Keep in mind that all modifications to a Swing component (e.g. your JPanel) must happen on the EDT.

Categories

Resources