如何在周围没有 "invisible box" 的情况下绘制自定义圆形 JComponent?

How can I paint a custom circular JComponent without an "invisible box" around it?

我正在编写一个 JApplet,该 JApplet 显示一个图表,其中数字在圆圈中,线条连接圆圈。我创建了一个 class 来扩展 JComponent 以充当圆圈。我重写了 paintComponent() 方法来绘制一个内部带有数字的圆圈,并将这些圆圈放在我的小程序上,然后我在 paint() 方法中绘制了线条。

但是,如果线条以一定角度碰到圆圈,它们会在圆圈之前在勾勒出整个 JComponent 轮廓的正方形处提前切出,即使背景应该是透明的。这在圆圈周围形成了一个 "invisible box"。

我准备了一个小程序来演示这个问题:

下面是该示例的代码:

import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;

import javax.swing.*;

public class Circle extends JApplet {
    private static final long serialVersionUID = 1L;

    myCircle myC = new myCircle(10, 215, 215); // custom circle
    Container c;

    public void init() {
        setSize(500, 500);
        c = getContentPane();
        c.setLayout(null);
        c.setBackground(Color.lightGray);
        c.add(myC);
    }

    public void paint(Graphics g) {
        super.paint(g);
        Graphics2D g2d = (Graphics2D)g;
        g2d.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(Color.black);
        g2d.setStroke(new BasicStroke(2f, BasicStroke.CAP_BUTT,     BasicStroke.JOIN_MITER));

        //draw lines
        g2d.draw(new Line2D.Double(0f, 0f, 500f, 500f));
        g2d.draw(new Line2D.Double(0f, 500f, 500f, 0f));
        g2d.draw(new Line2D.Double(0f, 250f, 500f, 250f));
        g2d.draw(new Line2D.Double(250f, 0f, 250f, 500f));
        g2d.draw(new Line2D.Double(150f, 0f, 350f, 500f));
        myC.repaint(); // put the circle on top
    }

    public class myCircle extends JComponent {
        private static final long serialVersionUID = 1L;

        int number; // number to display

        public myCircle(int num, int x, int y) {
            this.number = num;
            this.setLocation(x, y);
            this.setSize(75, 75);
        }

        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D)g;
            g2d.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(
                    RenderingHints.KEY_TEXT_ANTIALIASING,
                    RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g2d.setStroke(new BasicStroke(2f, BasicStroke.CAP_BUTT,     BasicStroke.JOIN_MITER));
            g2d.setColor(Color.white);
            g2d.fill(new Ellipse2D.Float(1f, 1f, 70f, 70f));
            g2d.setColor(Color.black);
            g2d.draw(new Ellipse2D.Float(1f, 1f, 70f, 70f));
            g2d.drawString(Integer.toString(number), 15f, 20f);
        }
    }
}

您需要查看所绘制线条的长度。从图形的边缘到圆圈的角度比侧面更长。回想一下具有 45 度、45 度和 90 度角的三角形,边长之比为 1:1:sqrt(2)

我不会单独使用组件来表示圆形。相反,只需将其全部绘制在一个 JPanel 上(我使用的是 JFrame 而不是小程序):

public class Circle extends JPanel {

    int number = 10;
    float size = 500f;
    float rad = 70f;
    float stringLocX = 15f, stringLocy = 20f;

    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(Color.LIGHT_GRAY);
        g2d.fillRect(0, 0, getWidth(), getHeight());

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2d.setStroke(new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER));

        g2d.setColor(Color.BLACK);
        g2d.draw(new Line2D.Double(0f, 0f, size, size));
        g2d.draw(new Line2D.Double(0f, size, size, 0f));
        g2d.draw(new Line2D.Double(0f, size / 2, size, size / 2));
        g2d.draw(new Line2D.Double(size / 2, 0f, size / 2, size));
        g2d.draw(new Line2D.Double(150f, 0f, 350f, 500f));

        Ellipse2D.Float circle = new Ellipse2D.Float((size - rad) / 2, (size - rad) / 2, rad, rad);
        g2d.setColor(Color.WHITE);
        g2d.fill(circle);
        g2d.setColor(Color.BLACK);
        g2d.draw(circle);
        g2d.drawString(Integer.toString(number), (size - rad) / 2 + stringLocX,
                        (size - rad) / 2 + stringLocy);
    }

    @Override
    public Dimension getPreferredSize() {

        return new Dimension((int) size, (int) size);
    }

    public static void main(String[] args) {

        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame();
            frame.add(new Circle());
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.pack();
            frame.setVisible(true);
        });
    }
}

根据您拥有的圆圈数量以及它们必须包含的数据,您可以只保留这些数字的列表,然后在 painComponent 中遍历该列表并绘制它们。

备注:

  • 不要使用 null 布局。
  • 最好将硬编码数字保留为字段,这样您可以更轻松地更改它们,可能使它们成为 final.