如何在Swing中逐渐旋转图像?

时间:2010-08-04 13:09:04

标签: java swing rotation

当用户点击按钮时,我有一个正在旋转的图像。但它没有用。

我希望看到图像逐渐旋转到90度直到它停止但它没有。单击按钮时,图像必须逐渐旋转90度。

我创建了一个SSCCE来演示这个问题。请将CrossingPanelSSCE课程中的图片替换为您选择的任何图片。只需将图片放入images文件夹并将其命名为images/railCrossing.JPG

RotateButtonSSCE

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();
    }

}

CrossingPanelSSCE

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;
    }



}

VisualizationPanelSSCE

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);
        }


    }

TestGUISSCE它包含主要方法。

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();
    }
}

3 个答案:

答案 0 :(得分:25)

除了@ tulskiy的有用观察之外,我还要补充两点:

  1. 始终在event dispatch thread上构建您的GUI,如下所示。

  2. sscce应该是短,自包含,正确(可编译),示例。为方便起见,不要求其他人重新创建多个公共类;使用顶级(包私有)或嵌套类。由于这是一个图形问题,请使用反映您问题的公共或合成图像。

  3. 在下面的示例中,paintComponent()会更改图形上下文的变换以实现旋转。请注意,操作是在声明顺序的(明显)反向执行的:首先,图像的中心被转换为原点;第二,图像旋转;第三,图像的中心被转换为面板的中心。您可以通过调整面板大小来查看效果。

    附录:请参阅此替代approach使用AffineTransform

    image

    package overflow;
    
    import java.awt.*;
    import java.awt.event.*;
    import java.awt.image.BufferedImage;
    import java.util.Random;
    import javax.swing.*;
    
    /**
     * @see https://stackoverflow.com/questions/3371227
     * @see https://stackoverflow.com/questions/3405799
     */
    public class RotateApp {
    
        private static final int N = 3;
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    JFrame frame = new JFrame();
                    frame.setLayout(new GridLayout(N, N, N, N));
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    for (int i = 0; i < N * N; i++) {
                        frame.add(new RotatePanel());
                    }
                    frame.pack();
                    frame.setVisible(true);
                }
            });
        }
    }
    
    
    class RotatePanel extends JPanel implements ActionListener {
    
        private static final int SIZE = 256;
        private static double DELTA_THETA = Math.PI / 90;
        private final Timer timer = new Timer(25, this);
        private Image image = RotatableImage.getImage(SIZE);
        private double dt = DELTA_THETA;
        private double theta;
    
        public RotatePanel() {
            this.setBackground(Color.lightGray);
            this.setPreferredSize(new Dimension(
                image.getWidth(null), image.getHeight(null)));
            this.addMouseListener(new MouseAdapter() {
    
                @Override
                public void mousePressed(MouseEvent e) {
                    image = RotatableImage.getImage(SIZE);
                    dt = -dt;
                }
            });
            timer.start();
        }
    
        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            g2d.translate(this.getWidth() / 2, this.getHeight() / 2);
            g2d.rotate(theta);
            g2d.translate(-image.getWidth(this) / 2, -image.getHeight(this) / 2);
            g2d.drawImage(image, 0, 0, null);
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            theta += dt;
            repaint();
        }
    
        @Override
        public Dimension getPreferredSize() {
            return new Dimension(SIZE, SIZE);
        }
    
    }
    
    class RotatableImage {
    
        private static final Random r = new Random();
    
        static public Image getImage(int size) {
            BufferedImage bi = new BufferedImage(
                size, size, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = bi.createGraphics();
            g2d.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setPaint(Color.getHSBColor(r.nextFloat(), 1, 1));
            g2d.setStroke(new BasicStroke(size / 8));
            g2d.drawLine(0, size / 2, size, size / 2);
            g2d.drawLine(size / 2, 0, size / 2, size);
            g2d.dispose();
            return bi;
        }
    }
    

答案 1 :(得分:5)

Rotated Icon的代码使用AffineTransform围绕其中心旋转。

答案 2 :(得分:4)

this.crossingP.paintComponent(comp2D);

永远不要这样做!您的CrossingPane未添加到任何组件,因此repaint()没有任何效果。您可以通过在paintComponent()方法中添加打印来检查它。因此,您需要将CrossingPane添加到VisualizationPane:

setLayout(new BorderLayout());
add(crossingP, BorderLayout.CENTER);

图像居中存在一些问题,但这并不难解决。

PS。再读一遍布局和绘画。