JFrame调整大小后如何计算圆形的X-Y坐标和半径?

How to calculate the X-Y coordinates and Radius of Circle shape after resize of JFrame?

我正在使用 JComponent (AWT/SWING) 在 JFrame 上绘制圆圈,我想确保当用户调整框架大小时,会进行某些计算并在屏幕上动态绘制圆圈(无论是否它更大,更小,向左或向右移动等)。我实现了 ComponentAdapter 事件和 componentResized 方法,但是我正在努力想出一些动态的东西。这是我的代码:

CircleViewer Class

import javax.swing.JFrame;
import java.awt.event.*;

public class CircleViewer
{
    public static void main(String[] args)
    {
        final JFrame frame = new JFrame("Circle Shapes");
        final CirclePanel panel = new CirclePanel();

        // Class for Mouse Listener which implements the necessary interfaces
        class MousePressListener implements MouseListener, MouseMotionListener
        {
            public void mouseClicked(MouseEvent event) { }
            public void mouseEntered(MouseEvent event) { }
            public void mouseExited(MouseEvent event) { }
            public void mouseWheelMoved(MouseWheelEvent event) { }
            public void mouseMoved(MouseEvent event) { }
            public void mousePressed(MouseEvent event) { }

            @Override
            public void mouseDragged(MouseEvent event)
            {
                var x = event.getX();
                var y = event.getY();
                panel.moveTo(x, y);
            }

            @Override
            public void mouseReleased(MouseEvent event)
            {
                panel.finalMove();
            }
        }

        panel.addComponentListener(new ComponentAdapter() {
            public void componentResized(ComponentEvent event)
            {
                panel.frameResizeCalculation(frame.getWidth(), frame.getHeight());
            }
        });

        MousePressListener listener = new MousePressListener();
        panel.addMouseListener(listener);
        panel.addMouseMotionListener(listener);

        frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(panel);
        frame.setVisible(true); 
    }
    public static final int FRAME_WIDTH = 700;
    public static final int FRAME_HEIGHT = 500;
}

圆形面板Class

import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import java.util.ArrayList;
import java.awt.Color;
import java.lang.Math;
import java.awt.BasicStroke;
import java.awt.Stroke;

public class CirclePanel extends JComponent
{
    private int mouseX;
    private int mouseY;
    private ArrayList<Circle> circleList;
    private final BasicStroke dashLine = new BasicStroke(1, 
            BasicStroke.CAP_BUTT, 
            BasicStroke.JOIN_BEVEL, 
            0, new float[]{6}, 0);
    private Circle newCircle;
    private final Color newCircleColor = Color.RED;
    private final Color finalCircleColor = Color.BLUE;

    public CirclePanel()
    {
        this.circleList = new ArrayList<Circle>();
        this.mouseX = 0;
        this.mouseY = 0;
    }

    public void moveTo(int x, int y)
    {
        mouseX = x;
        mouseY = y;
        if (newCircle == null)
        {
            newCircle = new Circle(x,y,0);
        }
        else
        {
            int dX = newCircle.get(0) - mouseX;
            int dY = newCircle.get(1) - mouseY;
            newCircle.set(2, (int)Math.sqrt(dX*dX + dY*dY));
        }
        repaint();
    }

    public void finalMove()
    {
        if (newCircle != null)
        {
            circleList.add(newCircle);
            newCircle = null;
            repaint();
        }
    }
    // Do something here and change X-Y coordinates and radius of the circles and finally call repaint() method of Graphics2D
    public void frameResizeCalculation(int width, int height)
    {
        var dX = CircleViewer.FRAME_WIDTH - width;
        var dY = CircleViewer.FRAME_HEIGHT - height;
    }

    public void paintComponent(Graphics g)
    {

        g.setColor(finalCircleColor);
        for (Circle circle : circleList) 
        {

            drawCircle(g, circle);
        }

        Circle c = newCircle;
        if (c != null)
        {
            g.setColor(newCircleColor);
            drawCircle(g, c);

            Graphics2D g2 = (Graphics2D)g.create();
            g2.setStroke(dashLine);
            g2.drawLine(c.get(0), c.get(1), mouseX, mouseY);
            g2.dispose();
        }
    }

    public void drawCircle(Graphics g, Circle c)
    {
        g.drawOval(c.get(0) - c.get(2), c.get(1) - c.get(2), c.get(2) * 2, c.get(2) * 2);
    }
}

最后,圆

public class Circle
{
    private int x;
    private int y;
    private int radius;

    public Circle(int x, int y, int radius)
    {
        this.x = x;
        this.y = y;
        this.radius = radius;
    }

    public int get(int option)
    {
        switch (option)
        {
            case 0:
                return this.x;
            case 1:
                return this.y;
            case 2:
                return this.radius;
        }
        return 0;
    }

    public void set(int option, int value)
    {
        switch (option)
        {
            case 0: //set x
                this.x = value;
                break;
            case 1:
                this.y = value;
                break;
            case 2:
                this.radius = value;
                break;
        }
    }
}

that certain calculations are made and circles are drawn on screen dynamically (whether if it's bigger, smaller, moved to left or right etc.).

好吧,您需要定义调整框架大小时要发生的情况。我们无法告诉您该怎么做。

然而,在您担心之前,您需要重组您的 classes 以使其能够拥有动态绘画。

我发现基本代码存在以下问题:

  1. 忘掉帧大小吧。该大小与将在您的 CirclePanel 中完成的自定义绘画无关。那就是 CirclePanel 的大小与框架的大小不同,因为框架大小包括框架边框和标题栏。您的逻辑应该基于面板的大小,而不是框架。

  2. 进行自定义绘制时,组件也有责任将 getPreferredSize() 方法覆盖为 return 组件的首选大小。然后在您的代码中将面板添加到框架,然后在框架上调用 pack() 方法。

  3. 您调用 class CirclePanel,但您扩展了 JComponent。为什么?给你的 class 一个合适的名字。如果你想扩展 JComponent 然后调用你的 class CircleComponent。如果您想调用 class CirclePanel,请扩展 JPanel。但是您还需要了解扩展 JComponent 和 JPanel 之间的区别。所有 Swing 组件都负责在进行任何绘画之前清除组件的背景。 JPanel 会自动为您执行此操作,您只需在开始时调用 super.paintComponent(...) 即可。 JComponent 不会清除背景,因此您必须通过设置 Graphics object 的颜色来清除它,然后调用 fillRect(0, 0, getWidth(), getHeight()) 来绘制背景。

  4. Circle object 应该包含绘制自身所需的所有信息。您已经有了 x、y、半径值。我建议你还需要一个 Color 属性 这样每个 Circle 都可以是不同的颜色。

  5. Circle object 应该知道如何使用其属性来绘制自身。因此你的 Circle class 应该有一个方法,比方说 draw(Graphics grapics)。然后使用 class 的属性绘制椭圆。所以这意味着 paintComponent() 方法将调用 Circle class 的 draw(...) 方法,您将删除当前拥有的 drawOval(...) 方法。

  6. A "getter" 方法不带参数。如果您觉得其他 classes 需要知道 x、y、radius 属性,则创建 getX()、getY() 和 getRadiout() 方法。我建议您不需要 get()/set() 方法。

我建议您首先需要执行以上建议,然后才能使绘画动态化。

接下来,您不需要将 ComponentListener 添加到面板。相反,您需要向 CirclePanel class 的 paintComponent(...) 方法添加逻辑。每次面板大小改变时都会自动调用 paintComponent() 方法。基本逻辑是确定绘制每个圆时要使用的"multiplier"。

所以你可以使用getPreferredSize()方法获取首选宽度,你可以使用面板的getWidth()方法获取当前大小。所以你的乘数是:

double multiplierX = getWidth() / getPreferredSize().x;

现在需要将此信息传递给 Circle objects draw(...) 方法,因此方法签名将变为 draw(Graphics g, double multiplierX)。当您调用 drawOval(...) 方法时,您将乘数应用于 "x" 参数。这应该会导致圆圈在调整框架大小时在水平方向上移动。

然后您将对乘数 Y 重复上述步骤,使圆在垂直方向移动。

然后您需要决定如何影响半径?