Java 如何围绕JPanel中的一个点旋转一个圆?

Java 如何围绕JPanel中的一个点旋转一个圆?,java,swing,jpanel,awt,Java,Swing,Jpanel,Awt,我试图在程序中围绕一个单独的点旋转一个圆。现在我可以让圆旋转,但它慢慢地开始越来越接近它旋转的起点。我试图使用JPanel来实现这一点,并将其实现为一个矩形 package WoffindenZone; import java.awt.*; import javax.swing.*; import java.util.*; import java.awt.event.*; import java.lang.Math; public class Protector extends Rectangl

我试图在程序中围绕一个单独的点旋转一个圆。现在我可以让圆旋转,但它慢慢地开始越来越接近它旋转的起点。我试图使用JPanel来实现这一点,并将其实现为一个矩形

package WoffindenZone;
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.event.*;
import java.lang.Math;
public class Protector extends Rectangle{
double Velocity;
int speed = 3;
Protector(int x, int y, int PROTECTOR_DIAMETER){
    super(x,y,PROTECTOR_DIAMETER,PROTECTOR_DIAMETER);
}

public void keyPressed(KeyEvent e){
    if(e.getKeyCode()==KeyEvent.VK_A) {
        setDirection(speed);
        move();
    }
    if(e.getKeyCode()==KeyEvent.VK_D) {
        setDirection(speed);
        move();
    }
}
public void keyReleased(KeyEvent e){
    if(e.getKeyCode()==KeyEvent.VK_A) {
        setDirection(0);
        move();
    }
    if(e.getKeyCode()==KeyEvent.VK_D) {
        setDirection(0);
        move();
    }
}
public void setDirection(int Direction){
    Velocity = Direction*Math.PI/180;
}

public void move(){
    x = (int)Math.round(500 + Math.cos(Velocity) * (x-500) - Math.sin(Velocity) * (y-((1000*0.5555)/2)));
    y = (int)Math.round(((1000*0.5555)/2) + Math.sin(Velocity) * (x-500) + Math.cos(Velocity) * (y-((1000*0.5555)/2)));
    System.out.println(x);
    System.out.println(y);
}
public void draw(Graphics g){
    g.setColor(Color.blue);
    g.fillOval(x,y,width,height);
}    

使用仿射变换的旋转实例。有关详细信息,请参阅

返回围绕定位点旋转坐标的变换。此操作相当于平移坐标,使定位点位于原点(S1),然后围绕新原点(S2)旋转坐标,最后平移,使中间原点恢复到原始定位点(S3)的坐标

如何围绕JPanel中的一个点旋转一个圆

下面是我如何围绕
JPanel
中的一个点旋转一个圆

public class DrawingPanel extends JPanel {

    private static final long serialVersionUID = 1L;
    
    private Circle circle;
    
    public DrawingPanel(Dimension size, Circle circle) {
        this.circle = circle;
        this.setBackground(Color.WHITE);
        this.setPreferredSize(size);
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        
        Point center = circle.getCenter();
        int radius = circle.getRadius();
        int diameter = radius + radius;
        g2d.setColor(circle.getColor());
        g2d.fillOval(center.x - radius, center.y - radius, 
                diameter, diameter);
    }
    
}

我不知道如何制作动画GIF。想象一下,蓝色圆圈绕着绘图中心顺时针旋转
JPanel

public class DrawingPanel extends JPanel {

    private static final long serialVersionUID = 1L;
    
    private Circle circle;
    
    public DrawingPanel(Dimension size, Circle circle) {
        this.circle = circle;
        this.setBackground(Color.WHITE);
        this.setPreferredSize(size);
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        
        Point center = circle.getCenter();
        int radius = circle.getRadius();
        int diameter = radius + radius;
        g2d.setColor(circle.getColor());
        g2d.fillOval(center.x - radius, center.y - radius, 
                diameter, diameter);
    }
    
}
那么,让我们从头开始。基本上,我有一个圆在另一个圆的圆周上旋转。因此,我从纯Java创建了一个
Circle
模型类

public class Circle {
    
    private final int radius;
    
    private final Color color;
    
    private Point center;
    
    public Circle(int radius, Color color) {
        this.radius = radius;
        this.color = color;
    }
    
    public Point calculateCircumferencePoint(int theta) {
        double radians = Math.toRadians(theta);
        int x = center.x + (int) Math.round(Math.cos(radians) * radius);
        int y = center.y + (int) Math.round(Math.sin(radians) * radius);
        return new Point(x, y);
    }
    
    public void setCenter(int x, int y) {
        this.center = new Point(x, y);
    }

    public void setCenter(Point center) {
        this.center = center;
    }

    public int getRadius() {
        return radius;
    }

    public Color getColor() {
        return color;
    }

    public Point getCenter() {
        return center;
    }
    
}
该类由基本的getter和setter组成。我将半径和颜色设置为final,因为在这个Java应用程序中它们不会改变值

calculateCircumferencePoint
方法是唯一有趣的方法。它以度为单位获取
int
角度,并计算由该角度表示的圆周上的点,四舍五入到最近的X和Y整数点

接下来,我们创建两个
Circle
实例,一个内圈和一个外圈。下面是类构造函数设置绘图区域、内圈和外圈的首选大小。我们从零度(向右)开始外圆

现在,我们可以开始编写GUI了。首先,我们通过调用
SwingUtilities
invokeLater
方法来启动Java应用程序。此方法确保我们在上创建和执行Swing组件

接下来,我们定义
JFrame
。这是我们目前掌握的代码

public static void main(String[] args) {
    SwingUtilities.invokeLater(new RotateCircle());
}

private Animation animation;

private Circle innerCircle;
private Circle outerCircle;

private DrawingPanel drawingPanel;

private Dimension drawingPanelSize;

public RotateCircle() {
    this.drawingPanelSize = new Dimension(400, 400);
    int innerCircleRadius = drawingPanelSize.width / 4;
    int centerX = drawingPanelSize.width / 2;
    int centerY = drawingPanelSize.height / 2;
    int outerCircleRadius = drawingPanelSize.width / 10;
    
    this.innerCircle = new Circle(innerCircleRadius, null);
    this.innerCircle.setCenter(centerX, centerY);
    this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
    Point point = innerCircle.calculateCircumferencePoint(0);
    this.outerCircle.setCenter(point);
}

@Override
public void run() {
    JFrame frame = new JFrame("Rotate Circle");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    drawingPanel = new DrawingPanel(drawingPanelSize, 
            outerCircle);
    frame.add(drawingPanel, BorderLayout.CENTER);
    
    frame.pack();
    frame.setLocationByPlatform(true);
    frame.setVisible(true);
    
    animation = new Animation(0);
    new Thread(animation).start();
}
必须按特定顺序调用
JFrame
方法。这是我大多数SWwing应用程序使用的顺序

我打包了
JFrame
。我没有设置
JFrame
大小。我让用户设置我的
JFrame
的大小。
JFrame
内容窗格的默认布局是
BorderLayout
。我将我的绘图
JPanel
放在
边框布局的中心

接下来,我创建图形
JPanel

public class DrawingPanel extends JPanel {

    private static final long serialVersionUID = 1L;
    
    private Circle circle;
    
    public DrawingPanel(Dimension size, Circle circle) {
        this.circle = circle;
        this.setBackground(Color.WHITE);
        this.setPreferredSize(size);
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        
        Point center = circle.getCenter();
        int radius = circle.getRadius();
        int diameter = radius + radius;
        g2d.setColor(circle.getColor());
        g2d.fillOval(center.x - radius, center.y - radius, 
                diameter, diameter);
    }
    
}
绘图
JPanel
所做的就是绘制一个
对象。很简单

fillOval
方法从左上角绘制一个椭圆形。我们从中心点计算左上角的点

计算和更新外圈中心点的责任落在我的控制器类上,
动画类。我使用一个简单的循环来更新θ角,计算新的外圈中心点,绘制外圈,然后等待一段时间

这是密码

public class Animation implements Runnable {
    
    private int theta;
    
    public Animation(int theta) {
        this.theta = theta;
    }
    
    @Override
    public void run() {
        while (true) {
            theta++;
            theta = (theta >= 360) ? 0 : theta;
            
            Point center = innerCircle.calculateCircumferencePoint(theta);
            outerCircle.setCenter(center);
            repaint();
            sleep(30L);
        }
    }
    
    private void repaint() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                drawingPanel.repaint();
            }
        });
    }
    
    private void sleep(long duration) {
        try {
            Thread.sleep(duration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
}
Animation
repaint
方法在另一个
SwingUtilities
方法中调用绘图
JPanel
repaint
方法。此方法确保在事件分派线程上进行绘图

最后,这里是一个完整的、可运行的示例。我使用了内部类,所以我可以将代码作为一个块发布,您可以将代码作为一个块复制并运行。通常,类应该在单独的文件中,对于更复杂的GUI,应该在单独的包中

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class RotateCircle implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new RotateCircle());
    }
    
    private Animation animation;
    
    private Circle innerCircle;
    private Circle outerCircle;
    
    private DrawingPanel drawingPanel;
    
    private Dimension drawingPanelSize;
    
    public RotateCircle() {
        this.drawingPanelSize = new Dimension(400, 400);
        int innerCircleRadius = drawingPanelSize.width / 4;
        int centerX = drawingPanelSize.width / 2;
        int centerY = drawingPanelSize.height / 2;
        int outerCircleRadius = drawingPanelSize.width / 10;
        
        this.innerCircle = new Circle(innerCircleRadius, null);
        this.innerCircle.setCenter(centerX, centerY);
        this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
        Point point = innerCircle.calculateCircumferencePoint(0);
        this.outerCircle.setCenter(point);
    }

    @Override
    public void run() {
        JFrame frame = new JFrame("Rotate Circle");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        drawingPanel = new DrawingPanel(drawingPanelSize, 
                outerCircle);
        frame.add(drawingPanel, BorderLayout.CENTER);
        
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
        
        animation = new Animation(0);
        new Thread(animation).start();
    }
    
    public class DrawingPanel extends JPanel {

        private static final long serialVersionUID = 1L;
        
        private Circle circle;
        
        public DrawingPanel(Dimension size, Circle circle) {
            this.circle = circle;
            this.setBackground(Color.WHITE);
            this.setPreferredSize(size);
        }
        
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            
            Graphics2D g2d = (Graphics2D) g;
            g2d.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
            
            Point center = circle.getCenter();
            int radius = circle.getRadius();
            int diameter = radius + radius;
            g2d.setColor(circle.getColor());
            g2d.fillOval(center.x - radius, center.y - radius, 
                    diameter, diameter);
        }
        
    }
    
    public class Animation implements Runnable {
        
        private int theta;
        
        public Animation(int theta) {
            this.theta = theta;
        }
        
        @Override
        public void run() {
            while (true) {
                theta++;
                theta = (theta >= 360) ? 0 : theta;
                
                Point center = innerCircle.calculateCircumferencePoint(theta);
                outerCircle.setCenter(center);
                repaint();
                sleep(30L);
            }
        }
        
        private void repaint() {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    drawingPanel.repaint();
                }
            });
        }
        
        private void sleep(long duration) {
            try {
                Thread.sleep(duration);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
    }
    
    public class Circle {
        
        private final int radius;
        
        private final Color color;
        
        private Point center;
        
        public Circle(int radius, Color color) {
            this.radius = radius;
            this.color = color;
        }
        
        public Point calculateCircumferencePoint(int theta) {
            double radians = Math.toRadians(theta);
            int x = center.x + (int) Math.round(Math.cos(radians) * radius);
            int y = center.y + (int) Math.round(Math.sin(radians) * radius);
            return new Point(x, y);
        }
        
        public void setCenter(int x, int y) {
            this.center = new Point(x, y);
        }

        public void setCenter(Point center) {
            this.center = center;
        }

        public int getRadius() {
            return radius;
        }

        public Color getColor() {
            return color;
        }

        public Point getCenter() {
            return center;
        }
        
    }

}

Java命名约定使用大写字母作为类的名称;变量和方法以小写字母(方向)开头。常量使用所有的大写字母和蛇壳。@NomadMaker起初我有一个xDirection和yDirection,并使圆圈自由移动,所以当我更改内容时,我忘了将d更改为小写字母。感谢heads upStore为
x
y
存储
double
值,并在每次计算新值时使用
double
值。当您实际调用
fillOval
时,仅将圆/强制转换为
int
。创建一个包含中心java.awt.Point和int半径的圆类。创建要绘制的圆和围绕旋转圆旋转绘制圆的圆。在一幅画上画画。请参见Oracle教程以查看示例。