一尘不染

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

java

当用户单击按钮时,我正在旋转图像。但这是行不通的。

我想看到图像逐渐旋转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();
    }
}

阅读 857

收藏
2020-03-01

共1个答案

一尘不染

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

始终在事件分发线程上构造GUI ,如下所示。

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

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

附录:另请参见使用的另一种方法AffineTransform

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;
    }
}
2020-03-01