【问题标题】:Draw and move a circle in Java在 Java 中绘制和移动一个圆
【发布时间】:2014-11-21 22:02:04
【问题描述】:

我正在使用 Swing 在 Java 中创建一个小型 GUI。我要做的就是取ArrayListof Circles 并绘制它们。我遇到了两个问题:

1) 在绘制圆圈之前,我必须反复调用我的 draw 方法。如果我只是在没有任何反应的情况下调用我的draw 方法,我会得到一张空白图。如果我在一个运行时间少于 30 毫秒的循环中调用它,它只会绘制我想要绘制的两个圆圈中的第一个。最后,如果我调用它超过 30 毫秒,它会绘制我要绘制的两个圆圈。

2) 当我移动其中一个圆圈时,绘图上会出现“闪烁”。

我对 Swing 编程不太熟悉。我查看了示例代码并观看了一些视频 - 我所看到的内容对我来说是正确的。但我想我一定是搞砸了,因为在我看过的视频中看起来不是这样的。

这是我的GUI 课程:

package gui;

import draw.*;
import java.util.List;
import javax.swing.*;

public class GUI extends JFrame {
    private CirclePainter drawingBoard = new CirclePainter();

    public GUI()
    {
        setSize(500, 500);
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        this.setVisible(true);
        this.add(drawingBoard);
        drawingBoard.setVisible(true);
    }

    public void draw(List<Circle> circles)
    {
        drawingBoard.paintComponent(drawingBoard.getGraphics(), circles);
    }
}

我的CirclePainter 班级

package gui;

import draw.Circle;

import javax.swing.*;
import java.awt.*;
import java.util.List;

class CirclePainter extends JPanel
{
    public void paintComponent(Graphics graphics, List<Circle> circles)
    {
        super.paintComponent(graphics);
        for(Circle circle : circles)
            graphics.fillOval(circle.getX(), circle.getY(), circle.getRadius() * 2, circle.getRadius() * 2);
    }
}

编辑:编辑了一些代码,因为这是一个学校项目。剩下的代码应该足以让将来访问的人仍然理解问题。

【问题讨论】:

    标签: java swing user-interface jframe jpanel


    【解决方案1】:
    1. 千万不要在你做的时候直接打电话给paintComponent(...)
    2. 而是在必要时通过在组件上调用 repaint() 来建议平局。
    3. 不要使用通过组件上的getGraphics() 调用获得的图形对象进行绘制。而是使用 paintComponent 方法中提供的 Graphics 对象进行绘制。
    4. 避免在 Swing GUI 中使用 while (true) 循环,因为您可能会占用 Swing 事件线程并冻结 GUI。使用 Swing Timer 制作简单的动画。
    5. 您可能甚至不需要 Swing Timer,因为您的动画可以由 MouseListener/MouseMotionListener 驱动。
    6. 最重要的 - 请阅读 Swing 绘画和其他教程,因为大部分信息都可以在此处找到。看起来您正在猜测如何进行一些编码,而在绘制或动画 GUI 时,这是一件危险的事情。您可以在Swing info 链接中找到大多数教程。
    7. 考虑使用 Shape 对象来表示您的 Circle,例如 ellipse2D。这会有所帮助的原因是它有一些非常有用的方法,包括一个contains(Point p) 方法,它可以帮助您确定鼠标点击是否落在您的圈子内。
    8. 您需要决定 _x 和 _y 在哪里代表您的圆的中心点。如果是这样,那么您需要调整绘图,将其向左和向上移动 _radius 量。
    9. 考虑将 Graphics 对象转换为 Graphics2D 对象,以便使用其额外的方法和属性。
    10. 一个这样的属性是 RenderingHings。设置您的 Graphics2D RenderingHints 以允许抗锯齿消除您的图像“锯齿”。这可以通过以下方式完成:g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 其中 g2 是您的 Graphics2D 对象。
    11. 您的paintComponent 方法不是真正的paintComponent 覆盖,因此无法正常工作。它应该是一个protected 方法,而不是public,它应该有一个参数、一个Graphics 对象和第二个参数,并且您应该将@Override 注释放在它上面。

    例如,请查看我的this answer 来解决类似问题。

    以 _x 和 _y 为中心并使用渲染提示的 paintComponent 方法示例:

    class CirclePainter extends JPanel implements Iterable<Circle> {
       private static final int PREF_W = 500;
       private static final int PREF_H = PREF_W;
       private CircleList circleList = new CircleList();
    
       @Override
       protected void paintComponent(Graphics graphics) {
          super.paintComponent(graphics);
          Graphics2D g2 = (Graphics2D) graphics;
          g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
          for (Circle circle : circleList) {
             // if x and y are the center points, then you must subtract the radius.
             int x = circle.getX() - circle.getRadius();
             int y = circle.getY() - circle.getRadius();
             int width = circle.getRadius() * 2;
             int height = width;
             g2.fillOval(x, y, width, height);
          }
       }
    

    【讨论】:

    • 这些看起来不错的建议。我认为解决问题中提到的问题的最佳方法可能取决于您喜欢的学习方式。有些人更喜欢通过编码来学习,而不是先阅读教程。
    • @FreekdeBruijn:是的,实验总是一件好事,但根据我的经验,像 Swing 图形这样的东西所以违反直觉,我们大多数人,包括我自己,都猜错了.在这里,您必须从教程中获得一些线索才能继续前进。顺便给你的答案投一票。
    • 我同意,通过这种使用 Swing 图形的实验,很容易将自己画到角落里。尽管如此,当您尝试构建一些小东西时,有时很难投入大量时间学习。
    • @FreekdeBruijn:还有很多 Swing 图形程序的例子,包括在这个网站上找到的动画,有些是我写的。
    • 太棒了。有人给出的答案实际上回答了我的问题,但是您的帖子很有帮助。谢谢。
    【解决方案2】:

    基于您的代码和Hovercraft Full Of Eels 的建议,通过对 GUI 和 CirclePainter 类进行这些修改,可以朝着正确的方向迈出一小步:

    // GUI.draw
    public void draw(List<Circle> circles)
    {
    //    drawingBoard.paintComponent(drawingBoard.getGraphics(), circles);
        drawingBoard.setCircles(circles);
        drawingBoard.repaint();
    }
    
    
    class CirclePainter extends JPanel
    {
    //    public void paintComponent(Graphics graphics, List<Circle> circles)
    //    {
    //        super.paintComponent(graphics);
    //        for(Circle circle : circles)
    //            graphics.fillOval(circle.getX(), circle.getY(), circle.getRadius() * 2, circle.getRadius() * 2);
    //    }
    
        private List<Circle> circles;
    
        public void setCircles(final List<Circle> circles) {
            this.circles = circles;
        }
    
        @Override
        protected void paintComponent(final Graphics graphics) {
            super.paintComponent(graphics);
            for (Circle circle : circles)
                graphics.fillOval(circle.getX(), circle.getY(), circle.getRadius() * 2, circle.getRadius() * 2);
        }
    }
    

    这样,您可能没有解决所有基本问题,但您的程序只需进行微小的更改即可运行。 Swing 是一个非常不错的库,学习更多内容会很有趣。

    【讨论】:

    • 我认为 Eels 的建议更好......但是这个回答了我的问题。所以……我选择你。谢谢!
    • @ThomYorkkke:所以我的快速而肮脏的修复选择了Hovercraft Full Of Eels的好建议......谢谢。请查看其他示例(如Java Animate JLabel)以查找更多动画代码并祝您的圈子好运!
    • 一切都是为了简单的出路... :-D
    猜你喜欢
    • 2014-04-11
    • 1970-01-01
    • 2011-01-31
    • 2017-07-20
    • 1970-01-01
    • 2021-01-19
    • 1970-01-01
    • 1970-01-01
    • 2017-04-01
    相关资源
    最近更新 更多