JAVA: как я могу заставить это работать без использования потока?

#java #swing #user-interface #try-catch

#java #swing #пользовательский интерфейс #попробуйте-поймайте

Вопрос:

 public class CarGame extends JFrame {
    class MyThread extends Thread{
        private JLabel label;
        private int x, y;
        public MyThread(String fname, int x, int y) {
            this.x = x;
            this.y = y;
            label = new JLabel();
            label.setIcon(new ImageIcon(fname));
            label.setBounds(x, y, 100, 100);
            add(label);
        }
        public void run() {
            for (int i=0; i<200; i  ) {
                x  = 10 * Math.random();
                label.setBounds(x, y, 100, 100);
                repaint();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public CarGame() {
        setTitle("Car Race");
        setSize(600, 200);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(null);
        (new MyThread("car1.gif", 100, 0)).start();
        (new MyThread("car2.gif", 100, 50)).start();
        (new MyThread("car3.gif", 100, 100)).start();
        setVisible(true);
    }
    public static void main(String[] args) {
        CarGame t = new CarGame();
    }
}
  

Привет, я студент университета, и у меня возник вопрос во время изучения JAVA.
Я хочу, чтобы эта автомобильная игра работала без использования какого-либо потока.
Пожалуйста, помогите мне!
Спасибо 🙂

Комментарии:

1. Одна из проблем заключается в том, что графический интерфейс swing запускается в собственном потоке. Вы не можете избежать этого.

2. Кроме того, основной поток всегда есть, от него будет невозможно избавиться.

3. без потоков выполнение невозможно.

4. Возможно, вы захотите просто использовать массив List or, чтобы сохранить JLabels и другие вместе, затем зациклить их все в одном методе и использовать для этого один основной поток. Для этого вам не нужен поток для каждого изображения.

5. В чем смысл этого вопроса??? Что работает или не работает? Вы пытаетесь добиться анимации? Если это так, то вам нужно использовать таймер Swing для планирования анимации.

Ответ №1:

Создайте объект для каждого автомобиля, который содержит его состояние. Поместите все машины в список и повторите его 200 раз:

 import java.util.Arrays;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class CarGame extends JFrame {
    
    class Car {
        private JLabel label;
        private int x, y;
        public Car(String fname, int x, int y) {
            this.x = x;
            this.y = y;
            label = new JLabel();
            label.setIcon(new ImageIcon(fname));
            label.setBounds(x, y, 100, 100);
            add(label);
        }
        
        public void move()
        {
            x  = 10 * Math.random();
            label.setBounds(x, y, 100, 100);
            repaint();          
        }
    }
    
    public CarGame() {
        setTitle("Car Race");
        setSize(600, 200);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(null);
        List<Car> cars=Arrays.asList(new Car("car1.gif", 100, 0),new Car("car2.gif", 100, 50),new Car("car3.gif", 100, 100));
        setVisible(true);
        for(int i=0;i<200;i  )
        {
            cars.forEach(car->car.move());
            try {
                Thread.currentThread().sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        CarGame t = new CarGame();
    }
}
  

У вас всегда будет один поток.

Примечание: использование Thread.sleep() для этой цели не такая хорошая идея, поскольку это заблокирует поток от выполнения другой работы. Лучше было бы использовать таймер, который запускается каждые 100 мс и вызывает cars.foreach(…) . Конечно, это приведет ко второму потоку. Таймер может быть отменен, как только первая (или последняя) машина достигнет финиша. (спасибо @JoopEggen за правильный комментарий)

Для полноты картины (и потому, что это было весело делать): версия без потоков, но с :

  • javax.swing.Таймер, который останавливается после завершения всех машин.
  • подсчитывает конечную позицию каждого автомобиля
  • отображает текст для тех, у кого, например, нет gif-изображения автомобиля.
     import java.util.Arrays;
    import java.util.List;
    import javax.swing.Timer;
    
    import javax.swing.ImageIcon;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    
    public class CarGame extends JFrame {
        int width = 600;
    
        int position=1;
        
        class Car {
            private JLabel label;
            private int x, y;
    
            public Car(String fname, int x, int y) {
                this.x = x;
                this.y = y;
                label = new JLabel(fname ">");
                label.setIcon(new ImageIcon(fname ".gif"));
                label.setBounds(x, y, 100, 100);
                add(label);
            }
    
            public boolean move() {
                if (!finished()) {
                    x  = 10 * Math.random();
                    label.setBounds(x, y, 100, 100);
                    if(finished())
                        label.setText(label.getText() "[" (position  ) "]");
                }
                return finished();
            }
    
            public boolean finished() {
                return x > width;
            }
        }
    
        public CarGame() {
            setTitle("Car Race");
            setSize(width 100, 200);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setLayout(null);
            List<Car> cars = Arrays.asList(new Car("car1", 100, 0), new Car("car2", 100, 50),
                    new Car("car3", 100, 100));
            setVisible(true);
            Timer timer=new Timer(100,null);
            timer.addActionListener(e-> {
                    // count the number of non-finished cars
                    if(cars.stream().map(car -> car.move()).filter(b->!b).count()==0)
                        timer.stop();
            });
            timer.start();
        }
    
        public static void main(String[] args) {
            new CarGame();
        }
    }
  

Комментарии:

1. Вместо ожидания (100) таймер (поток), запланированный каждые 100 мс, тоже был бы хорош.

2. @JoopEggen но, опять же, вы бы использовали несколько потоков (в фоновом режиме, но все же)

3. Вот почему вы получили мой голос, но между режимом сна и таймером выбор кажется очевидным. Режим ожидания также предполагает, что система продолжит работу, а не зависнет. Было бы неплохо упомянуть таймер.

4. Хотя это может работать, это противоречит стандартной практике Swing, которая заключается в том, что все компоненты Swing или состояние компонентов должны обновляться в потоке отправки событий (EDT). Также нет необходимости вызывать repaint() для каждого отдельного компонента при его перемещении. Swing достаточно умен, чтобы вызывать revalidate() и repaint() при изменении свойства компонента, или вы можете просто вызвать repaint на всей панели после перемещения каждого компонента. Таким образом, использование таймера Swing является правильным решением.