【问题标题】:How can I make a slow loop?我怎样才能做一个慢循环?
【发布时间】:2020-07-04 08:53:46
【问题描述】:

我已经用 Java 制作了一个 GUI,并查看了如何减慢循环速度。在线的所有方法都不起作用,并且 GUI 在触发循环时冻结,这也发生在没有行来减慢循环的情况下。这是我的代码(主要位和变量更改为 coin 系统):

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import javax.swing.JButton;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class GUI implements ActionListener {
    static int coins = 0;
    static int autoCoinsLevel = 0;
    int autoCoinsNextLevelUpgradeCost = 100;
    String versionRing = "Alpha";
    String versionNumber = "1.2";
    JLabel coinText;
    JFrame frame;
    JPanel panel;
    JLabel autoCoinsCostText;
    
    public GUI() {
        frame = new JFrame();
        
        JButton button = new JButton("Click me!");
        JButton autoClickUpgradeButton = new JButton("Auto Generator - Level " + autoCoinsLevel);
        button.addActionListener(this);
        autoCoinsUpgradeButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if(coins >= autoCoinsNextLevelUpgradeCost && autoCoinsLevel < 9) {
                    autoCoinsLevel++;
                    autoCoinsUpgradeButton.setText("Auto Coin Maker - Level " + autoClickLevel);
                    coins = coins - autoCoinsNextLevelUpgradeCost;
                    coinText.setText("Coins: " + coins);
                    autoCoinsNextLevelUpgradeCost = autoCoinsNextLevelUpgradeCost * 10;
                    autoCoinsCostText.setText("Auto Coin Maker Upgrade Cost: " + autoCoinsNextLevelUpgradeCost + " Coins");
                    frame.pack();
                    while(autoCoinsLevel > 0) {
                        if(autoCoinsLevel == 1) {
                            coins++;
                            // delay 1 seconds
                        } else if(autoCoinsLevel == 2) {
                            coins++;
                            // delay .75 seconds
                        }
                        // and so on until `autoCoinsLevel == 9`
                    }
                }
            }
        });
        
        coinText = new JLabel("Coins: 0");
        autoCoinsCostText = new JLabel("Auto Coin Maker Upgrade Cost: " + autoCoinsNextLevelUpgradeCost + " Coins");
        
        panel = new JPanel();
        panel.setBorder(BorderFactory.createEmptyBorder(60, 100, 30, 100));
        panel.setLayout(new GridLayout(0, 1));
        panel.add(button);
        panel.add(coinsText);
        panel.add(autoCoinsUpgradeButton);
        panel.add(autoCoinsCostText);
        
        frame.add(panel, BorderLayout.CENTER);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setTitle("Clicker Clicker - " + versionRing + " " + versionNumber);
        frame.pack();
        frame.setVisible(true);
    }
    
    public static void main(String[] args) {
        new GUI();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // increase coins script
    }
}

我不知道为什么应用程序在我尝试的大多数方法中都停止响应,而且根本没有任何减慢速度的方法也会使应用程序挂起...

我是非常 Java 新手,所以解释一下 thisthat 的作用会帮助我更好地理解。 :)

【问题讨论】:

  • 你永远不想减慢摆动主线程。这就是控制你的gui的东西。您想使用计时器线程进行计时,同时让 swing 线程处理 gui 的其余部分。
  • 该应用程序由单线程支持,如果它忙于循环,则无法处理其他事情......以延迟您不必阻止的操作线程,但是推迟操作,看类java.util.Timer
  • @rascio "..看看 java.util.Timer" 类因为所有的 Swing/AWT 应用程序都应该在 Event Dispatch Thread 上创建和更新,所以使用javax.swing.Timer 确保在 EDT 上执行其中的操作。
  • 啊,对不起,我从未使用过 swing :D 我不知道它的具体课程。那么是的,它是正确的,没有并发问题。
  • 您的 autoCoinsLevel 正在 EDT 中更新,但在主线程中可能看不到该更新(可能是由于托管了 while 循环)。该变量应声明为volatile。我在答案部分提供了此行为的演示。

标签: java swing loops


【解决方案1】:

正如@NomadMaker 指出的那样,最好创建一个单独的线程,因为主线程控制 GUI。

我会按如下方式创建一个 WaitThread 类

public class WaitThread extends Thread{
    private long waitInMs;

    public WaitThread(long waitInMs){
        this.waitInMs = waitInMs;
    }


    @Override
    public void run() {
        try {
            System.out.println("Waiting for "+this.waitInMs+" ms");
            sleep(this.waitInMs);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

然后从主线程如下

// Note: Looks like this is a forever running while loop 
while(autoCoinsLevel > 0) {
        
        if(autoCoinsLevel == 1) {
           coins++;
           // delay 1 seconds
           WaitThread waitThread = new WaitThread(1*1000);
           waitThread.start();
           waitThread.join();
        } else if(autoCoinsLevel == 2) {
           coins++;
           // delay .75 seconds
           WaitThread waitThread = new WaitThread((long)0.75*1000);
           waitThread.start();
           waitThread.join();
        }
        // and so on until `autoCoinsLevel == 9`
}

【讨论】:

  • 感谢您的回答!很容易理解这有很大帮助(我非常缺乏经验,对 Java 一点也不了解)。这样做会打印出Waiting for 1000 ms 行,但GUI 变得无响应。无论如何要让它在被触发后保持响应?
  • 在代码中添加这样的 [System.out.println("\"Coins\" is currently at " + coins)] 行后,控制台显示coins 变量正在增加,每秒增加 1 个;这正是我想要的。但是,GUI不会响应
【解决方案2】:

您是变量autoCoinslevel,并且可能其他人正在 EDT 中进行更新。主线程对此一无所知,因此看不到这些值的最新更新。在下面查看volatile 关键字的不同之处。

  • 首先按原样运行程序。单击该按钮会增加 autoCoinsLevel,但不会像预期的那样改变循环行为 - 当计数为奇数时打印,但当计数为偶数时停止。
  • 然后把int autoCoinsLevel = 0改成volatile int autoCoinsLevel = 0看看有什么区别。
import java.awt.Dimension;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class VolatileDemo extends JPanel {
    JFrame f = new JFrame();
    
    int autoCoinsLevel = 0;
    
    public VolatileDemo() {
        f.add(this);
        setPreferredSize(new Dimension(200, 50));
        JButton b = new JButton("Increment AutoCoinsLevel");
        b.addActionListener(ae -> {
            autoCoinsLevel++;
            System.out.println(
                    "ActionListener entered\nautoCoinsLevel = " + autoCoinsLevel);
        });
        add(b);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
    
    public static void main(String[] args) {
        new VolatileDemo().start();
    }
    public void start() {
        while (autoCoinsLevel < 10) {
            if (autoCoinsLevel % 2 == 1) {
                System.out.println(
                        "autoCoinLevels = " + autoCoinsLevel);
            }
        }
        System.out.println("Done with while loop");
    }
}

【讨论】:

    猜你喜欢
    • 2021-10-25
    • 2019-09-01
    • 2020-08-14
    • 1970-01-01
    • 2013-08-18
    • 1970-01-01
    • 1970-01-01
    • 2021-04-13
    • 2021-09-27
    相关资源
    最近更新 更多