在Java上绘制两个球向不同方向移动,但其中一个消失了

在Java上绘制两个球向不同方向移动,但其中一个消失了,java,multithreading,swing,jpanel,Java,Multithreading,Swing,Jpanel,我正在尝试创建一个程序,该程序将绘制两个球,一个在北中心,另一个在南中心。我需要在不同的方向移动球,第一个球在北方随机向南移动,另一个球在南方中心向北方移动。我可以让北中心的球向下移动,但南部的第二个球在被拉出来后立即消失 PS:我需要有两个内部类,分别是Ball1和Ball2。请帮忙。非常感谢。问题 事件调度线程中的while循环,用于调整图形对象的位置 方法中的Thread.sleep 不调用super.paintComponent 在paintComponent方法中更新对象的状态 Swi

我正在尝试创建一个程序,该程序将绘制两个球,一个在北中心,另一个在南中心。我需要在不同的方向移动球,第一个球在北方随机向南移动,另一个球在南方中心向北方移动。我可以让北中心的球向下移动,但南部的第二个球在被拉出来后立即消失

PS:我需要有两个内部类,分别是
Ball1
Ball2
。请帮忙。非常感谢。

问题

  • 事件调度线程中的while循环,用于调整图形对象的位置
  • 方法中的
    Thread.sleep
  • 不调用
    super.paintComponent
  • paintComponent
    方法中更新对象的状态
  • Swing使用一个单线程模型,该模型负责向所有组件发送重绘请求

    在EDT中执行任何停止处理这些事件的操作都将阻止Swing重新绘制UI。这将使动画看起来像是在一个步骤中突然从开始到结束

    请查看以了解更多详细信息。特别是,看一看和

    我要强调第4点-

    您不控制重绘周期。重新绘制请求可能是由于一些您没有要求的原因而提出的,这些原因会导致对象更新超出您的控制范围,或者在您不希望对象更新时。决不能在任何
    paint
    方法中更改UI任何部分的状态

    简单示例

    这是一个非常简单的示例,但它演示了在Swing中制作任何动画所需理解的基本概念

    public class SimpleBouncyBall {
    
        public static void main(String[] args) {
            new SimpleBouncyBall();
        }
    
        public SimpleBouncyBall() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (Exception ex) {
                    }
    
                    JFrame frame = new JFrame("Test");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(new CourtPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class CourtPane extends JPanel {
    
            private Ball ball;
            private int speed = 5;
    
            public CourtPane() {
                Timer timer = new Timer(40, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        Rectangle bounds = new Rectangle(new Point(0, 0), getSize());
                        if (ball == null) {
                            ball = new Ball(bounds);
                        }
                        speed = ball.move(speed, bounds);
                        repaint();
                    }
                });
                timer.setRepeats(true);
                timer.setCoalesce(true);
                timer.start();
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(100, 100);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g); 
                if (ball != null) {
                    Graphics2D g2d = (Graphics2D) g.create();
                    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                    Point p = ball.getPoint();
                    g2d.translate(p.x, p.y);
                    ball.paint(g2d);
                    g2d.dispose();
                }
            }
    
        }
    
        public class Ball {
    
            private Point p;
            private int radius = 12;
    
            public Ball(Rectangle bounds) {
    
                p = new Point();
                p.x = 0;
                p.y = bounds.y + (bounds.height - radius) / 2;
    
            }
    
            public Point getPoint() {
                return p;
            }
    
            public int move(int speed, Rectangle bounds) {
    
                p.x += speed;
                if (p.x + radius >= (bounds.x + bounds.width)) {
    
                    speed *= -1;
                    p.x = ((bounds.x + bounds.width) - radius) + speed;
    
                } else if (p.x <= bounds.x) {
    
                    speed *= -1;
                    p.x = bounds.x + speed;
    
                }
    
                p.y = bounds.y + (bounds.height - radius) / 2;
    
                return speed;
    
            }
    
            public void paint(Graphics2D g) {
                g.setColor(Color.RED);
                g.fillOval(0, 0, radius, radius);
            }
    
        }
    
    }
    
    公共类SimpleBouncyBall{
    公共静态void main(字符串[]args){
    新的SimpleBouncyBall();
    }
    公共SimpleBouncyBall(){
    invokeLater(新的Runnable(){
    @凌驾
    公开募捐{
    试一试{
    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    }捕获(例外情况除外){
    }
    JFrame=新JFrame(“测试”);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(新的CourtPane());
    frame.pack();
    frame.setLocationRelativeTo(空);
    frame.setVisible(true);
    }
    });
    }
    公共类CourtPane扩展了JPanel{
    私人舞会;
    私人内部速度=5;
    公共法庭(){
    计时器计时器=新计时器(40,新ActionListener(){
    @凌驾
    已执行的公共无效操作(操作事件e){
    矩形边界=新矩形(新点(0,0),getSize());
    如果(ball==null){
    球=新球(边界);
    }
    速度=球。移动(速度、边界);
    重新油漆();
    }
    });
    timer.setRepeats(真);
    timer.setCoalesce(真);
    timer.start();
    }
    @凌驾
    公共维度getPreferredSize(){
    返回新维度(100100);
    }
    @凌驾
    受保护组件(图形g){
    超级组件(g);
    如果(球!=null){
    Graphics2D g2d=(Graphics2D)g.create();
    g2d.setRenderingHint(renderingHits.KEY\u抗锯齿,renderingHits.VALUE\u抗锯齿\u开启);
    点p=ball.getPoint();
    翻译(p.x,p.y);
    球形涂料(g2d);
    g2d.dispose();
    }
    }
    }
    公共班级舞会{
    私人点p;
    私有整数半径=12;
    公共球(矩形边界){
    p=新点();
    p、 x=0;
    p、 y=边界.y+(边界.高度-半径)/2;
    }
    公共点getPoint(){
    返回p;
    }
    公共整数移动(整数速度,矩形边界){
    p、 x+=速度;
    如果(p.x+半径>=(bounds.x+bounds.width)){
    速度*=-1;
    p、 x=((bounds.x+bounds.width)-半径)+速度;
    
    }否则如果(p.x为什么你需要有两个内部类?你的代码缩进到处都是。请理解你要求志愿者投入精力和时间来帮助你。在这样做的时候,如果你首先投入精力发布格式良好的代码,我们将不胜感激。为什么让我们难以阅读和理解你编写的代码posted?类不应该以这种方式使用。你应该将颜色传递给类的构造函数,并合并
    Ball1
    Ball2
    ,同时,
    x
    y
    应该是
    Ball
    的成员。我建议你删除上面的代码,重新开始,因为它离你想做的事情不远。因为我假设您正在调用
    Thread.sleep(…)
    方法中的
    paintComponent(…)
    ,这是不应该执行的操作。请在本网站中搜索使用Swing计时器的Swing动画示例,因为这正是您想要执行的操作。无论是使用Swing计时器还是后台线程,都不要调用
    sleep(…)
    在Swing线程上,尤其是在绘制方法中。不要忘记添加“糟糕的代码格式设置”问题。1+