1

I'm making a Java App and I'd like to know if there is a way to make the repaint on jframe slower. Let me elaborate, the app has a jframe and calls another jframe to display a graphic that has to move from side to side but it can do it a speed that you can actually see the movement.

Edit* In order to get my self clear, I'm adding a piece of code.

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


public class NewJFrame extends javax.swing.JFrame {

    /**
     * Creates new form NewJFrame
     */
    public NewJFrame() {
        initComponents();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        jButton1 = new javax.swing.JButton();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTextArea1 = new javax.swing.JTextArea();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jButton1.setText("Start");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        jTextArea1.setColumns(20);
        jTextArea1.setRows(5);
        jScrollPane1.setViewportView(jTextArea1);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1)
                .addContainerGap())
            .addGroup(layout.createSequentialGroup()
                .addGap(152, 152, 152)
                .addComponent(jButton1)
                .addContainerGap(191, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(47, 47, 47)
                .addComponent(jButton1)
                .addGap(18, 18, 18)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 149, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(63, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
        String position="";
        JFrame frame1=new JFrame();
        JLabel label1=new JLabel();
        label1.setText("Hello!");
        frame1.add(label1);
        frame1.setSize(400,300);
        frame1.setVisible(true);
        for(int i=1;i<20;i++){
            label1.setLocation(i, i);
            frame1.repaint();
            position+="The label is at position: "+label1.getLocation()+"\n";
            jTextArea1.setText(position);
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
               NewJFrame frame= new NewJFrame();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify
    private javax.swing.JButton jButton1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTextArea jTextArea1;
    // End of variables declaration
}

What I need is to get the frame1 to show an animation of the label1 positioning and to get a line of position printed in the textarea each time it changes. Thanks

Diego S
  • 9
  • 2
  • 5
    use `javax.swing.Timer` for issuing repaints. Check **[this](http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html)** page. – Extreme Coders Feb 03 '13 at 15:54
  • 4
    I second @ExtremeCoders recommendation. The reason that `Thread.sleep(...)` doesn't work is because you put all of Swing painting and user interaction to sleep when you do this. Never use `Thread.sleep(...)` on the event thread, and never call any long-running code on the event thread much less the paint or paintComponent method. – Hovercraft Full Of Eels Feb 03 '13 at 16:01
  • Consider posting an [SSCCE](http://sscce.org) that shows a basic example of your problem/ – Guillaume Polet Feb 03 '13 at 17:44
  • 1
    possible duplicate of [Threading a paint method](http://stackoverflow.com/questions/14072940/threading-a-paint-method) – David Kroukamp Feb 03 '13 at 17:50
  • 1
    @DavidKroukamp possible duplicate to any *Swing Animation* question ;) - nice link though – MadProgrammer Feb 03 '13 at 21:38
  • *"the app has a jframe and calls another jframe"* See [The Use of Multiple JFrames, Good/Bad Practice?](http://stackoverflow.com/a/9554657/418556) – Andrew Thompson Feb 03 '13 at 23:49

1 Answers1

3
  1. sleep should be strictly forbidden on the EDT
  2. If you feel like you want to use sleep, you should think directly to a Swing Timer

Now, if you want to see an example of how you can easily change the speed of a moving element, you don't have to alter the frequency of the Swing Timer but actually change the speed of the moving elements. A 50Hz frequency is usually enough for a human being.

Here is a small demo code:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class TestAnimation2 {

    private static final int NB_OF_IMAGES_PER_SECOND = 50;
    private static final int WIDTH = 800;
    private static final int HEIGHT = 600;

    private static final int MIN = 0;
    private static final int MAX = 100;

    private double speed = convert(50);

    private double dx;
    private double dy;

    private double x = WIDTH / 2;
    private double y = HEIGHT / 2;

    private JFrame frame;
    private CirclePanel circle;
    private Runnable job;

    private long lastMove = System.currentTimeMillis();

    protected void initUI() throws MalformedURLException {
        frame = new JFrame(TestAnimation2.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(null);
        circle = new CirclePanel();
        circle.setSize(20, 20);
        frame.add(circle);
        frame.setSize(WIDTH, HEIGHT);
        dx = speed;
        dy = speed;
        final JSlider slider = new JSlider(MIN, MAX);
        slider.addChangeListener(new ChangeListener() {

            @Override
            public void stateChanged(ChangeEvent e) {
                speed = convert(slider.getValue());
                if (dx > 0) {
                    dx = speed;
                } else {
                    dx = -speed;
                }
                if (dy > 0) {
                    dy = speed;
                } else {
                    dy = -speed;
                }

            }
        });
        slider.setValue(50);
        slider.setLocation(0, 0);
        slider.setSize(slider.getPreferredSize());
        frame.add(slider);
        frame.setVisible(true);
        Timer t = new Timer(1000 / NB_OF_IMAGES_PER_SECOND, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                move();
            }
        });
        t.start();
    }

    protected double convert(double sliderValue) {
        return sliderValue + 1;
    }

    protected void move() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                x += dx;
                y += dy;
                if (x + circle.getWidth() > frame.getContentPane().getWidth()) {
                    x = frame.getContentPane().getWidth() - circle.getWidth();
                    dx = -speed;
                } else if (x < 0) {
                    x = 0;
                    dx = speed;
                }
                if (y + circle.getHeight() > frame.getContentPane().getHeight()) {
                    y = frame.getContentPane().getHeight() - circle.getHeight();
                    dy = -speed;
                } else if (y < 0) {
                    y = 0;
                    dy = speed;
                }
                circle.setLocation((int) x, (int) y);
                circle.repaint();
            }
        });
    }

    public static class CirclePanel extends JPanel {

        public CirclePanel() {
            super();
            setOpaque(false);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.RED);
            g.fillOval(0, 0, getWidth(), getHeight());
        }
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
            UnsupportedLookAndFeelException {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                try {
                    new TestAnimation2().initUI();
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
  • Is it necessary to wrap the code in `move()` with `SwingUtilities#invokeLater()`? The action listener already runs on the EDT. – lhballoti Feb 03 '13 at 19:36
  • @lhballoti No, it is not necessary (it is event a bit less efficient). This is because I used the same code with a ThreadPool which required it. – Guillaume Polet Feb 03 '13 at 19:39
  • Hi, first of all thanks a lot for answering my question. Now I have read about the Timer in swing and understood that when the timer goes off the action in the actionperformed takes place. Well what I need is to set the jdf.repaint in this delay in order to give an animation to the jdf frame. But I can't make it work. Help me Please!! – Diego S Feb 04 '13 at 21:34
  • @DiegoS Would love to help you, but without seeing complete, executable code, it is really hard for me to help you. Update your code with an [SSCCE](http://sscce.org). – Guillaume Polet Feb 04 '13 at 23:11
  • The problem is that this is only a window part of a project, and also it gets values of other windows. So it's kind of hard to give you just a part of the code and also it would be a mess beceause i'm using Java FX 2 for the design – Diego S Feb 04 '13 at 23:32
  • The basic problem is that the jdf is a frame which is invoked and that i would like to get refreshed with delay trough the timer. Note that the jdf get his updated values from the for block – Diego S Feb 04 '13 at 23:34
  • @DiegoS An [SSCCE](http://sscce.org) does not mean that you have to extract precisely the code where the problem occurs. You could just provide a very basic example of what you are trying to achieve, we would then ty to answer your question and all that would be left is to reproduce the same in your original program. – Guillaume Polet Feb 04 '13 at 23:34
  • @DiegoS Just a wild guess, pure shot in the dark: extract everything that is in your for-loop into another method. Then instead of calling your for-lopp, call a Swing Timer with an `ActionListener` that would call the method previously created. – Guillaume Polet Feb 04 '13 at 23:48
  • Guillaume now theres a example of what I need to get trough. The for-loop is a process of evaluation from which the jdf gets the values in order to do the repaint. I could get it in a method but if call it from the timer how could I control the times the timer executes ?? Thanks a lot by the way – Diego S Feb 05 '13 at 00:03
  • Like you did in your for-loop: create a variable that will count the number of times the method has been called. Everytime you invoke the method, increment that variable. Whenever you have reached the number of times you want to call your method, you can call [`stop()`](http://docs.oracle.com/javase/6/docs/api/javax/swing/Timer.html#stop()) on your timer. Btw, you can pass a `null` ActionListener to the Timer constructor and add the ActionListener after with [`addActionListener`](http://docs.oracle.com/javase/6/docs/api/javax/swing/Timer.html#addActionListener(java.awt.event.ActionListener)) – Guillaume Polet Feb 05 '13 at 00:08
  • Could you please add an example? I can't get this control part right, on the other hand I have been able to create the animation like you said. – Diego S Feb 05 '13 at 00:22
  • I have tried to do this but the variable I declare outside the timer doesn't update after the timer increments it, so it has the same value outside the timer. – Diego S Feb 05 '13 at 00:41