无法手动绘制到 JPanel

Unable to draw manually to JPanel

我正在尝试制作一个轨道模拟器,我 运行 遇到了这个问题。我已经检查了整个 Stack Overflow,但找不到解决方案。我只是想手动绘制到 JPanel,但它没有显示出来。我已将布局设置为空,使其可见,将其添加到 JFrame,将正文添加到 Plane,以及您通常会做的所有事情。

这是正文class:

package viperlordx.orbitsimulator;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;

import javax.swing.JLabel;

@SuppressWarnings("serial")
public class Body extends JLabel {
    private double mass;
    private Point location;
    private Vector velocity;
    private Color color;
    private Plane plane;
    public Body(double mass, Point location, Vector velocity, Color color) {
        this.mass = mass;
        this.location = location;
        this.velocity = velocity;
        this.color = color;
        this.setVisible(true);
        this.setBounds(location.x, location.y, 100, 100);
    }
    public void moveTick() {

        velocity.addTo(location);
    }
    public void setPlane(Plane plane) {
        this.plane = plane;
    }
    public Plane getPlane() {
        return plane;
    }
    @Override
    public void paintComponent(Graphics g) {
        System.out.println("Painting");
        super.paintComponent(g);
        if (plane != null && g != null) {
            g.setColor(color);
            g.fillOval(location.x, location.y, getWidth(), getHeight());
        }
    }
    public Point getLocation() {
        return location;
    }
    public void setLocation(Point location) {
        this.location = location;
    }
    public Vector getVelocity() {
        return velocity;
    }
    public void setVelocity(Vector vector) {
        velocity = vector;
    }
}

飞机class:

package viperlordx.orbitsimulator;

import java.awt.Graphics;
import java.util.HashSet;
import java.util.Set;

import javax.swing.JPanel;

@SuppressWarnings("serial")
public class Plane extends JPanel {
    private HashSet<Body> bodies = new HashSet<Body>();
    public void addBody(Body body) {
        this.add(body);
        bodies.add(body);
    }
    public Plane() {
        this.setLayout(null);
    }
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (bodies != null) {
            for (Body body : bodies) {
                body.paintComponent(g);
            }
        }
    }
    public Set<Body> getBodies() {
        return bodies;
    }
}

现在主要class:

package viperlordx.orbitsimulator;

import java.awt.Color;
import java.awt.Point;

import javax.swing.JFrame;

public class Main {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(1000, 1000);
        frame.setLocationRelativeTo(null);
        Plane plane = new Plane();
        frame.add(plane);
        plane.setVisible(true);
        frame.setLayout(null);
        plane.setBounds(0, 0, 1000, 1000);
        plane.setBackground(Color.WHITE);
        plane.addBody(new Body(10.0, new Point(10, 10), new Vector(0, 0), Color.GREEN));
        frame.setVisible(true);
        frame.setTitle("Orbit");
        plane.repaint();
    }
}

您混淆了组件上的绘图形状和将组件添加到容器中。你的 Body 不应该是一个组件,只是一个 class 保存要绘制的对象的数据。此数据用于在 "target" 组件 上绘制一个 对应的形状 - 在您的案例中是 Plane

我删除了不相关的代码并根据我上面的解释进行了更改:

public class Main {

    public static void main(String[] args) {

        SwingUtilities.invokeLater(() -> {

            JFrame frame = new JFrame();
            Plane plane = new Plane();
            frame.add(plane);
            plane.setBackground(Color.WHITE);
            plane.addBody(new Body(10.0, new Point(10, 10), new Vector(0, 0), Color.GREEN));

            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setTitle("Orbit");
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}

@SuppressWarnings("serial")
class Plane extends JPanel {

    private HashSet<Body> bodies = new HashSet<>();

    public void addBody(Body body) {

        bodies.add(body);
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        for (Body body : bodies) {
            // Might want to call all needed accessors before getting to work.
            g.setColor(body.getColor());
            g.fillOval(body.getLocation().x, body.getLocation().y, getWidth(), getHeight());
        }
    }

    @Override
    public Dimension getPreferredSize() {

        // Calculate the size
        return new Dimension(1000, 500);
    }
}

class Body {

    private Point location;
    private Vector velocity;
    private Color color;

    public Body(double mass, Point location, Vector velocity, Color color) {

        this.location = location;
        this.velocity = velocity;
        this.color = color;
    }

    public Point getLocation() {

        return location;
    }

    public Color getColor() {

        return color;
    }
}

备注:

  • Start Swing on the EDT.
  • 不要设置框架的大小(如果我的屏幕没有 1000 像素的高度怎么办?),改为调用 pack() 并确保组件您在其上适当绘制 @Override getPreferredSize()。这意味着您将需要根据您在其上绘制的内容进行计算。
  • 一般来说,setVisible(true) 应该是你做的最后一件事。完成后无需重绘。
  • 自 Java7 起,您可以编写 HashSet<Body> bodies = new HashSet<>() 而无需在 RHS 中指定泛型类型。
  • 不要使用原始类型 Vector。事实上,根本没有理由使用这个 class。 SetList 通常会做得更好。如果他们持有计算中使用的数字,那么他们的通用类型应该是 FloatDouble 可能。