title: How to rotate an image gradually in Swing?
body: <p>I have an image I am rotating when the user clicks on a button. But it is not working.</p>
  
  <p>I would like to see the image rotating gradually to 90 degrees till it stops but it doesn't. The image must rotate 90 degrees gradually when the button is clicked.</p>
  
  <p>I have created an SSCCE to demonstrate the problem. Please replace the image in the <code>CrossingPanelSSCE</code> class with any image of your choice. Just put the image in your <code>images</code> folder and name it <code>images/railCrossing.JPG</code>.</p>
  
  <p><code>RotateButtonSSCE</code></p>
  
  <pre><code>import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import javax.swing.Action;
  import javax.swing.BorderFactory;
  import javax.swing.JButton;
  import javax.swing.JPanel;
  
  public class RotateButtonSSCE extends JPanel implements ActionListener{
        private JButton rotate = new JButton("Rotate");
        private VisualizationPanelSSCE vis = new VisualizationPanelSSCE();
  
      public RotateButtonSSCE() {
          this.setBorder(BorderFactory.createTitledBorder("Rotate Button "));
          this.rotate.addActionListener(this);
          this.add(rotate);
      }
  
      public void actionPerformed(ActionEvent ev) {
          vis.rotatetheCrossing();
      }
  
  }
  </code></pre>
  
  <p><code>CrossingPanelSSCE</code></p>
  
  <pre><code>import java.awt.Color;
  import java.awt.Dimension;
  import java.awt.FlowLayout;
  import java.awt.Graphics;
  import java.awt.Graphics2D;
  import java.awt.Image;
  import java.awt.Rectangle;
  import java.awt.Toolkit;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.geom.AffineTransform;
  
  import javax.swing.BorderFactory;
  import javax.swing.JPanel;
  import javax.swing.border.TitledBorder;
  
  public class CrossingPanelSSCE  extends JPanel{
  
      private static final long serialVersionUID = 1L;
  
      // private data members
       private Image crossingImage;
       private int currentRotationAngle;
       private int imageWidth;
       private int imageHeight;
       private AffineTransform affineTransform;
       private boolean clockwise; 
       private static int ROTATE_ANGLE_OFFSET = 2;
  
       private int xCoordinate;
       private int yCoordinate;
  
       private static javax.swing.Timer timer;
  
       private void initialize(){
           this.crossingImage = Toolkit.getDefaultToolkit().getImage("images/railCrossing.JPG");
           this.imageWidth = this.getCrossingImage().getWidth(this);
           this.imageHeight = this.getCrossingImage().getHeight(this);
           this.affineTransform = new AffineTransform();
           currentRotationAngle = 90;
           timer = new javax.swing.Timer(20, new MoveListener());
       } 
  
      public CrossingPanelSSCE(int x, int y) {
          this.setxCoordinate(x);
          this.setyCoordinate(y);
          this.setPreferredSize(new Dimension(50, 50));
          this.setBackground(Color.red);
          TitledBorder border = BorderFactory.createTitledBorder("image");
          this.setLayout(new FlowLayout());
          this.initialize();
  
      }
  
  
      public void paintComponent(Graphics grp){ 
  
          Rectangle rect = this.getBounds();
          Graphics2D g2d = (Graphics2D)grp;
          g2d.setColor(Color.BLACK);
          this.getAffineTransform().setToTranslation(this.getxCoordinate(), this.getyCoordinate());
  
            //rotate with the rotation point as the mid of the image
          this.getAffineTransform().rotate(Math.toRadians(this.getCurrentRotationAngle()), this.getCrossingImage().getWidth(this) /2, 
                                           this.getCrossingImage().getHeight(this)/2);
  
          //draw the image using the AffineTransform
          g2d.drawImage(this.getCrossingImage(), this.getAffineTransform(), this);
      }
  
  
      public  void rotateCrossing(){
          System.out.println("CurrentRotationAngle: " + currentRotationAngle);
          this.currentRotationAngle += ROTATE_ANGLE_OFFSET;
          //int test = currentRotationAngle % 90;
          if(currentRotationAngle % 90 == 0){
           setCurrentRotationAngle(currentRotationAngle);
           timer.stop();           
          }
  
           //repaint the image panel
           repaint(); 
      }
  
  
      void start() {
          if (timer != null) {
              timer.start();
          }
      }
  
  
       private class MoveListener implements ActionListener {
  
              public void actionPerformed(ActionEvent e) {
                 rotateCrossing();
          }
  
       }
  
      public Image getCrossingImage() {
          return crossingImage;
      }
      public void setCrossingImage(Image crossingImage) {
          this.crossingImage = crossingImage;
      }
  
      public int getCurrentRotationAngle() {
          return currentRotationAngle;
      }
      public void setCurrentRotationAngle(int currentRotationAngle) {
          this.currentRotationAngle = currentRotationAngle;
      }
  
      public int getImageWidth() {
          return imageWidth;
      }
      public void setImageWidth(int imageWidth) {
          this.imageWidth = imageWidth;
      }
  
      public int getImageHeight() {
          return imageHeight;
      }
      public void setImageHeight(int imageHeight) {
          this.imageHeight = imageHeight;
      }
  
      public AffineTransform getAffineTransform() {
          return affineTransform;
      }
      public void setAffineTransform(AffineTransform affineTransform) {
          this.affineTransform = affineTransform;
      }
  
      public boolean isClockwise() {
          return clockwise;
      }
      public void setClockwise(boolean clockwise) {
          this.clockwise = clockwise;
      }
  
      public int getxCoordinate() {
          return xCoordinate;
      }
      public void setxCoordinate(int xCoordinate) {
          this.xCoordinate = xCoordinate;
      }
  
      public int getyCoordinate() {
          return yCoordinate;
      }
      public void setyCoordinate(int yCoordinate) {
          this.yCoordinate = yCoordinate;
      }
  
      public javax.swing.Timer getTimer() {
          return timer;
      }
      public void setTimer(javax.swing.Timer timer) {
          this.timer = timer;
      }
  
  
  
  }
  </code></pre>
  
  <p><code>VisualizationPanelSSCE</code></p>
  
  <pre><code>import gui.CrossingPanel;
  import java.awt.BasicStroke;
  import java.awt.Color;
  import java.awt.Dimension;
  import java.awt.Graphics;
  import java.awt.Graphics2D;
  import java.awt.RenderingHints;
  import java.awt.Shape;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.geom.GeneralPath;
  
  import javax.swing.BorderFactory;
  import javax.swing.JPanel;
  import javax.swing.SwingUtilities;
  import javax.swing.border.TitledBorder;
  
  import application.Robot2;
  
  public class VisualizationPanelSSCE extends JPanel{
  
  
          //private data members
          private GeneralPath path;
          private Shape horizontalRail;
          private Shape verticalRail;
          private static int LENGTH = 350;
          private CrossingPanelSSCE crossingP;
  
  
  
           private void initializeComponents(){
               this.path = new GeneralPath();
               this.horizontalRail = this.createHorizontalRail();
               this.verticalRail = this.createVerticalRail();
               this.crossingP = new CrossingPanelSSCE(328,334);
           }
  
          public VisualizationPanelSSCE(){ 
              this.initializeComponents();
              this.setPreferredSize(new Dimension(400,400));
               TitledBorder border = BorderFactory.createTitledBorder("Rotation");
               this.setBorder(border);
  
          }
  
          public GeneralPath getPath() {
              return path;
          }
          public void setPath(GeneralPath path) {
              this.path = path;
          }
  
  
          private Shape createHorizontalRail(){
              this.getPath().moveTo(5, LENGTH);
              this.getPath().lineTo(330, 350);
              this.getPath().closePath();
              return this.getPath();
          }
  
          private Shape createVerticalRail(){
              this.getPath().moveTo(350, 330);
              this.getPath().lineTo(350,10);
              this.getPath().closePath();
              return this.getPath();
          }
  
  
          public void paintComponent(Graphics comp){
               super.paintComponent(comp); 
              Graphics2D comp2D = (Graphics2D)comp;
              BasicStroke pen = new BasicStroke(15.0F, BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND);
  
              comp2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                                      RenderingHints.VALUE_ANTIALIAS_ON);
              comp2D.setPaint(Color.black);
              comp2D.setBackground(Color.WHITE);
              comp2D.draw(this.horizontalRail);
              this.crossingP.paintComponent(comp2D);
          }
  
  
          public CrossingPanelSSCE getCrossingP() {
              return crossingP;
          }
          public void setCrossingP(CrossingPanelSSCE crossingP) {
              this.crossingP = crossingP;
          }
  
          public void rotatetheCrossing(){
  
               Runnable rotateCrossing1 = new Runnable(){  
                  public void run() {
                    crossingP.start();
                }
            };
              SwingUtilities.invokeLater(rotateCrossing1);
          }
  
  
      }
  </code></pre>
  
  <p><code>TestGUISSCE</code> it contains the main method.</p>
  
  <pre><code>import java.awt.Dimension;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.event.MouseAdapter;
  import java.awt.event.MouseEvent;
  import java.util.Random;
  
  import javax.swing.*;
  
  public class TestGUISSCE{
      private RotateButtonSSCE rotate = new RotateButtonSSCE();
      private VisualizationPanelSSCE vision = new VisualizationPanelSSCE();
  
      public void createGui(){
  
           JFrame frame = new JFrame("Example");
           frame.setSize(new Dimension(500, 500));
  
  
           JPanel pane = new JPanel();
           pane.add(this.vision);
           pane.add(rotate);  
           frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
           frame.add(pane);
           frame.setVisible(true);
  
      }
  
      public static void main(String[] args) {
          new TestGUISSCE().createGui();
      }
  }
  </code></pre>