【问题标题】:Rotating a wheel in Java using Swing and Graphics2D?使用 Swing 和 Graphics2D 在 Java 中旋转轮子?
【发布时间】:2015-04-16 23:10:56
【问题描述】:

我正在研究一个可以围绕中心旋转轮子的类。轮子是使用 graphics2d 创建的,但我不知道如何让轮子围绕中心旋转。 目前,轮子旋转,但不完全围绕原点旋转。

我在这里的最终目标是创建彩色轮子以及围绕它的程序,但我主要关心的是让旋转轮子工作。如果您能指出我正确的方向,我将永远感激不尽!

这是我当前的代码:

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.*;


public class RotateApp {

private static final int N = 3;

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {

        public void run() {
            JFrame frame = new JFrame();
            frame.setLayout(new GridLayout(N, N, N, N));
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(new RotatePanel());
            frame.pack();
            frame.setVisible(true);
            System.out.println();
        }
    });
}
}


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


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

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(10.0f));
    g2d.draw(new Line2D.Double(0, 100, 100, 100));
    g2d.draw(new Line2D.Double(100, 100, 200, 100));

    g2d.draw(new Line2D.Double(100, 0, 100, 100));
    g2d.draw(new Line2D.Double(100, 100, 100, 200));

    g2d.draw(new Line2D.Double(25, 25, 100, 100));
    g2d.draw(new Line2D.Double(100, 100, 175, 175));

    g2d.draw(new Line2D.Double(175, 25, 100, 100));
    g2d.draw(new Line2D.Double(100, 100, 25, 175));

    g2d.draw(new Ellipse2D.Double(0, 0, 200, 200));
    g2d.dispose();
    return bi;
}
}

【问题讨论】:

  • 先平移,后旋转。更好的选择,使用AffineTransform
  • 对于exampleexampleexample。是的,我知道这些示例旋转图像,但概念是相同的
  • 修改Graphics对象的状态是一件危险的事情,一般来说Graphics上下文是一个共享资源,这意味着你对它所做的任何修改都会传递给组件被画。而是使用Graphics#create(并将其转换为Graphics2D)来创建Graphics 上下文属性的快照并对其进行操作。完成后,在副本上致电Graphics#dispose
  • 感谢您的回复 :) 我是一名初级程序员,所以我需要一段时间才能将您所说的内容付诸实践。话虽如此,我会努力的!
  • 快速解决方案,交换 translate 和 rotate 调用,让你先翻译

标签: java swing rotation


【解决方案1】:

您可以使用Rotated Icon 类为您进行轮换,这样您就不必担心所有的轮换逻辑,并且轮换逻辑在一个可重用的类中。

使用这个类的一个例子是:

import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.util.*;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;

public class Rotation4 extends JPanel
{
    private JLabel label;
    private RotatedIcon rotated;
    private int degrees;

    public Rotation4(Image image)
    {
        setLayout( new GridBagLayout() );

        Icon icon = new ImageIcon( image );
        rotated = new RotatedIcon(icon, 0);
        rotated.setCircularIcon(true);
        label = new JLabel(rotated);
        label.setOpaque(true);
        label.setBackground(Color.RED);
        add(label, new GridBagConstraints());
        setDegrees( 0 );
    }

    public void setDegrees(int degrees)
    {
        this.degrees = degrees;
        rotated.setDegrees( degrees );
        label.revalidate();
        label.repaint();
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                Image bi = RotatableImage.getImage(210);
                final Rotation4 r = new Rotation4(bi);

                    final JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 0);
                    slider.addChangeListener(new ChangeListener()
                    {
                        public void stateChanged(ChangeEvent e)
                        {
                            int value = slider.getValue();
                            r.setDegrees( value );
                        }
                    });

                JFrame f = new JFrame();
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.add(new JScrollPane(r));
                f.add(slider, BorderLayout.SOUTH);
                f.setSize(400, 400);
                f.setLocationRelativeTo(null);
                f.setVisible(true);
            }
        });
    }

    static 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(10.0f));

            g2d.draw(new Line2D.Double(5, 105, 205, 105));
            g2d.draw(new Line2D.Double(105, 5, 105, 205));
            g2d.draw(new Line2D.Double(35, 35, 175, 175));
            g2d.draw(new Line2D.Double(175, 35, 35, 175));

            g2d.draw(new Ellipse2D.Double(5, 5, 199, 199));

            g2d.setColor(Color.BLACK);
            g2d.fillOval(100, 100, 10, 10);

            g2d.dispose();
            return bi;
        }
    }
}

请注意,我还必须对您的图像和绘画进行更改。无论您是使用 RotatedIcon 还是自己执行旋转代码,都需要进行这些更改:

  1. 图片大小改为210。这是因为你的笔画大小是10,所以你需要考虑圆形轮廓中多余的像素。

  2. 你需要把原来的圆改变一半的笔画大小。所以在这种情况下,原点变为 (5, 5)。

  3. 椭圆的大小需要更改为 199。这是因为椭圆轮廓的绘制方式。轮廓需要 1 个额外的像素。如果将大小保留为 200,则轮廓的 1 个像素将丢失。这在使用大小为 10 的笔划时不是很明显,但如果您使用大小为 1,则在右边缘和下边缘会丢失轮廓。

  4. 您的线路位置需要更改。你不希望这条线正好在圆的边缘,因为那样你会在边缘得到一条平线而不是圆线。所以我从头开始 5 个像素的线开始,从最后 5 个像素结束。

【讨论】:

    【解决方案2】:

    好的,几乎没有修改和更少的“辐条”,我让你的轮子旋转为中心(1.)和多色(2.):


    更新 1.) 要在您的(原始)代码中进行中心旋转,只需将 SIZE更改为 200!

    import java.awt.BasicStroke;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GridLayout;
    import java.awt.Image;
    import java.awt.RenderingHints;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.geom.Ellipse2D;
    import java.awt.geom.Line2D;
    import java.awt.image.BufferedImage;
    import java.util.Random;
    import javax.swing.*;
    
    public class RotateApp {
    
        private static final int N = 3;
    
        public static void main(String[] args) {
            EventQueue.invokeLater(() -> {
                JFrame frame = new JFrame();
                frame.setLayout(new GridLayout(N, N, N, N));
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new RotatePanel());
                frame.pack();
                frame.setVisible(true);
                System.out.println();
            });
        }
    }
    
    class RotatePanel extends JPanel implements ActionListener {
    
        private static final int SIZE = 256;
        private static final 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(SIZE, SIZE));
            this.addMouseListener(new MouseAdapter() {
    
                @Override
                public void mousePressed(MouseEvent e) {
                    dt = -dt;
                    image = RotatableImage.getImage(SIZE);
                }
            });
            timer.start();
        }
    
        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            g2d.rotate(theta,128,128);
            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);
            final Color c1 = Color.getHSBColor(r.nextFloat(), 1, 1);
            final Color c2 = Color.getHSBColor(r.nextFloat(), 1, 1);
            g2d.setPaint(c1);
            g2d.setStroke(new BasicStroke(10.0f));
            g2d.draw(new Line2D.Double(0, size/2, size, size/2));
    
            g2d.setPaint(c2);
            g2d.draw(new Line2D.Double(size/2, 0, size/2, size));
    
            g2d.setPaint(c1);
            g2d.draw(new Ellipse2D.Double(0, 0, size, size));
            g2d.dispose();
            return bi;
        }
    }
    

    解释:

    1. 因此,您的解决方案中的“摇摆”来自这样一个事实,即您将图像和容器/面板的大小设置为 256x256,但“基于”您的轮子布局仅基于 200x200。我固定了所有尺寸,并画了一个正确的十字,g2d.rotate(theta,128,128); (!) 与中心相关。

    2. 多色(简单):您可以在每个形状之间调用setPaint()! ;)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-22
      • 1970-01-01
      • 2018-08-16
      • 2011-01-15
      • 1970-01-01
      • 2017-09-25
      • 2010-12-19
      • 1970-01-01
      相关资源
      最近更新 更多