【问题标题】:Swing Splash screen with progress bar带有进度条的 Swing 闪屏
【发布时间】:2012-08-13 11:07:52
【问题描述】:

这是我的初始屏幕代码,

public class SplashScreen extends JWindow {

private static final long serialVersionUID = 1L;
private BorderLayout borderLayout = new BorderLayout();
private JLabel imageLabel = new JLabel();
private JProgressBar progressBar = new JProgressBar(0, 100);

public SplashScreen(ImageIcon imageIcon) {
    imageLabel.setIcon(imageIcon);
    setLayout(borderLayout);
    add(imageLabel, BorderLayout.CENTER);
    add(progressBar, BorderLayout.SOUTH);
    pack();
    setLocationRelativeTo(null);
}

public void showScreen() {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            setVisible(true);
        }
    });
}

public void close() {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            setVisible(false);
            dispose();
        }
    });
}

public void setProgress(final String message, final int progress) {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            progressBar.setValue(progress);
            if (message == null) {
                progressBar.setStringPainted(false);
            } else {
                progressBar.setStringPainted(true);
            }
            progressBar.setString("Loading " + message + "...");
        }
    });
}
}

从我这样调用的主要方法中,

public static void main(String[] args) {

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            try {
                UIManager.setLookAndFeel(UIManager
                        .getSystemLookAndFeelClassName());

                SplashScreen splashScreen = new SplashScreen(new ImageIcon("images/splash.jpg"));
                splashScreen.showScreen();

                AppFrame frame = new AppFrame(splashScreen);

            } catch (Exception e) {
                appLogger.error(e.getMessage(), e); 
            }
        }
    });
}

在我调用的 AppFrame 的构造函数中, splashScreen.setProgress(msg, val) 更新进度条的方法。但飞溅没有显示。它仅在最后显示帧仅显示几分之一秒,即使加载需要很长时间。但是如果我把这三行

SplashScreen splashScreen = new SplashScreen(new ImageIcon("images/splash.jpg"));
splashScreen.showScreen();
AppFrame frame = new AppFrame(splashScreen);

在 invokeLater() 之外,会显示初始屏幕并且进度条会很好地更新。我相信 GUI 更新应该在 invokeLater 中。可能是什么问题?

顺便说一句,AppFrame 会加载我的应用程序的各种面板。

编辑: 我的 AppFrame 的模拟如下所示。

public class AppFrame extends JFrame {

public AppFrame(SplashScreen splashScreen) {
    JPanel test = new JPanel();
    test.setLayout(new GridLayout(0, 10));

    splashScreen.setProgress("jlabel", 10);
    for(int i = 0; i < 10000; i++) {
        test.add(new JButton("Hi..." + i));
        splashScreen.setProgress("jbutton", (int)(i * 0.1));
    }
    add(new JScrollPane(test));
    setPreferredSize(new Dimension(800, 600));
    pack();
    setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    setLocationRelativeTo(null);
    splashScreen.setProgress("complete", 100);
    setVisible(true);
}
}

【问题讨论】:

  • " 我认为 GUI 更新应该在 invokeLater 中。"invokeAndWait。但是做一些记录,我希望你会发现完成splashScreen.showScreen();AppFrame frame = new AppFrame(splashScreen); 之间的时间比你预期的要少得多。另请注意,最好的飞溅是严格的 AWT(没有 Swing)。
  • 这意味着我应该使用 Window 或 Frame 而不是 JWindow?谢谢。
  • 是的,使用WindowFrame。还要查看 MediaTracker/CanvasEventQueue - 必须没有使用 Swing 导入或类,或者首先加载整个 Swing 包。
  • 还可以考虑使用java-web-start 启动画面。
  • 嗯……真的是ui创建需要这么长时间吗?如果没有,请提取 real 时间消耗 (!ui = !EDT) 并在 SwingWorker 的 doInBackground 中准备它们,将中间进度发布到启动画面并在完成时关闭启动画面/显示应用程序跨度>

标签: java swing splash-screen jprogressbar invokelater


【解决方案1】:

嗯,看看我放在一起的这个工作示例,它使用SplashScreen 类(仅使用简单的TimerActionListenerProgressBar 的值增加到100 并显示一个框架) :

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Font;
import java.awt.event.ActionListener;
import javax.swing.*;

public class SplashScreen extends JWindow {

    private static JProgressBar progressBar = new JProgressBar();
    private static SplashScreen splashScreen;
    private static int count = 1, TIMER_PAUSE = 25,PROGBAR_MAX=100;
    private static Timer progressBarTimer;
    ActionListener al = new ActionListener() {

        @Override
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            progressBar.setValue(count);
            System.out.println(count);
            if (PROGBAR_MAX == count) {
                splashScreen.dispose();//dispose of splashscreen
                progressBarTimer.stop();//stop the timer
                createAndShowFrame();
            }
            count++;//increase counter

        }
    };

    public SplashScreen() {
        createSplash();
    }

    private void createSplash() {
        Container container = getContentPane();

        JPanel panel = new JPanel();
        panel.setBorder(new javax.swing.border.EtchedBorder());
        container.add(panel, BorderLayout.CENTER);

        JLabel label = new JLabel("Hello World!");
        label.setFont(new Font("Verdana", Font.BOLD, 14));
        panel.add(label);

        progressBar.setMaximum(PROGBAR_MAX);
        container.add(progressBar, BorderLayout.SOUTH);


        pack();
        setLocationRelativeTo(null);
        setVisible(true);

        startProgressBar();
    }

    private void startProgressBar() {
        progressBarTimer = new Timer(TIMER_PAUSE, al);
        progressBarTimer.start();
    }

    private void createAndShowFrame() {
        JFrame frame = new JFrame();
        frame.setSize(500, 500);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                splashScreen = new SplashScreen();
            }
        });
    }
}

【讨论】:

    【解决方案2】:

    从另一个invokeLater 调用invokedLaterrunnable 只有在第一个执行完成后才会执行。

    您应该像这样修改 Splashscreen 代码:

    ...
    private void runInEdt(final Runnable runnable) {
        if (SwingUtilities.isEventDispatchThread())
            runnable.run();
        else
            SwingUtilities.invokeLater(runnable);
    }
    
    public void showScreen() {
        runInEdt(new Runnable() {
            public void run() {
                setVisible(true);
            }
        });
    }
    
    public void close() {
        runInEdt(new Runnable() {
            public void run() {
                setVisible(false);
                dispose();
            }
        });
    }
    
    public void setProgress(final String message, final int progress) {
        runInEdt(new Runnable() {
            public void run() {
                progressBar.setValue(progress);
                if (message == null)
                    progressBar.setStringPainted(false);
                else
                    progressBar.setStringPainted(true);
                progressBar.setString("Loading " + message + "...");
            }
        });
    }
    

    【讨论】:

    • 我尝试了您的解决方案。由于它已经在 EDT 中,否则 'runInEdt' 中的部分将不会执行。无论如何,我仍然有同样的问题。 EDT 中的进度条未更新。我将其用作example,并在main() 中添加了invokeLater。没有 invokeLater 它工作正常。
    • 'runInEDT' 方法可确保您的 Splashscreen 是线程安全的。它可以从任何线程调用。我不明白为什么我的解决方案不适合你。也许您可以提供有关 AppFrame 构造函数内容的更多详细信息。
    • 请查看我的 AppFrame 模拟的编辑。基本上,我在该构造函数中加载了我的整个应用程序组件。所以我想在组件加载时更新进度条。但即使加载需要很长时间,它也没有显示。
    • 您确定这种模拟行为与真实行为相同吗?我的意思是,通常加载时间不是由于 gui 初始化。
    • 感谢一位团队成员的 dao 代码在 EDT 中运行。
    【解决方案3】:
        Thread l_splash_thread = new Thread(
                new SplashScreen ());
        l_splash_thread.start();
    
        try {
            l_splash_thread.join();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
    

    我认为这样你可以握住你的启动屏幕。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-03
      • 1970-01-01
      相关资源
      最近更新 更多