Как рассчитать координаты XY и радиус формы круга после изменения размера JFrame? - PullRequest
0 голосов
/ 29 октября 2019

Я рисую Круги на JFrame, используя JComponent (AWT / SWING), и я хочу убедиться, что, когда пользователь изменяет размер фрейма, выполняются определенные вычисления и динамически рисуются круги на экране (будь то больше, меньше, перемещены влево или вправо и т. д.). Я реализовал событие ComponentAdapter и метод componentResized, однако я борюсь с тем, чтобы придумать что-то динамичное. Вот мой код:

Класс CircleViewer

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;
}

Класс CirclePanel

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;
        }
    }
}

1 Ответ

1 голос
/ 29 октября 2019

, что сделаны определенные вычисления, и на экране динамически нарисованы круги (будь то больше, меньше, смещены влево или вправо и т. Д.).

Ну, вам нужно определить, что вы хотите, чтобы происходило при изменении размера кадра. Мы не можем сказать вам, что делать.

Однако, прежде чем беспокоиться о том, что вам нужно реструктурировать свои классы, чтобы сделать возможным динамическое рисование.

Я вижу следующие проблемыс основным кодом:

  1. Забудьте о размере кадра. Этот размер не имеет отношения к пользовательской картине, которая будет выполнена в вашей CirclePanel. То есть размер CirclePanel НЕ совпадает с размером вашего фрейма, поскольку размер фрейма включает границы фрейма и строку заголовка. Ваша логика должна основываться на размере панели, а не на рамке.

  2. При выполнении пользовательского рисования компонент также должен переопределить метод getPreferredSize(), чтобы вернутьпредпочтительный размер компонента. Затем в своем коде вы добавляете панель к фрейму и затем вызываете метод pack() для фрейма.

  3. Вы вызываете свой класс CirclePanel, но расширяете JComponent. Почему? Дайте своему классу правильное имя. Если вы хотите расширить JComponent, тогда вызовите свой класс CircleComponent. Если вы хотите назвать свой класс CirclePanel, то расширьте JPanel. Но вы также должны понимать разницу между расширением JComponent и JPanel. Все компоненты Swing отвечают за очистку фона компонента ПЕРЕД выполнением рисования. JPanel делает это для вас автоматически, вы просто вызываете super.paintComponent (...) в начале. JComponent НЕ очищает фон, поэтому вы должны очистить его, установив цвет объекта Graphics, а затем вызовите fillRect (0, 0, getWidth (), getHeight ()) для рисования фона.

  4. Объект Circle должен содержать всю информацию, необходимую для рисования. У вас уже есть значения x, y, radius. Я бы посоветовал вам также использовать свойство Color, чтобы каждый круг мог иметь свой цвет.

  5. Затем объект круга должен знать, как рисовать себя, используя свои свойства. Поэтому у вашего класса Circle должен быть метод, скажем, draw(Graphics grapics). Затем вы используете свойства класса для рисования овала. Таким образом, это означает, что метод paintComponent () вызовет метод draw (...) класса Circle, и вы удалите метод drawOval (...), который у вас есть на данный момент.

  6. Метод «получения» не принимает параметры. Если вы чувствуете, что другим классам нужно знать свойства x, y, radius, создайте методы getX (), getY () и getRadiout (). Я бы посоветовал вам не использовать методы get () / set ().

Я предлагаю вам сначала реализовать перечисленные выше рекомендации, прежде чем рисовать динамически.

Далее вам не нужно добавлять ComponentListener на панель. Вместо этого вам нужно добавить логику в метод paintComponent(...) вашего класса CirclePanel. Метод paintComponent () будет вызываться автоматически каждый раз, когда изменяется размер панели. Основная логика заключается в определении «множителя», который будет использоваться при рисовании каждого круга.

Таким образом, вы можете использовать метод getPreferredSize(), чтобы получить предпочтительную ширину, и вы можете использовать метод getWidth() дляпанель, чтобы получить текущий размер. Таким образом, ваш множитель будет:

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

Теперь эту информацию необходимо передать в метод рисования (...) объектов Circle, чтобы сигнатура метода стала draw(Graphics g, double multiplierX). Когда вы вызываете метод drawOval(...), вы применяете множитель к параметру «x». Это должно привести к смещению кругов в горизонтальном направлении при изменении размера рамки.

Затем вы должны повторить вышеописанный шаг, чтобы множитель Y имел смещение кругов в вертикальном направлении.

ВыТогда нужно будет решить, как вы хотите повлиять на радиус?

...