Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (539)
Games in Android Showcase (133)
games submitted by our members
Games in WIP (603)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Double Buffering with a JPanel  (Read 2769 times)
0 Members and 1 Guest are viewing this topic.
Offline Tekkerue

Senior Devvie


Medals: 6
Projects: 1



« Posted 2014-07-30 14:37:27 »

---------------------------------------------------------------

*** UPDATE *** I have changed the topic slightly from my first post. Start from this post for the updated question:
http://www.java-gaming.org/topics/double-buffering-with-a-jpanel/33983/msg/320384/view.html#msg320384

---------------------------------------------------------------


Hi all,

I’m trying to get two JLabels to update at exactly the same time which I would like to do from my repaint method in my loop. However, the problem is that calling setText and setBackground on the JLables automatically calls the repaint method, therefore these labels aren’t actually getting updated by my repaint method in my loop.

I’m trying to implement a method of double buffering where I update the labels off screen and then in my repaint method I make an Image of the JPanel that holds the JLabels and display that Image. However, I cannot get this to work. The JFrame shows up solid black.

I believe that my problem is getting the Image of the JPanel and I’ve tried a couple different methods, but neither of them worked. I left both methods in my code so you can see the two methods I tried and one of them is commented out.

I did check to make sure everything else in my loop and double buffer were working correctly by removing all of the JPanel Image creating stuff in my paintComponent method and added a line to draw an oval with Graphics...
g.fillOval(x, 200, 20, 20);
Then I incremented “x” in my update method and the oval does move successfully across the screen. So everything else appears to be working.

Any help in getting my JPanel double buffer to work would be greatly appreciated...of if there is a better way I could be going about this, I’m open to suggestions there as well. Thanks! Smiley

Here’s my sample code:

TestApplication.java
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class TestApplication {
   static TestApplication app;
   static Loop loop;
   static Canvas canvas;
   
   JPanel         mainPanel;
   JLabel[]         beatDisplay;
   
   boolean isPlaying=true;
   
   int[] count = { 1, 1 };
   boolean[] isGreen = { true, true };
   
   JPanel center = new JPanel();
   
   public static void main (String[] args) {      
      app = new TestApplication();
     
      SwingUtilities.invokeLater(new Runnable(){
         public void run() {
            app.gui_Create();
            app.loop();
         }
      });
   }
   
   public void gui_Create() {
      gui_MainPanel();
      gui_AddContent();
      canvas = new Canvas(app);
   }
   public void gui_MainPanel() {
      mainPanel = new JPanel();
      mainPanel.setPreferredSize(new Dimension(400,400));
      mainPanel.setLayout(new BorderLayout());
      mainPanel.setVisible(true);
   }
   public void gui_AddContent() {
      center.setSize(new Dimension(400,400));
      center.setMaximumSize(new Dimension(400,400));
      center.setMinimumSize(new Dimension(400,400));
      center.setPreferredSize(new Dimension(400,400));
      center.setOpaque(true);
      center.setVisible(true);
     
      beatDisplay = new JLabel[2];
      for (int i=0; i<2; i++){
         String buttonName = "~ SOUND " + i + " ~";
         beatDisplay[i] = new JLabel(buttonName);
         beatDisplay[i].setPreferredSize(new Dimension(400, 50));
         beatDisplay[i].setHorizontalAlignment(SwingConstants.CENTER);
         beatDisplay[i].setOpaque(true);
         beatDisplay[i].setFont(new Font("Serif", Font.BOLD, 40));
         
         center.add(beatDisplay[i]);
      }
     
//       canvas.add(center, BorderLayout.CENTER);
   }
   
   public void loop(){
      loop = new Loop(app, canvas);
      loop.start();
   }
   
   public void update(){
      if (loop.frameCount%30 == 0){
         for (int i=0; i<2; i++){
            beatDisplay[i].setText(""+count[i]);
            if (isGreen[i]){
               beatDisplay[i].setBackground(Color.GREEN);
               isGreen[i] = false;
            }
            else{
               beatDisplay[i].setBackground(Color.YELLOW);
               isGreen[i] = true;
            }
           
            count[i]++;
            if (count[i] > 4)
               count[i] = 1;
         }
      }
   }
}


Loop.java
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
public class Loop extends Thread {
   static TestApplication app;
   static Canvas canvas;
   
   int fps = 60;
   long frameCount=1;
   
   public Loop(TestApplication appIn, Canvas canvasIn){
      app = appIn;
      canvas = canvasIn;
   }
   
   public void run(){
      while (app.isPlaying){
         app.update();
         canvas.repaint();
         try { Thread.sleep(fps); } catch (InterruptedException ie){}
         frameCount++;
      }
   }
}


Canvas.java
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

import java.awt.image.BufferedImage;

public class Canvas extends JFrame{
   static TestApplication app;
   
   Image dbImage;
   Graphics dbg;
   
   public Canvas(TestApplication appIn){
      app = appIn;
     
      setTitle("Sound Test");
      setPreferredSize(new Dimension(400,400));
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      pack();
      setVisible(true);
   }
   
   public void setMainPanel(JPanel panel){
      setContentPane(panel);
   }
   
   public void paint(Graphics g){
      dbImage = createImage(getWidth(), getHeight());
      dbg = dbImage.getGraphics();
      paintComponent(dbg);
      g.drawImage(dbImage, 0, 0, this);
   }
   
   public void paintComponent(Graphics g){
      Image temp;
     
      // NEITHER OF THESE WORK FOR CRREATING THE "TEMP" IMAGE
//       temp = app.center.createImage(app.center.getWidth(), app.center.getHeight());  
       temp = createImage(app.center);
     
       g.drawImage(temp, 0, 0, this);
     
      repaint();
   }
   
   private static BufferedImage createImage(JPanel panel) {
      int w = panel.getWidth();
      int h = panel.getHeight();
      BufferedImage bi = new BufferedImage(400, 400, BufferedImage.TYPE_INT_RGB);
      Graphics2D g = bi.createGraphics();
      return bi;
   }
}
Online BurntPizza

« JGO Bitwise Duke »


Medals: 291
Exp: 5 years



« Reply #1 - Posted 2014-07-30 14:44:56 »

If the labels calling additional repaints() is actually your problem, why not this:

1  
2  
3  
4  
5  
6  
7  
loop {
    canvas.setIgnoreRepaint(true);
    app.update();
    canvas.setIgnoreRepaint(false);
    canvas.repaint();
    sleep();
}
Offline Longor1996
« Reply #2 - Posted 2014-07-30 14:49:34 »

Why would you want to force the JLabel's to repaint at the same time?
It's just JLabels, so there shouldn't be any performance issues with letting Swing repaint them automatically.

If the labels calling additional repaints() is actually your problem, why not this:

1  
2  
3  
4  
5  
6  
7  
loop {
    canvas.setIgnoreRepaint(true);
    app.update();
    canvas.setIgnoreRepaint(false);
    canvas.repaint();
    sleep();
}


The method setIgnoreRepaint(true) tells Swing/AWT to ignore the repaint-messages from the operating-system, but does not disable the repaint()-calls that are made in setText(). See here: http://docs.oracle.com/javase/7/docs/api/java/awt/Component.html#setIgnoreRepaint(boolean)

Sorry for my bad English! That's because i am from Germany.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline KevinWorkman

JGO Kernel


Medals: 107
Projects: 11
Exp: 12 years


klaatu barada nikto


« Reply #3 - Posted 2014-07-30 14:50:09 »

This whole approach doesn't make a lot of sense to me.

Swing is already double-buffered. Why do you think you need to handle double-buffering yourself?

Why are you updating a JLabel "off-screen"? All changes to Swing components must be done on the EDT anyway, so I'm not sure what kind of performance gain you're expecting from this.

In any case, when do you draw the JPanel to the BufferedImage?

Static Void Games - Play indie games, learn game programming, upload your own games!
Online BurntPizza

« JGO Bitwise Duke »


Medals: 291
Exp: 5 years



« Reply #4 - Posted 2014-07-30 14:53:41 »

The method setIgnoreRepaint(true) tells Swing/AWT to ignore the repaint-messages from the operating-system, but does not disable the repaint()-calls that are made in setText(). See here: http://docs.oracle.com/javase/7/docs/api/java/awt/Component.html#setIgnoreRepaint(boolean)

Indeed, you're quite right. Forgot about that distinction.

@OP I think everyone would appreciate an sscce of what it is you're actually trying to do.
Offline Tekkerue

Senior Devvie


Medals: 6
Projects: 1



« Reply #5 - Posted 2014-07-30 15:14:55 »

Wow...you guys are quick! Cheesy Thanks everyone!

To everyone: Basically what I'm trying to do is prevent one of the label displays from getting updated slightly before the other one. This is for a metronome application and sometimes (especially at faster speeds) one display will change just slightly before the other one when they should both change at precisely the same time. That's basically what I'm trying to prevent from happening with my double buffering method. I know double buffering is usually used to prevent "flickering", but I figured that approach might work for what I need also.

KevinWorkman, I've read up on painting but I still don't fully understand some of the "magic" that happens behind the painting methods. I found an example of double buffering using Graphics that moved an oval around the screen with the arrow keys and I tried replacing the "draw oval" part with a "draw Image" which I'm trying to get from the JPanel. If drawing an oval works (and it does when I add that into my example code), then drawing an image should theoretically work as well as long as I get the get the Image correctly from the JPanel...which unfortunately isn't working.
Online BurntPizza

« JGO Bitwise Duke »


Medals: 291
Exp: 5 years



« Reply #6 - Posted 2014-07-30 15:21:49 »

I don't see any de-syncing here:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
public static void main(String[] a) throws InterruptedException {
   JFrame frame = new JFrame();
   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   frame.setSize(400, 400);
   frame.setLocationRelativeTo(null);
   
   JPanel panel = new JPanel(); // JPanel is double buffered by default
   frame.add(panel);
   panel.setLayout(new GridBagLayout());
   
   JLabel label1 = new JLabel("Label 1");
   JLabel label2 = new JLabel("Label 2");
   JLabel label3 = new JLabel("Label 3");
   JLabel label4 = new JLabel("Label 4");
   label1.setOpaque(true);
   label2.setOpaque(true);
   label3.setOpaque(true);
   label4.setOpaque(true);
   panel.add(label1);
   panel.add(label2);
   panel.add(label3);
   panel.add(label4);
   
   frame.setVisible(true);
   
   int count = 0;
   int color = 0;
   
   Color[] colors = new Color[] { Color.white, Color.lightGray };
   
   while (true) {
      if (count++ % 30 == 0) {
         label1.setBackground(colors[++color & 1]);
         label2.setBackground(colors[color + 1 & 1]);
         label3.setBackground(colors[color & 1]);
         label4.setBackground(colors[color & 1]);
      }
      Thread.sleep(16);
   }
}


In fact I don't even see it when the labels are added straight to the JFrame without a JPanel.
Offline Tekkerue

Senior Devvie


Medals: 6
Projects: 1



« Reply #7 - Posted 2014-07-30 15:54:17 »

BurntPizza, these shorter code samples don't really show the problem, but my actual metronome app has a lot more code in between changing the display. I shortened this example down to the bare essentials of trying to get the double buffering to work. I didn't think anyone would want to sort through my current 9 files of code. Cheesy I'm also in the process of reworking my program (based on some help I had in another thread) in order to implement the "loop" as I did it a totally different way before. But I was having issues with the display being off sync and I wanted to get that problem sorted out before I got to deep in rewriting everything.
Online BurntPizza

« JGO Bitwise Duke »


Medals: 291
Exp: 5 years



« Reply #8 - Posted 2014-07-30 16:13:09 »

What about if you insert a sleep(30):
1  
2  
3  
4  
5  
label1.setBackground(colors[++color & 1]);
label2.setBackground(colors[color + 1 & 1]);
label3.setBackground(colors[color & 1]);
Thread.sleep(30);
label4.setBackground(colors[color & 1]);


Is that like what you're experiencing, like what you mean by "a lot more code in between changing the display"? Update all states at the same time and you should be good, if there's signifficant stuff executing in between the updating of different UI components, then yeah, that's gonna look weird.
Offline Longor1996
« Reply #9 - Posted 2014-07-30 16:22:40 »

But I was having issues with the display being off sync and I wanted to get that problem sorted out before I got to deep in rewriting everything.

I have the feeling your problems with the display being off-sync has to do with your possibly incorrect usage of Swing, or more precisely the EDT.
If you put any long-running tasks into the EDT, (be it a complex calculation or the loading of a file), the entire GUI will start to behave really weird and get unresponsive. Making mistakes like not changing Swing-components on the EDT, or running a very expensive operation on the EDT, is the main reason for a lot of bugs. What's even worse is constantly calling repaint() as fast as possible by using a dynamic-timestep application-loop, which is what I think you are doing (?).

Fix your application-loop so it doesn't call the repaint() method more than ~30 times per second.

Why 30 times: Nobody can possibly read a number that changes more than 30 times per second (there are probably exceptions to that). Example: I can barely guess a number that changes 20 times per second, any faster and I am totally lost.

Sorry for my bad English! That's because i am from Germany.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Online BurntPizza

« JGO Bitwise Duke »


Medals: 291
Exp: 5 years



« Reply #10 - Posted 2014-07-30 16:32:57 »

I doubt it is, but if the fact that the JLabels cause repaints is really your problem, there is a way to prevent it, although it's a really nasty hack:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
<snip>
      NColor[] colors = new NColor[] { new NColor(Color.white), new NColor(Color.lightGray) };
     
      while (true) {
         if (count++ % 30 == 0) {
            label1.setBackground(colors[++color & 1]);
            label2.setBackground(colors[color + 1 & 1]);
            label3.setBackground(colors[color & 1]);
            Thread.sleep(30);
            label4.setBackground(colors[color & 1]);
         }
         panel.repaint();
         Thread.sleep(16);
      }
   }
}

@SuppressWarnings("serial")
class NColor extends Color {
   
   public NColor(Color c) {
      super(c.getRGB());
   }
   
   @Override
   public boolean equals(Object obj) {
      return true; // AAHHH
   }
}


Note that the labels change their backgrounds in lockstep (the panel.repaint()) even though there's that sleep(30) that from my previous example would normally cause a lag on label4.
I expect there is a similar hack for setText().

I do not recommend this at all, but you might try it just to see if that is what your problem actually is.
Offline KevinWorkman

JGO Kernel


Medals: 107
Projects: 11
Exp: 12 years


klaatu barada nikto


« Reply #11 - Posted 2014-07-30 16:36:01 »

Basically what I'm trying to do is prevent one of the label displays from getting updated slightly before the other one.

You should only be modifying the JLabels on the EDT.

Painting is done on the EDT as well. So are events.

That means that if you set the text (or icons, whatever) of both JLabels at the same time, neither JLabel will repaint before the other one is updated, because you're using the EDT to update the JLabels, so painting can't be started until you're done.

You're introducing a ton of needless complexity to this program, when really all you need are the basics.

Update the JLabels from the EDT. Let them paint themselves.

Static Void Games - Play indie games, learn game programming, upload your own games!
Online BurntPizza

« JGO Bitwise Duke »


Medals: 291
Exp: 5 years



« Reply #12 - Posted 2014-07-30 16:41:27 »

You should only be modifying the JLabels on the EDT.

Update the JLabels from the EDT. Let them paint themselves.

This is solid advise as well.
Another demo modification:

(back to normal colors, no NColor hacks)
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
<snip>
while (true) {
      if (count++ % 30 == 0) {
         color++;
         final int c = color;
         SwingUtilities.invokeLater(() -> {  // run update on EDT (you don't have to use JDK8)
            label1.setBackground(colors[c & 1]);
            label2.setBackground(colors[c + 1 & 1]);
            label3.setBackground(colors[c & 1]);
            try {
               Thread.sleep(30);
            } catch (Exception e) {
            }
            label4.setBackground(colors[c & 1]);
         });
      }
      panel.repaint();
      Thread.sleep(16);
   }


Updates in lockstep.
Offline KevinWorkman

JGO Kernel


Medals: 107
Projects: 11
Exp: 12 years


klaatu barada nikto


« Reply #13 - Posted 2014-07-30 17:03:06 »

To demonstrate my points, here is a program that constantly updates two JLabels **on the EDT** and keeps them in sync simply by obeying the Swing rule that anything that changes the GUI (including your JLabels) must be done on the EDT:

Warning: this program might not be safe for people with epilepsy.


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
import java.awt.BorderLayout;
import java.awt.Color;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;


public class Test {
   public static void main (String [] args){

      JFrame frame = new JFrame("JLabel Test");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      final JLabel topLabel = new JLabel("top");
      final JLabel bottomLabel = new JLabel("bottom");

      topLabel.setOpaque(true);
      bottomLabel.setOpaque(true);

      Thread t = new Thread(){
         public void run(){

            int steps = 0;

            while(true){

               //this is a lazy trick
               final String stepsText = "Steps: " + (++steps);

               //this will be done on the EDT so the whole thing will happen before any painting
               SwingUtilities.invokeLater(new Runnable(){
                  public void run(){

                     Color bg = new Color((int)(Math.random()*256), (int)(Math.random()*256), (int)(Math.random()*256));
                     Color fg = new Color((int)(Math.random()*256), (int)(Math.random()*256), (int)(Math.random()*256));


                     topLabel.setBackground(bg);
                     topLabel.setForeground(fg);

                     bottomLabel.setBackground(bg);
                     bottomLabel.setForeground(fg);
                     
                     topLabel.setText(stepsText);
                     bottomLabel.setText(stepsText);

                  }
               });

               try {
                  Thread.sleep(10);
               }
               catch (InterruptedException e) {
                  e.printStackTrace();
               }

            }
         }
      };


      frame.add(topLabel, BorderLayout.NORTH);
      frame.add(bottomLabel,BorderLayout.SOUTH);

      frame.setSize(200, 100);
      frame.setVisible(true);

      t.start();
   }
}



If you don't believe me that they're staying in sync, try taking a screenshot.

Static Void Games - Play indie games, learn game programming, upload your own games!
Online BurntPizza

« JGO Bitwise Duke »


Medals: 291
Exp: 5 years



« Reply #14 - Posted 2014-07-30 17:04:13 »

@Kevin Also see my example.
Offline KevinWorkman

JGO Kernel


Medals: 107
Projects: 11
Exp: 12 years


klaatu barada nikto


« Reply #15 - Posted 2014-07-30 17:16:20 »

@Kevin Also see my example.

Whoops, all I saw was your snippet, and I wanted to provide a complete MCVE. I didn't realize you did the same thing above!

Static Void Games - Play indie games, learn game programming, upload your own games!
Offline Tekkerue

Senior Devvie


Medals: 6
Projects: 1



« Reply #16 - Posted 2014-07-30 17:45:18 »

To everyone mentioning the EDT,

Ahhhhh, yeah! That's much better than the hack job that I was trying to do! Smiley

So let's see if I've worked this through correctly...instead of directly changing the text and background in my update method, I'd instead set a Boolean (to test whether or not to change the JLabel), Color, and String (which would both be sent to the JLabel) and then call another method which would use these variables to update the JLabel via the EDT. Correct?


BurntPizza,

Yeah, your example was pretty close to the delay I was getting, sometimes mine would be a little more actually. I also had to change your colors to alternate between yellow and green. The grey and white actually didn't show the delay as well.

I ran your code on the EDT (like you posted later) and I even increased the delay...this works PERFECT no delay at all!


KevinWorkman,

Wow, that's some serious seizure inducing flashing going on there! Shocked LOL

I believed you that they're in sync, but I took a screen shot anyways. Grin It was perfect as expected!


Thank you all again everyone for your awesome responses. This helps immensely!!!
Offline Tekkerue

Senior Devvie


Medals: 6
Projects: 1



« Reply #17 - Posted 2014-08-03 00:22:17 »

Hello again,

I have one more question...

I used BurntPizza’s example, tweaked it a bit and added an animation onto it using jpg’s. For my metronome program I’d also like to add a “swinging” metronome animation so I’m trying my hand at getting some kind of simple animation (only two frames) going along with updating the JLabels.

The only way I could get the animation to switch at the same time as the other JLabels was to use two separate jpg images, load them as ImageIcons, and then add them to a JLabel by calling setIcon in the invokeLater method. This method does work, but since I’m not experienced in animation I just wanted to make sure that this was an acceptable method to use for animation...or would I be better off using another method?

I did try the single “sprite sheet” method at first, but from the tutorials and examples I looked at this involved drawing inside of the paintComponent method and I wasn’t able to figure out how to get it that to work along with my JLabels. So I tried the ImageIcon method and that was fairly easy to get working.

Here is what I was able to get working...am I on the right path here? Thanks! Smiley

Test.java
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
import java.awt.*;
import javax.swing.*;

public class Test extends JFrame{
   JPanel panel = new JPanel();
   final JLabel label1 = new JLabel("Label 1");
   final JLabel label2 = new JLabel("Label 2");
   JLabel animationLabel = new JLabel();
   
   final Color[] colors = new Color[] { Color.green, Color.yellow };
   final int[] position = { 100, 300 };
   
   int count = 0;
   int index = 0;
   
   ImageIcon[] animation = new ImageIcon[2];
   
   public static void main(String[] a) throws InterruptedException {
      Test test = new Test();
   }
   
   public Test() throws InterruptedException {
      animation[0] = new ImageIcon(getClass().getResource("dot1.jpg"));
      animation[1] = new ImageIcon(getClass().getResource("dot2.jpg"));
     
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      setSize(410, 300);
      setLocationRelativeTo(null);
     
      label1.setMinimumSize(new Dimension(100,100));
      label1.setPreferredSize(new Dimension(100,100));
      label1.setOpaque(true);
     
      label2.setMinimumSize(new Dimension(100,100));
      label2.setPreferredSize(new Dimension(100,100));
      label2.setOpaque(true);
     
      animationLabel.setMinimumSize(new Dimension(400,100));
      animationLabel.setPreferredSize(new Dimension(400,100));
      animationLabel.setOpaque(true);

      panel.setSize(new Dimension(400, 400));      
      panel.add(label1);
      panel.add(label2);
      panel.add(animationLabel);
     
      add(panel);
      setVisible(true);
     
      while (true) {
         if (count++ % 30 == 0) {
            final int i = index % 2;            
            SwingUtilities.invokeLater(new Runnable(){
               public void run(){
                  label1.setBackground(colors[i]);
                  label2.setBackground(colors[i]);
                  animationLabel.setIcon(animation[i]);
               }
            });
            index++;
         }
         Thread.sleep(16);
      }
   }
}


And here are the two jpgs I used:

dot1.jpg


dot2.jpg
Online BurntPizza

« JGO Bitwise Duke »


Medals: 291
Exp: 5 years



« Reply #18 - Posted 2014-08-03 00:58:50 »

Here it is with the animation driving the labels via PropertyChangeListeners:

http://pastebin.java-gaming.org/171be99430117

I find the Observer pattern (EventListeners) quite suitable for this kind of thing.
Online SHC
« Reply #19 - Posted 2014-08-03 04:17:31 »

Go the easy way. JPanel has
setDoubleBuffered(boolean)
method.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
public class GamePanel extends JPanel
{
    // [snip]
    public GamePanel()
    {
        super();

        setDoubleBuffered(true);
        setFocusable(true);
        requestFocus();
    }
    // [snip]
}

Then simply override the paint method and do your rendering.

Online BurntPizza

« JGO Bitwise Duke »


Medals: 291
Exp: 5 years



« Reply #20 - Posted 2014-08-03 04:55:39 »

Go the easy way. JPanel has
setDoubleBuffered(boolean)
method.

You know it's already true by default on a JPanel? (Any other constructor)

I'll assume you haven't read the thread because he had a threading issue, not a buffering one, and the discussion has evolved to a different place anyway.
Online SHC
« Reply #21 - Posted 2014-08-03 05:02:25 »

Sorry, I just read the title, and when scrolling down, I saw long codes, so I made that post. Sorry.

Offline Tekkerue

Senior Devvie


Medals: 6
Projects: 1



« Reply #22 - Posted 2014-08-03 14:23:23 »

SHC,

D’oh! Sorry about that, that was my bad! I didn’t want to create a bunch of new topics on the forum over this so I posted my updated question on the same topic. I just edited the original post to notify you all about the slightly changed topic. Smiley


BurntPizza,

Holy smokes, that is awesome! Grin Wow, I have a lot to learn on this stuff...I spent days trying to get those two stupid dots to move back and forth and you cranked out that awesome animation within the hour. lol

When I do get to making the swinging metronome animation, I would like to use images however rather than drawing with graphics so I can make the animation look “pretty” in photoshop. It’ll probably end up looking something like this...although I’ll probably make my own “less realistic” looking version.


Here is a mobile metronome program that is similar to the kind of animation style that I’m shooting for:
https://www.youtube.com/watch?v=X8zUw1BzVd8

I think I could get something like this to work with the “setIcon” method that I used above, but I don’t know if that’s a good method to use for animation as far as speed, smoothness, etc...I’ve never seen that used in any of the animation tutorials I’ve looked at, so I’m thinking this probably isn’t a recommended method.


Thanks again!
Online BurntPizza

« JGO Bitwise Duke »


Medals: 291
Exp: 5 years



« Reply #23 - Posted 2014-08-03 15:18:18 »

It's possible to use ImageIcons, but yeah, I wouldn't recommend it.

I'd use ImageIO to read in the images into a List<BufferedImage> or something, which is the animation for half a swing (from one side to the other) and ping-pong through the list like so:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
boolean direction = true;
int currentImageIndex = 0;

// a simple form of state machine, sweeps back and forth on the range [0, images.size() - 1]
void update() {
    currentImageUpdate += direction ? 1 : -1;

    if (currentImageIndex == 0) {
        direction = true;
    }
   
    if (currentImageIndex == images.size() - 1) {
        direction = false;
    }
}

public void paintComponent(Graphics g) {
    g.drawImage(background, 0, 0, null);
    g.drawImage(images.get(currentImageIndex), 0, 0, null);
    update();
}


You can add event hooks into update() if needed, like if (currentImageIndex == 0) // fire event listeners to detect one end of the swing.
Offline Tekkerue

Senior Devvie


Medals: 6
Projects: 1



« Reply #24 - Posted 2014-08-03 16:07:40 »

Thanks BurntPizza!

I just tried re-working my original single sprite sheet example (using a BufferedImage) so I could post the code I was trying and I actually got it working this time!!! Smiley

Before I was trying to do it all in a single file and I calling repaint() from within the EDT, but it wasn't working. So instead I tried creating a separate animation JPanel class and this method works now. I’m not sure exactly what I was doing wrong before...but for whatever reason, starting over almost from scratch I finally got it!

Would this method be ok to use for animating more frames?

Thanks again!

[EDIT] CODE UPDATED! SEE NEXT POST...
Offline Tekkerue

Senior Devvie


Medals: 6
Projects: 1



« Reply #25 - Posted 2014-08-03 19:54:09 »

BurntPizza,

Here's an updated version using multiple frames and the method you suggested for switching directions.

I also tweaked it so it's more like an "game loop" with an update and edtPaint methods. One thing I've noticed now is that I'm resetting the background color to the same color for the labels as well as redrawing the same image on the frames where the visuals don't change. Would it be more efficient to set a flag and check to see if it these items need to be updated before setting them again or is it ok to do this kind of constant updating on each frame on the edt?

I just want to make sure I'm not setting myself up for problems down the road as I started adding more stuff to it.

Thanks again!

Test.java
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

import java.awt.image.*;
import javax.imageio.ImageIO;
import java.io.IOException;

public class Test extends JFrame{
   JPanel panel = new JPanel();
   final JLabel label1 = new JLabel("Label 1");
   final JLabel label2 = new JLabel("Label 2");
    AnimationPanel animationPanel;
   
   final Color[] colors = new Color[] { Color.green, Color.yellow };
   final int[] position = { 100, 300 };
   
   int delay = 4;  
   int count = 0;
   int labelIndex = 0;
   
    private BufferedImage animation;
   
   boolean rightDirection = true;
   int currentImageIndex = 2;
   
   public static void main(String[] a) throws InterruptedException {
      Test test = new Test();
   }
   
   public Test() throws InterruptedException {
       try { animation = ImageIO.read(getClass().getResource("dot.jpg")); } catch (IOException ioe) { ioe.printStackTrace(); }
      animationPanel = new AnimationPanel(animation);
      animationPanel.setMinimumSize(new Dimension(400,100));
      animationPanel.setPreferredSize(new Dimension(400,100));
     
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      setSize(410, 300);
      setLocationRelativeTo(null);
     
      panel.setSize(new Dimension(400, 400));
      add(panel);
     
      label1.setMinimumSize(new Dimension(100,100));
      label1.setPreferredSize(new Dimension(100,100));
      label1.setOpaque(true);
     
      label2.setMinimumSize(new Dimension(100,100));
      label2.setPreferredSize(new Dimension(100,100));
      label2.setOpaque(true);
     
      panel.add(label1);
      panel.add(label2);
      panel.add(animationPanel);
     
      setVisible(true);
     
      loop();      
   }
   
   public void loop() throws InterruptedException {
      while (true) {
         update();
         edtPaint();
         Thread.sleep(16);
      }
   }
   
   public void update(){
      if (count % (int)(delay/4) == 0){
         animationPanel.setSubImage(currentImageIndex);
         
         currentImageIndex += rightDirection ? 1 : -1;
         if (currentImageIndex == 0) {
            rightDirection = true;
         }
         if (currentImageIndex == 4) {
            rightDirection = false;
         }
      }
     
      if (count % delay == 0){
         labelIndex++;
      }
     
      count++;
   }
   
   public void edtPaint(){
      final int li = labelIndex % 2;
     
      SwingUtilities.invokeLater(new Runnable(){
         public void run(){
            label1.setBackground(colors[li]);
            label2.setBackground(colors[li]);
            animationPanel.paintSubImage();
         }
      });
   }
}


AnimationPanel.java
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;

public class AnimationPanel extends JPanel{
   BufferedImage img;
   BufferedImage subImg;
   
   public AnimationPanel(BufferedImage imgIn){
      img = imgIn;
      setLayout(null);
   }
   
   public void setSubImage(int frameIn){
      subImg = img.getSubimage(400*frameIn, 0, 400, 100);
   }
   
   public void paintSubImage(){
      repaint();
   }
   
   public void paintComponent(Graphics g){
      g.drawImage(subImg, 0, 0, null);      
   }
}


dot.jpg
Online BurntPizza

« JGO Bitwise Duke »


Medals: 291
Exp: 5 years



« Reply #26 - Posted 2014-08-03 20:46:52 »

Yeah it doesn't really matter if you set the background color frequently, it's not an expensive operation. If you want to add the logic to do it only when necessary for peace of mind, do it.
Only things really left for me to complain about are things you get with experience, like a developed sense of code conventions/style/formatting. Although Eclipse does have an on-save auto-formatter, imports organizer, and some other stuff I recommend you check out.
Offline Tekkerue

Senior Devvie


Medals: 6
Projects: 1



« Reply #27 - Posted 2014-08-03 20:58:48 »

Alright, thanks a lot BurntPizza!

I'm currently using a very simple Java IDE called jGrasp. When I start working on my actual program I'll be using NetBeans since that was recommended for use with Codename One, does NetBeans have a similar kind of auto-formatting feature? And just so I'm aware of them, what kinds of formatting issues have you noticed in my sample code?

Thanks again for your help! Smiley
Online BurntPizza

« JGO Bitwise Duke »


Medals: 291
Exp: 5 years



« Reply #28 - Posted 2014-08-03 21:28:25 »

http://stackoverflow.com/questions/956900/code-cleanup-in-netbeans

Eh, just some (mostly) little things:

  • Some lines are indented 1 space more than the rest
  • Some operators/commas aren't spaced while most are:
    (delay/4) == 0
  • try/catch all on one line
  • Imports and members not sorted
  • Inconsistency in field modifiers (some final, some not, some private, some not)
  • Inconsistency in field initialization (some in declaration, some in Test constructor)
  • Poor encapsulation: AnimationPanel is it's own class, but majority of it's initialization and logic is handled externally
  • Exceptions not being handled
  • Unnecessary methods (paintSubImage, etc.) (related to encapsulation)
  • Unnecessary repetition: label1, label2

Random things I could think of looking at it, goes from quite nit-picky to obvious to fairly advanced. I don't expect most of this from a simple demo/test program, sure, and some of it like I said you only gain from experience.
Refactoring is your friend! Try to develop good architectural and style habits.
Offline Tekkerue

Senior Devvie


Medals: 6
Projects: 1



« Reply #29 - Posted 2014-08-03 22:03:27 »

Ok, thanks a lot BurntPizza! Those are great things to be on the look out for. I will say that most of them were neglected only because this was a quick example to post on the forum and not things I intend to do in my real program. I did a lot of hacking, pasting, and moving things around so sometimes I didn't correct every detail just for the sake of time.

Thank you again for your help! I'm really glad I found this forum, you all are awesome! Smiley
Pages: [1]
  ignore  |  Print  
 
 

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

rwatson462 (38 views)
2014-12-15 09:26:44

Mr.CodeIt (31 views)
2014-12-14 19:50:38

BurntPizza (62 views)
2014-12-09 22:41:13

BurntPizza (99 views)
2014-12-08 04:46:31

JscottyBieshaar (60 views)
2014-12-05 12:39:02

SHC (74 views)
2014-12-03 16:27:13

CopyableCougar4 (77 views)
2014-11-29 21:32:03

toopeicgaming1999 (138 views)
2014-11-26 15:22:04

toopeicgaming1999 (127 views)
2014-11-26 15:20:36

toopeicgaming1999 (38 views)
2014-11-26 15:20:08
Resources for WIP games
by kpars
2014-12-18 10:26:14

Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!