【问题标题】:paintComponent not working油漆组件不工作
【发布时间】:2013-06-04 01:27:26
【问题描述】:

这可能是一个愚蠢的问题,但我如何调用paintComponent?它根本不显示对象。它在公共类 Ball 中扩展了 JPanel 实现 Runnable。

public class Balls {

    public static void main(String[] args) {
        new Balls();
    }

    public Balls() {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame("Balls!");
                frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
                frame.add(new ballAdder());
                frame.setSize(1000, 1000);
                frame.setVisible(true);

            }
        });
    }

    public class ballAdder extends JPanel {

        public ballAdder() {
            add(new Ball(5, 5));

        }
    }

    public class Ball extends JPanel implements Runnable {

        public int x, y;
        public int speedx, speedy;
        public int width = 40, height = 40;

        public Ball(int x, int y) {
            this.x = x;
            this.y = y;
            new Thread(this).start();

        }

        public void move() {
            x += speedx;
            y += speedy;
            if (0 > x || x > 950) {
                speedx = -speedx;
            }
            if (0 > y || y > 950) {
                speedy = -speedy;
            }
            repaint();
        }

        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.BLACK);
            g.fillOval(x, y, width, height);
        }

        public void run() {
            while (true) {
                move();
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

【问题讨论】:

    标签: java swing paintcomponent


    【解决方案1】:

    您不应该自己打电话给paintComponent(或paint)。这是由RepaintManager 完成的。

    您实际遇到的问题是 speedxspeedy0,这意味着您的球永远不会移动...

    另一个问题是ballAdder 类正在使用FlowLayout,而您Ball 类没有提供有关其首选大小的任何详细信息,这意味着Ball 面板的首选大小为0x0 .

    评论

    您的设计存在可扩展性问题。除了因为布局问题您会发现很难在 UI 中添加一个以上的球...

    每个Ball 都有自己的线程。这意味着,添加的球越多,运行的线程就越多。这将持续消耗资源并影响应用程序的性能。

    最好提供一个Drawable 对象的概念,该对象知道它应该在其容器的概念中显示在何处,并且可以是paintComponent 中的painted。通过使用单个javax.swing.Timer,它应该更有能力支持越来越多的随机球。

    第一次修复

    要解决您的第一个问题,您可以执行以下操作...

    public class ballAdder extends JPanel {
        public ballAdder() {
            setLayout(new BorderLayout());
            add(new Ball(5, 5));
        }
    }
    

    此修复的问题在于,您只能在容器上拥有一个 Ball,因为它需要占用最大的可用空间。

    您可能希望阅读Using Layout Managers 了解更多详情

    (可能的)更好的解决方案

    (可能的)更好的解决方案是使用单个JPanel 作为“球坑”,它维护对球列表的引用。

    然后您将使用BallPitPanepaintComponent 方法绘制所有球(在球列表中)。

    通过使用单个javax.swing.Timer,您可以遍历球列表并更新那里的位置(在BallPitPane 的上下文中

    恕我直言,这比尝试与布局管理器或编写自己的布局管理器更容易......

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class Bounce {
    
        public static void main(String[] args) {
            new Bounce();
        }
    
        public Bounce() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new BallPitPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class BallPitPane extends JPanel {
    
            private List<Ball> balls;
            private Random rand;
    
            public BallPitPane() {
                rand = new Random(System.currentTimeMillis());
                balls = new ArrayList<>(25);
                Timer timer = new Timer(40, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (balls.isEmpty()) {
                            balls.add(new Ball(BallPitPane.this));
                        }
    
                        if (rand.nextBoolean()) {
                            balls.add(new Ball(BallPitPane.this));
                        }
    
                        for (Ball ball : balls) {
                            ball.move();
                        }
                        repaint();
                    }
                });
                timer.start();
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                for (Ball ball : balls) {
                    ball.paint(g2d);
                }
                g2d.dispose();
            }
        }
    
        protected static int random(int min, int max) {
    
            return (int)Math.round(Math.random() * (max - min)) + min;
    
        }
    
        public static class Ball {
    
            public static final int WIDTH = 10;
            public static final int HEIGHT = 10;
    
            private int x;
            private int y;
    
            private int deltaX;
            private int deltaY;
    
            private Color color;
            private BallPitPane parent;
    
            public Ball(BallPitPane parent) {
                this.parent = parent;
                x = parent.getWidth() / 2;
                y = parent.getHeight() / 2;
    
                deltaX = random(-4, 4);
                deltaY = random(-4, 4);
    
                color = new Color(random(0, 255), random(0, 255), random(0, 255));
            }
    
            public void move() {
                x += deltaX;
                y += deltaY;
    
                if (x + WIDTH > parent.getWidth()) {
                    x = parent.getWidth() - WIDTH;
                    deltaX *= -1;
                } else if (x < 0) {
                    x = 0;
                    deltaX *= -1;
                }
                if (y + HEIGHT > parent.getHeight()) {
                    y = parent.getHeight() - HEIGHT;
                    deltaY *= -1;
                } else if (y < 0) {
                    y = 0;
                    deltaY *= -1;
                }
            }
    
            public Color getColor() {
                return color;
            }
    
            public void paint(Graphics2D g2d) {
    
                g2d.setColor(getColor());
                g2d.fillOval(x, y, WIDTH, HEIGHT);
                g2d.setColor(Color.BLACK);
                g2d.drawOval(x, y, WIDTH, HEIGHT);
    
            }        
        }    
    }
    

    【讨论】:

    • 它一开始就没有显示出来
    • 它实际上有一个很小的框架,fillRect(0, 0, 1000, 1000); System.out.println(getBounds());返回 java.awt.Rectangle[x=487,y=5,width=10,height=10] 和一个可见的矩形,但由于某种原因 fillOval 不起作用。
    • +1,存在宽度和高度是因为 FlowLayout 在每个组件周围提供了 5 个像素的垂直/水平间隙。 FlowLayout 将球的大小设置为其首选大小,即 (0, 0),因此无需绘制任何内容。
    • 当我将paintComponet放在“ballAdder”类中时它可以工作,但在其他任何地方都没有,所以很困惑
    • @arynaq 尝试为Ball 面板添加边框,您会发现它永远不会被渲染...
    猜你喜欢
    • 1970-01-01
    • 2018-09-15
    • 1970-01-01
    • 2013-03-10
    • 1970-01-01
    相关资源
    最近更新 更多