Skip to content
Advertisement

Java Rotating Icon in JLabel

Hi I’m having a problem trying to rotate an image inside a JLabel. I got this code from StackOverflow, and I’m trying to change it a little bit so that instead of the image rotating in a Tab, it is rotating within a JLabel.

public class ProgressTabbedPane {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame("RotatingIcon"); 
                JTabbedPane tabbedPane = new JTabbedPane();
                JLabel lable = new JLabel();
              

                
                tabbedPane.addTab("Searching", new RotatingIcon(new ImageIcon("disk.png"), tabbedPane, 10), new JLabel( /*new ImageIcon( "resources/images/rotatingIcon.gif" )*/));               
                frame.getContentPane().add(tabbedPane);                
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                //frame.setUndecorated(true);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

            }
        });
    }

    private static class RotatingIcon implements Icon {

        private final Icon delegateIcon;
        private double angleInDegrees = 90;
        final private Timer rotatingTimer;

        private RotatingIcon(Icon icon, final JComponent component, int vrotating) {
            delegateIcon = icon;
            rotatingTimer = new Timer(vrotating, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    
                    angleInDegrees = angleInDegrees + 1;
                    if (angleInDegrees == 360) {
                        angleInDegrees = 0;
                    }
                    component.repaint();
                
                }
            });
            rotatingTimer.setRepeats(false);
            rotatingTimer.start();

        }

        @Override
        public void paintIcon(Component c, Graphics g, int x, int y) {
            rotatingTimer.stop();
            Graphics2D g2 = (Graphics2D) g.create();
            int cWidth = delegateIcon.getIconWidth() / 2;
            int cHeight = delegateIcon.getIconHeight() / 2;
            Rectangle r = new Rectangle(x, y, delegateIcon.getIconWidth(), delegateIcon.getIconHeight());
            g2.setClip(r);
            AffineTransform original = g2.getTransform();
            AffineTransform at = new AffineTransform();
            at.concatenate(original);
            at.rotate(Math.toRadians(angleInDegrees), x + cWidth, y + cHeight);
            g2.setTransform(at);
            delegateIcon.paintIcon(c, g2, x, y);
            g2.setTransform(original);
            rotatingTimer.start();
        }

        @Override
        public int getIconWidth() {
            return delegateIcon.getIconWidth();
        }

        @Override
        public int getIconHeight() {
            return delegateIcon.getIconHeight();
        }
    }
}

This is working, the image is rotating. However when i change it to this.

public class ProgressTabbedPane {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame("RotatingIcon"); 
                JTabbedPane tabbedPane = new JTabbedPane();
                JLabel lable = new JLabel();
              

                
                
                lable.setIcon(new RotatingIcon(new ImageIcon(disk.png"), tabbedPane, 10));                
                frame.getContentPane().add(lable);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                //frame.setUndecorated(true);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

            }
        });
    }

    private static class RotatingIcon implements Icon {

        private final Icon delegateIcon;
        private double angleInDegrees = 90;
        final private Timer rotatingTimer;

        private RotatingIcon(Icon icon, final JComponent component, int vrotating) {
            delegateIcon = icon;
            rotatingTimer = new Timer(vrotating, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    
                    angleInDegrees = angleInDegrees + 1;
                    if (angleInDegrees == 360) {
                        angleInDegrees = 0;
                    }
                    component.repaint();
                
                }
            });
            rotatingTimer.setRepeats(false);
            rotatingTimer.start();

        }

        @Override
        public void paintIcon(Component c, Graphics g, int x, int y) {
            rotatingTimer.stop();
            Graphics2D g2 = (Graphics2D) g.create();
            int cWidth = delegateIcon.getIconWidth() / 2;
            int cHeight = delegateIcon.getIconHeight() / 2;
            Rectangle r = new Rectangle(x, y, delegateIcon.getIconWidth(), delegateIcon.getIconHeight());
            g2.setClip(r);
            AffineTransform original = g2.getTransform();
            AffineTransform at = new AffineTransform();
            at.concatenate(original);
            at.rotate(Math.toRadians(angleInDegrees), x + cWidth, y + cHeight);
            g2.setTransform(at);
            delegateIcon.paintIcon(c, g2, x, y);
            g2.setTransform(original);
            rotatingTimer.start();
        }

        @Override
        public int getIconWidth() {
            return delegateIcon.getIconWidth();
        }

        @Override
        public int getIconHeight() {
            return delegateIcon.getIconHeight();
        }
    }
}

This stopped working, I’m sorry if it is a stupid question, but I don’t seem to find the answer.

Thank you

Advertisement

Answer

A painting method should only be used to paint something. It should not stop/start a Timer. So I would guess that you need get rid of the Timer logic from the painting method and then set the Timer to repeat so that continuous events are generated.

For an alternative approach check out the Animated Icon.

The Animated Icon will hold a list of Icons to display sequentially based on a Timer. When the Timer fires the next Icon in the cycle is displaye. You can configure the animation to be continuous or specify the number of cycles to display each Icon.

Note: this solution should be more efficient because it only repaints the Icon, not the entire Component.

If you don’t like the idea of creating all the Icons for the animation then you can use the Rotated Icon. With this class you set the degrees of rotation for the Icon. Then the Timer becomes completely separated from the class. Then when the Timer code fires you would increment the degrees of rotation.

Simple example using the AnimatedIcon:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class SSCCE extends JPanel
{
    public SSCCE()
    {
        setLayout( new BorderLayout() );

        JTabbedPane tabbedPane = new JTabbedPane();
        tabbedPane.add("1", new JPanel());
        tabbedPane.add("2", new JPanel());
        add(tabbedPane);

        AnimatedIcon icon = new AnimatedIcon(tabbedPane, 250, 3);
        ImageIcon duke = new ImageIcon( "copy16.gif" );
        icon.addIcon( duke );

        for (int angle = 30; angle < 360; angle += 30)
        {
            icon.addIcon( new RotatedIcon(duke, angle) );
        }

        tabbedPane.setIconAt(0, icon);
        icon.start();
    }


    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("SSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new SSCCE());
        frame.setLocationByPlatform( true );
        frame.pack();
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater( () -> createAndShowGUI() );
/*
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
*/
    }
}
Advertisement