球不动;线?

时间:2019-09-22 01:46:31

标签: java multithreading user-interface

这是一个UI,它使球以对角线的方式下降,但是球保持静止;似乎某些线程无法正常工作。你能告诉我如何使球移动吗?

请下载一个球并更改目录,以便程序可以找到您的球的分配位置。无需下载足球场,但如果需要,可以。最后,我要感谢您花费时间寻找这种故障。

import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import java.io.File;

class Animation extends JFrame implements ActionListener {  //Frame and listener

  Rectangle2D dimensions = new Rectangle2D.Double(0,0,850,595);  //Not implemented limits
  JButton animate, stop;
  Runnable runnable;
  Thread move;

    public Animation() {
      setLayout(new BorderLayout());  //BorderLayout disposition
      setTitle("Pelota en acción");      

        animate = new JButton("Animate it!");  //Button to create balls
          animate.setBounds(0,0,120,30);
          animate.addActionListener(new ActionListener(){
            @Override
              public void actionPerformed(ActionEvent e) {
                Image ball = null;
                new Layout().createEllipse(ball);
                runnable = new Layout();
                move = new Thread(runnable);
                  move.start();
               }
          });

          stop = new JButton("Freeze");  //Button to interrupt thread (not implemented)
          stop.setBounds(0,0,120,30);
          stop.addActionListener(new ActionListener(){
            @Override
              public void actionPerformed(ActionEvent e) {
                  move.interrupt();
                  Layout.running = false;
              }
          });

        JPanel subPanel = new JPanel();  //Layout with its buttons situated to the south
          subPanel.add(animate);
          subPanel.add(stop);
        add(subPanel,BorderLayout.SOUTH);

        add(new Layout());
    }

    public static void main(String[] args) {
        Animation ventana = new Animation();
          ventana.setSize(850,625);
          ventana.setLocationRelativeTo(null);
          ventana.setVisible(true);
          ventana.setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

  @Override
    public void actionPerformed(ActionEvent e) {} //Tag
}  //Class close


class Layout extends JPanel implements Runnable {  //Layout and thread

  int X,Y;  //Coordenadas
  static boolean running = true;  //"To interrupt the thread" momentaneously.
  static ArrayList<Image> balls = new ArrayList<>();  //Balls collection

  @Override
    public void run () {  //Just moves ball towards Narnia xd
        while(running) {
          X++; Y++;
            System.out.println(X+" "+Y);
            repaint();
            updateUI();
            try {
              Thread.sleep(4);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

  @Override
     public void paintComponent(Graphics g) {
      super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;
        g2.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
          repaint();
          updateUI();

        try {
            URL url = new URL("https://www.freejpg.com.ar/image-900/9c/9ca2/F100004898-textura_pasto_verde_linea_de_cal.jpg");
            Image picture = ImageIO.read(url);
              g.drawImage(picture,0,0,null);
        } catch(IOException e){
            System.out.println("URL image was not found");
        }
        finally {
          try {     
        //----------------------------------------------------------------------------
              Image picture = ImageIO.read(new File("C:\\Users\\Home\\Desktop\\Cancha.jpg")); //Pitch
        //----------------------------------------------------------------------------    
                g.drawImage(picture, 0, 0, null);
          } catch (IOException ex) {
              System.out.println("Pitch image was not found");
          }
        }

        for (Image ball : balls) {  //I add balls to the Layout
          g2.drawImage(ball,X,Y,100,100,null);
        }
    }

    public void createEllipse (Image ball) {  //Method that adds balls to the collection
        try {
        //--------------------------------------------------------------------   Ball
            ball = ImageIO.read(new File("C:\\Users\\Home\\Desktop\\Pelota.png"));  //Change this
       //--------------------------------------------------------------------   Ball
        } catch(IOException ex) {
            System.out.println("Any balls were found");
        }
        balls.add(ball);
    }
}

2 个答案:

答案 0 :(得分:2)

所以要分解您的代码:

按下按钮后,您将执行以下代码:

run()

这将创建一个新的布局。这种X方法将增加Yint X,Y; //Coordenadas 变量。它们在这里声明:

Layout

这些是实例变量,这意味着它们属于您新创建的repaint()

然后,您在新的Layout上调用Layout,这将无济于事,因为此新版式尚未添加到某些窗口中。

那么,如何解决这个问题?

首先,您必须保留原始class Animation extends JFrame { // no need to implement ActionListener Rectangle2D dimensions = new Rectangle2D.Double(0,0,850,595); //Not implemented limits JButton animate, stop; Thread move; Layout layout;

// before: add(new Layout());
layout = new Layout();
add(layout);

然后在创建布局时记住它:

ActionListener

然后使用layout.createEllipse(ball); move = new Thread(layout); move.start(); 中的布局:

repaint()

这可能在并发方面存在一些问题(Swing并非线程安全的),因此,为达到良好的效果,您应该在AWTEventThread中调用// in run(), was repaint(): EventQueue.invokeLater(new Runnable() { @Override public void run() { repaint(); } });

  @Override
public void actionPerformed(ActionEvent e) {} //Tag

现在,还有一些清理任务:
删除此代码:

ActionListener

不再需要它,因为您没有实现static

从某些字段中删除volatile修饰符,然后添加volatile int X,Y; //Coordenadas volatile boolean running = true; //"To interrupt the thread" momentaneously. ArrayList<Image> balls = new ArrayList<>(); //Balls collection

volatile
从多个线程访问的变量需要

repaint()

还要从resetUI()方法中删除paintpaint。您不需要它们。

对于DocSchema.pre('find', function() { this.where({$isDeleted: { $ne: true }}); }) 中的图片:您应该将其缓存。将它们存储在一个字段中,因此您不必每次都加载图片。

完成所有这些操作后,您的代码将更加整洁,但仍有一些缺陷需要解决。但是至少您有一些工作要做。

答案 1 :(得分:2)

约翰内斯已经讲过许多与您的原始示例有误的地方,所以我不再赘述。

此示例使用Swing Timer代替Thread作为主要的“动画”循环。它还着重于展示封装和责任。

例如,AnimtionPane负责管理球,管理动画循环和绘画。但是,它并不负责确定球的“更新方式”或“油漆方式”,它只是提供了使这些事情发生的时间和功能。

我可以看到几个明显的问题:

  • 尝试从paintComponent方法中加载资源。这是个坏主意,因为它可能会减慢绘画速度,导致UI滞后
  • repaint方法中调用updateUIpaintComponent。您应该避免在绘制过程中引起对UI的任何新更新。这可能导致您的程序运行范围广,并消耗所有CPU周期,不仅使您的应用程序无响应,而且使整个系统无响应。

一些非常快的观点

  • 摆动不是线程安全的。您永远不要从事件调度线程的上下文外部更新UI(或UI依赖的任何内容)。本示例使用Swing Timer,因为它允许EDT发生延迟(而不是阻止UI),但是它的更新是在EDT中触发的,从而使我们可以从内部安全地更新UI。
  • 您创建Layout的多个实例,这意味着屏幕上的实例不是已更新的实例
  • 您的“冻结”逻辑已损坏。它永远不会“冻结”任何东西

可运行的示例

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.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private AnimationPane animationPane;

        public TestPane() {
            setLayout(new BorderLayout());

            animationPane = new AnimationPane();

            JButton actionButton = new JButton("Start");
            actionButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent evt) {
                    if (animationPane.isAnimating()) {
                        animationPane.stop();
                        actionButton.setText("Start");
                    } else {
                        animationPane.start();
                        actionButton.setText("Stop");
                    }
                }
            });

            add(animationPane);
            add(actionButton, BorderLayout.SOUTH);
        }

    }

    // This is just makes it seem more random ;)
    private static Random RANDOM = new Random();

    public class Ball {

        private int x;
        private int y;

        private int xDelta;
        private int yDelta;

        private Color color;

        private Shape shape;

        public Ball(Color color) {
            shape = new Ellipse2D.Double(0, 0, 10, 10);
            this.color = color;

            // Get some random motion
            do {
                xDelta = RANDOM.nextInt(6) + 2;
                yDelta = RANDOM.nextInt(6) + 2;
            } while (xDelta == yDelta);
        }

        public void update(Rectangle bounds) {
            x += xDelta;
            y += yDelta;

            if (x + 10 > bounds.x + bounds.width) {
                x = bounds.x + bounds.width - 10;
                xDelta *= -1;
            } else if (x < bounds.x) {
                x = bounds.x;
                xDelta *= -1;
            }
            if (y + 10 > bounds.y + bounds.height) {
                y = bounds.y + bounds.height - 10;
                yDelta *= -1;
            } else if (y < bounds.y) {
                y = bounds.y;
                yDelta *= -1;
            }
        }

        public void paint(Graphics2D g2d) {
            // This makes it easier to restore the graphics context
            // back to it's original state
            Graphics2D copy = (Graphics2D) g2d.create();
            copy.setColor(color);
            copy.translate(x, y);
            copy.fill(shape);
            // Don't need the copy any more, get rid of it
            copy.dispose();
        }
    }

    public class AnimationPane extends JPanel {

        // This does not need to be static
        private List<Ball> balls = new ArrayList<>();  //Balls collection
        private Timer timer;

        private List<Color> colors;

        public AnimationPane() {
            colors = new ArrayList<>(8);
            colors.add(Color.RED);
            colors.add(Color.GREEN);
            colors.add(Color.BLUE);
            colors.add(Color.CYAN);
            colors.add(Color.MAGENTA);
            colors.add(Color.ORANGE);
            colors.add(Color.PINK);
            colors.add(Color.YELLOW);
            timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent evt) {
                    if (RANDOM.nextBoolean()) {
                        makeBall();
                    }
                    Rectangle bounds = new Rectangle(new Point(0, 0), getSize());
                    for (Ball ball : balls) {
                        ball.update(bounds);
                    }
                    repaint();
                }
            });
            makeBall();
        }

        protected void makeBall() {
            Collections.shuffle(colors);
            balls.add(new Ball(colors.get(0)));
        }

        public boolean isAnimating() {
            return timer.isRunning();
        }

        public void start() {
            timer.start();
        }

        public void stop() {
            timer.stop();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g.create();
            g2.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
            // Bad ideas.  Repaint will cause a new paint event to be posted, causing your
            // UI to run away - consuming all your CPU cycles in a singulator forms
            // and destorys the known universe
            //repaint();
            // This doesn't do what you think it does and there shouldn't be
            // reason for you to call it
            //updateUI();

            // This is a bad idea as it could cause the paint cycles to slow down
            // destorying the responsiveness of your app
            // Besids, you should be passing this as the ImageObserver
//            try {
//                URL url = new URL("https://www.freejpg.com.ar/image-900/9c/9ca2/F100004898-textura_pasto_verde_linea_de_cal.jpg");
//                Image picture = ImageIO.read(url);
//                g.drawImage(picture, 0, 0, null);
//            } catch (IOException e) {
//                System.out.println("URL image was not found");
//            } finally {
//                try {
//                    //----------------------------------------------------------------------------
//                    Image picture = ImageIO.read(new File("C:\\Users\\Home\\Desktop\\Cancha.jpg")); //Pitch
//                    //----------------------------------------------------------------------------    
//                    g.drawImage(picture, 0, 0, null);
//                } catch (IOException ex) {
//                    System.out.println("Pitch image was not found");
//                }
//            }
            // This is "bad" per say, but each ball should have it's own
            // concept of location
//            for (Image ball : balls) {  //I add balls to the Layout
//                g2.drawImage(ball, X, Y, 100, 100, null);
//            }
            for (Ball ball : balls) {
                ball.paint(g2);
            }
            // I made a copy of the graphics context, as this is shared
            // with all the other components been painted, changing the
            // render hints could cause issues
            g2.dispose();
        }

    }

}