是否可以在这些代码上使用 JPanel 而不是 JComponent?

问题描述

我已经制作了这个太阳系,到目前为止我已经完成了制作这个代码。 如图所示,我想使用 public static class SolarCv extends JComponent 而不是使用 public static class SolarCv extends JPanel,但它不起作用。我应该在代码中更改什么以及如何添加围绕对象的轨道,如图所示?

package solar;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.io.IOException;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.WindowConstants;

public class Orbit {

    public static class SolarCv extends JComponent {
        int width;
        int height;

        public SolarCv(int width,int height) {
            this.width = width;
            this.height = height;
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(width,height);
        }

        @Override
        public void paint(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);

            // Clear the background.
            g2.setColor(getBackground());
            g2.fillRect(0,getWidth(),getHeight());

            // Sun transform. Just centre it in the window.
            AffineTransform StarSun = AffineTransform.getTranslateInstance(getWidth() / 2,getHeight() / 2);

            // Draw the sun
            g2.setTransform(StarSun);
            SuperStar(g2,80,Color.YELLOW);

            // Orbital period.
            // One rotation every 10s.
            double percentRotation = System.currentTimeMillis() % 10000 / 10000.0;
            // To radians.
            double angle = -Math.PI * 2 * percentRotation;

            // Earth transform.
            // Set the orbital radius to 1/3rd the panel width
            AffineTransform StarEarth = AffineTransform.getTranslateInstance(getWidth() / 4,0);
            // Rotate
            StarEarth.preConcatenate(AffineTransform.getRotateInstance(angle));
            // Add the sun transform
            StarEarth.preConcatenate(StarSun);

            // Draw the earth
            g2.setTransform(StarEarth);
            SuperStar(g2,40,Color.BLUE);

            // Moon transform.
            // Set the orbital radius to 1/10th the panel width
            AffineTransform StarMoon = AffineTransform.getTranslateInstance(getWidth() / 10,0);
            // Rotate
            StarMoon.preConcatenate(AffineTransform.getRotateInstance(angle));
            // Add the earth transform (already includes the sun transform)
            StarMoon.preConcatenate(StarEarth);

            // Draw the moon
            g2.setTransform(StarMoon);
            SuperStar(g2,20,Color.DARK_GRAY);
        }

        private void SuperStar(Graphics2D g2,int size,Color color) {
            g2.setColor(color);
            g2.filloval(-size / 2,-size / 2,size,size);
        }
    }

    public static void main(String[] args) throws IOException,InterruptedException {
        JFrame Solarsys = new JFrame("Orbit");
        Solarsys.setDefaultCloSEOperation(WindowConstants.EXIT_ON_CLOSE);
        JComponent SolarCv = new SolarCv(500,500);
        Solarsys.add(SolarCv);
        Solarsys.pack();
        Solarsys.setVisible(true);

        while (true) {
            Thread.sleep(20);
            SolarCv.repaint();
        }
    }
}

解决方法

我发现你的作业很有趣,所以我创建了以下 GUI。

Solar System GUI

简介

我无法制作太阳、地球和月亮的相对大小,因为它们不适合显示器。

我无法将太阳、地球和月亮的相对距离分开,因为它们不适合显示器。

但是,我可以使地球和月球的旋转速度相互关联。

那么,我是怎么做到的?

由于这是一项作业,我不会提供我的全部代码。我将解释我如何编写我的代码和一些代码片段,希望它在未来对 OP 和其他人有所帮助。

说明

通常,当您创建 Swing GUI 时,您应该使用 model / view / controller 模式。这种模式允许您将关注点分开,并一次专注于应用程序的一个部分。

我编写了五个 Java 类来创建这个 GUI。我写了两个模型类、两个视图类和一个控制器类。控制器类有一个额外的匿名类。

型号

我编写的第一个模型类是 Body 类。这个类是一个计划Java getter / setter 类。

Body 类包含一个 java.awt.Point 标记身体的中心,身体半径 int 像素,轨道半径 int 像素,以度为单位的 theta 角增量和体色。

现在,地球轨道应该是地球实例的一部分。但是,我发现将地球轨道作为太阳实例的一部分更容易。这是您的代码没有反映现实以便您可以更准确地模拟现实的情况之一。

我花了一段时间才想出适合地球和月球的 theta 角增量。我只给你我的方程式,因为我花了大约 15 分钟尝试不同的函数。

sun thetaIncrement = 625_000.0 / framesPerSecond / 365.25 / 360.0
earth thetaIncrement = 625_000.0 / framesPerSecond / 27.3 / 360.0

625,000 是一个“神奇”的数字,它使动画以合理的速度移动。 360 度是一个圆的度数。

这两个公式之间的唯一区别是进行完整旋转的天数的值。

Body 类有一个有趣的方法,即 getOrbitPoint 方法。基本上,getOrbitPoint 方法将极坐标转换为笛卡尔坐标。很简单,但我会把代码贴出来。

    public Point getOrbitPoint() {
        double radians = Math.toRadians(theta);
        int x = (int) Math.round(Math.cos(radians) * 
                orbitRadius + center.x);
        int y = (int) Math.round(Math.sin(radians) * 
                orbitRadius + center.y);
        
        theta += thetaIncrement;
        theta = (theta > 360.0) ? theta - 360.0 : theta;
        theta = (theta < -360.0) ? theta + 360.0 : theta;
        
        return new Point(x,y);
    }

正值 thetaIncrement 将使主体顺时针旋转。负 thetaIncrement 将使主体逆时针旋转。

我创建的第二个模型类是 SolarSystemModel 类。这个类是一个普通的 Java getter/setter 类。

SolarSystemModel 类保存显示面板像素数的 int 值、动画每秒帧数的 int 值和 {{1} } Body 实例。

我创建了一个工厂方法来创建三个 java.util.List 实例,即太阳、地球和月亮。这是代码片段。

Body

查看

我创建了两个类,一个用于 private List<Body> createBodyFactory() { List<Body> bodies = new ArrayList<>(); int c = drawingPanelWidth / 2; int o = drawingPanelWidth / 3; double thetaIncrement = 625_000.0 / framesPerSecond / 365.25 / 360.0; Body body = new Body(c,c,60,o,thetaIncrement,Color.YELLOW); bodies.add(body); Point point = body.getOrbitPoint(); thetaIncrement = 625_000.0 / framesPerSecond / 27.3 / 360.0; body = new Body(point.x,point.y,20,Color.BLUE); bodies.add(body); point = body.getOrbitPoint(); body = new Body(point.x,10,0.0,Color.WHITE); bodies.add(body); return bodies; } ,另一个用于绘图 JFrame。我发布了这两个类的代码,而不是冗长的解释。

JPanel

当您创建适当的应用程序模型时,您可以看到视图类变得多么简单。

public class SolarSystemGUI implements Runnable { public static void main(String[] args) { SwingUtilities.invokeLater(new SolarSystemGUI()); } private Animation animation; private DrawingPanel drawingPanel; private SolarSystemModel model; public SolarSystemGUI() { this.model = new SolarSystemModel(); } @Override public void run() { JFrame frame = new JFrame("Solar System"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); drawingPanel = new DrawingPanel(model); frame.add(drawingPanel,BorderLayout.CENTER); frame.pack(); frame.setLocationByPlatform(true); frame.setVisible(true); this.animation = new Animation(this,model); new Thread(animation).start(); } public void repaint() { drawingPanel.repaint(); } public class DrawingPanel extends JPanel { private static final long serialVersionUID = 1L; private SolarSystemModel model; public DrawingPanel(SolarSystemModel model) { this.model = model; this.setBackground(Color.BLACK); int width = model.getDrawingPanelWidth(); this.setPreferredSize(new Dimension(width,width)); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); List<Body> bodies = model.getBodies(); for (Body body : bodies) { drawBody(g,body); } } private void drawBody(Graphics g,Body body) { Point point = body.getCenter(); int radius = body.getRadius(); int x = point.x - radius; int y = point.y - radius; int width = radius + radius; g.setColor(body.getColor()); g.fillOval(x,y,width,width); } } } 方法创建 run 和绘图的一个实例 JFrame

绘图 JPanel 绘制了 JPanel 实例。 期间。绘图 Body 没有尝试做任何其他事情。

JPanel Graphics 方法绘制椭圆并根据左上角填充它。我们做了一些数学运算,这样我们就可以根据中心点绘制椭圆了。

控制器

只有一个控制器类,fillOval 类。

Animation 类暂停 5 秒,然后开始地球和月球的自转。由于我们已经完成了创建模型和视图的工作,因此 Animation 类也很简单。

由于我使用的是老式 Animation,所以我将在此处发布课程。

Runnable

结论

我希望这个解释有帮助。使用模型/视图/控制器模式。分开你的顾虑。一次专注于应用程序的一个部分。确保每个类方法都做一件事。