Мяч не двигается;Нить? - PullRequest
       38

Мяч не двигается;Нить?

0 голосов
/ 22 сентября 2019

Это пользовательский интерфейс, который заставляет мяч падать по диагонали, но мяч остается неподвижным;Кажется, что-то не работает с нитями.Подскажите, пожалуйста, как заставить мяч двигаться?

Пожалуйста, загрузите мяч и измените каталог, чтобы программа могла найти, где находится ваш мяч.Не обязательно скачивать футбольное поле, но если хотите, все в порядке.Наконец, я должен поблагодарить вас за то, что вы потратили время на поиски этой неисправности.

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 ]

2 голосов
/ 22 сентября 2019

Йоханнес уже говорил о многих вещах, которые не соответствуют вашему первоначальному примеру, поэтому я не буду повторять многие из них снова.

В этом примере используется Swing Timer вместо Thread в качестве основного цикла анимации.Он также фокусируется на демонстрации инкапсуляции и ответственности.

Например, AnimtionPane отвечает за управление шарами, управление циклом анимации и рисование.Тем не менее, он не отвечает за определение того, «как» шары обновляются или рисуются, он лишь обеспечивает время и функциональность для того, чтобы эти вещи происходили.

Я вижу несколько вопиющих проблем:

  • Попытка загрузить ресурсы из метода paintComponent.Это плохие идеи, так как это может замедлить прохождение краски, что приведет к задержке
  • вашего пользовательского интерфейса при вызове repaint и updateUI из метода paintComponent.Следует избегать появления каких-либо новых обновлений пользовательского интерфейса во время процесса рисования.Это может привести к тому, что ваша программа будет работать широко и потреблять все циклы ЦП, что сделает не только ваше приложение не реагирующим, но и всю систему.

Некоторые очень быстрые точки

  • Swing не безопасен для потоков.Вы никогда не должны обновлять пользовательский интерфейс (или все, на что он опирается) вне контекста потока диспетчеризации событий.В этом примере используется Swing Timer, поскольку он позволяет задержке EDT (а не блокировать пользовательский интерфейс), но его обновления инициируются в EDT, что позволяет нам безопасно обновлять пользовательский интерфейс из
  • Вы создаете несколько экземпляров 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();
        }

    }

}
2 голосов
/ 22 сентября 2019

Итак, чтобы сломать ваш код:

Когда кнопка нажата, вы выполняете следующий код:

Image ball = null;
new Layout().createEllipse(ball);
runnable = new Layout();
move = new Thread(runnable);
move.start();

Это создаст новый макет.Метод run() этого увеличит переменные X и Y.Они объявлены здесь:

int 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();

Это может иметь некоторыепроблемы с параллелизмом (Swing не является потокобезопасным), поэтому для правильной меры вы должны вызвать repaint() в 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() из метода paint.Они вам не нужны.

Для изображений в paint: вы должны их кешировать.Храните их в поле, чтобы вам не приходилось каждый раз загружать картинку.

Когда все это будет сделано, ваш код станет намного чище, но все же есть некоторые проблемы, с которыми нужно бороться.Но, по крайней мере, у вас что-то работает.

...