Swing:paintComponenet 不会绘制到 JPanel

Swing: paintComponenet won't paint to JPanel

我正在创建一个绘制形状的 GUI 界面。

我决定在 JPanel 上使用 paintComponent(),而不是在 JFrame 上使用 paint()。这是因为,在框架上绘画时,按钮不会出现,直到我将鼠标悬停在它们上面。

我最近制作的

CHANGES/UPDATES(下面更新了代码):

  1. 我在我的 artPanel JPanel class 中实现了 MouseListener 和 MouseMotionListener。这似乎解决了我的一些问题。但是有几个问题:
  2. 您可以下载我的java文件 here这样您就可以更轻松地找出问题所在。

我无法解决的问题:

  1. 线路按钮无法正确创建线路。我一直在玩弄 mouseListener 方法,但我无法让它工作。我希望它在单击和拖动鼠标时简单地画一条线。
  2. 其他按钮正确绘制形状。 然而...当我制作新形状时,形状不会留在面板上。例如,我画了一个圆圈。然后,当我再画一个圆圈时,第一个圆圈就消失了。我希望他们都一直留在 canvas 上。

除了这两个,它的效果还不错。它的样子的照片是 here.

public class Shapes extends JFrame implements ActionListener, WindowListener{

    /**
     * Instance variables are created for the shape's sizes, window height and width.
     */
    int currentX, currentY;
    int previousX, previousY;
    int topLeftX, topLeftY;
    int bottomRightX, bottomRightY;
    int height, width;
    public final int WINDOW_WIDTH = 900;
    public final int WINDOW_HEIGHT = 700;

    /**
     * String shape is created for the type of shape.
     */
    public String shape = "Shape";

    /**
     * Color is set to null, because no color is pressed yet.
     */
    public Color color = null;

    /**
     * MouseReleased is false because no mouse has been released yet.
     */
    public boolean mouseReleased = false;

    private class artPanel extends JPanel implements MouseListener, MouseMotionListener{

        public artPanel(){
         /**
         * Listeners are added to the GUI (for the mouse clicks, and for the window exit).
         */
        addMouseListener(this);
        addMouseMotionListener(this);
        }

        /**
         * mouseDragged method is overridden.
         * Previous X and Y variables are set to the current X and Y values.
         * Then, the current X and Y values are set to the position where the mouse is dragged.
         * The width and height is then calculated, while using the absolute value.
         * Repaint method is invoked.
         */
        @Override
        public void mouseDragged(MouseEvent e) {

            previousX = currentX;
            previousY = currentY;

            currentX = bottomRightX = e.getX();
            currentY = bottomRightY = e.getY();

            width = Math.abs(topLeftX - bottomRightX);
            height = Math.abs(topLeftY - bottomRightY);

            repaint();
        }

        /**
         * mouseClicked method is overridden.
         */
        @Override
        public void mouseClicked(MouseEvent e) {

        }

        /**
         * mouseEntered method is overridden.
         */
        @Override
        public void mouseEntered(MouseEvent e) {

        }

        /**
         * mouseExited method is overridden.
         */
        @Override
        public void mouseExited(MouseEvent e) {

        }

        /**
         * mousePressed method is overridden, current X and Y variables are set to the position where the mouse is pressed.
         */
        @Override
        public void mousePressed(MouseEvent e) {
            topLeftX = currentX = e.getX();
            topLeftY = currentY = e.getY();

        }

        /**
         * mouseReleased method is overridden.
         * Bottom Right X and Y variables are set to the position where the mouse is pressed.
         * Width and height is set using the absolute value of the difference.
         * Repaint method is invoked.
         */
        @Override
        public void mouseReleased(MouseEvent e) {
            bottomRightX = e.getX();
            bottomRightY = e.getY();

            width = Math.abs(topLeftX - bottomRightX);
            height = Math.abs(topLeftY - bottomRightY);

            mouseReleased = true;

            repaint();
        }




        /**
         * mouseMoved method is overridden.
         */
        @Override
        public void mouseMoved(MouseEvent e) {

        }

        /**
         * Paint method is created with parameter g for implementing graphics.
         */
        public void paintComponent(Graphics g){
            super.paintComponent(g);

            /**
             * If the color is not null (has been changed), then the color is set to the user's c
             */
            if(color != null)
                g.setColor(color);

            /**
             * If the shape is a line (line button clicked), then the line is drawn.
             */
            if(shape.equals("Line")){
                g.drawLine(previousX, previousY, currentX, currentY);
            }

            /**
             * If the shape is a circle (circle button clicked), then the circle is drawn.
             * The mouseReleased is set to false so that it draws it when it is dragged.
             */
            else if(shape.equals("Circle") && mouseReleased){
                g.drawOval(topLeftX, topLeftY, width, height);
                mouseReleased = false;
            }

            /**
             * If the shape is a Rectangle (rectangle button clicked), then the rectangle is drawn.
             * The mouseReleased is set to false so that it draws it when it is dragged.
             */
            else if(shape.equals("Rectangle") && mouseReleased){
                g.drawRect(topLeftX, topLeftY, width, height);
                mouseReleased = false;
            }

            /**
             * If the shape is an Arc (arc button clicked), then the arc is drawn.
             * The mouseReleased is set to false so that it draws it when it is dragged.
             */
            else if(shape.equals("Arc") && mouseReleased){
                g.drawArc(topLeftX, topLeftY, width, height, 0, 90);
                mouseReleased = false;
            }
        }

    }


    /**
     * Constructor for creating the GUI
     */
    public Shapes(){
        /**
         * Super is invoked, title is set
         */
        super("Draw Geometric Shapes");
        /**
         * Size is set using the instance variables, does nothing on close as default.
         */
        setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

        /**
         * Layout is set to borderlayout for the frame.
         */
        setLayout(new BorderLayout());

        /**
         * A panel for the buttons is created, uses a flowlayout.
         */
        JPanel buttons = new JPanel();
        buttons.setLayout(new FlowLayout());

        /**
         * Button for the color is created.
         */
        JButton colorChooser = new JButton("Color");
        colorChooser.addActionListener(this);
        buttons.add(colorChooser);

        /**
         * Button for making a line is created.
         */
        JButton line = new JButton("Line");
        line.addActionListener(this);
        buttons.add(line);

        /**
         * Button for making a circle is created.
         */
        JButton circle = new JButton("Circle");
        circle.addActionListener(this);
        buttons.add(circle);

        /**
         * Button for making a rectangle is created.
         */
        JButton rect = new JButton("Rectangle");
        rect.addActionListener(this);
        buttons.add(rect);

        /**
         * Button for making an arc is created
         */
        JButton arc = new JButton("Arc");
        arc.addActionListener(this);
        buttons.add(arc);

        /**
         * Buttons panel is added to the south part of the border layout (bottom of the frame).
         */
        add(buttons, BorderLayout.SOUTH);

        addWindowListener(this);

        artPanel p = new artPanel();
        p.setLayout(new FlowLayout());
        p.setBackground(Color.WHITE);

        add(p);
    }


    /**
     * @param args
     */
    public static void main(String[] args) {
        /**
         * New object of type Shapes is created and set to visible.
         */
        Shapes draw_shapes = new Shapes();
        draw_shapes.setVisible(true);
    }


    /**
     * actionPerformed is overridden and each button is set to a shape type
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        /**
         *  If the button "Color" is clicked, then the color chooser dialog appears.
         */
        if(e.getActionCommand().equals("Color")){
            color = JColorChooser.showDialog(this, "Color Chooser", color);
        }

        /**
         *  If the button "Line" is clicked, then the shape is set to Line.
         */
        else if(e.getActionCommand().equals("Line")){
            if(shape.equalsIgnoreCase("Line")){
                shape = "Line";
            }
            else shape = "Line";
        }

        /**
         *  If the button "Circle" is clicked, then the shape is set to circle.
         */
        else if(e.getActionCommand().equals("Circle")){
            if(shape.equalsIgnoreCase("Circle")){
                shape = "Circle";
            }
            else shape = "Circle";
        }

        /**
         *  If the button "Rectangle" is clicked, then the shape is set to rectangle.
         */
        else if(e.getActionCommand().equals("Rectangle")){
            if(shape.equalsIgnoreCase("Rectangle")){
                shape = "Rectangle";
            }
            else shape = "Rectangle";
        }

        /**
         *  If the button "Arc" is clicked, then the shape is set to Arc.
         */
        else if(e.getActionCommand().equals("Arc")){
            if(shape.equalsIgnoreCase("Arc")){
                shape = "Arc";
            }
            else shape = "Arc";
        }
    }

    }
}

我删除了其余部分,因为它们是 windowlistener 方法。

add(buttons, BorderLayout.SOUTH);

Panel p = new Panel();
add(p);

JPanel whitePanel = new JPanel();
whitePanel.setBackground(Color.WHITE);
add(whitePanel);

您的代码暗示您使用框架内容面板的默认布局管理器,它是 BorderLayout

您尝试将 "p" 和 "whitePanel" 添加到 BorderLayoutCENTER(当您未指定约束时这是默认设置)。只能将一个组件添加到 BorderLayout 的给定位置。只有最后添加的面板被绘制,这是你的 whitePanel 所以你永远不会看到你的面板的自定义绘制。

不知道您的确切布局要求是什么,所以除了使用不同的布局管理器或使用具有不同布局管理器的嵌套面板之外,我无法建议其他解决方案。

此外,在进行自定义绘画时,您应该将 getPreferredSize() 方法覆盖为 return 大小,以便布局管理器可以完成他们的工作。

不要将您的 class 称为 "Panel",因为已经有一个 AWT class 使用该名称,所以这会造成混淆。您的 class 名称应该更具描述性。

I decided to use paintComponent() on a JPanel, instead of using paint() on a JFrame.

你永远不应该覆盖框架上的油漆。您确实应该覆盖 JPanel 的 paintComponent() 。阅读有关 Custom Painting 的 Swing 教程部分,了解更多信息和工作示例。

编辑:

Then as soon as I make another circle, the first one disappears. I want them all to stay on the canvas at all times.

阅读 Custom Painting Approaches,其中展示了当您希望在同一面板上绘制多个对象时进行绘制的 2 种方法。您选择哪种方法取决于您的具体要求。