【问题标题】:Java splash screen not working on Mac OSX but working on a PCJava 闪屏在 Mac OSX 上不工作,但在 PC 上工作
【发布时间】:2017-03-15 19:50:11
【问题描述】:

下面的代码是我用 Eclipse 开发的一个小型 Java 应用程序的启动画面的实现。启动画面在 PC 上运行良好,但在 MAC 上却不行。在 MAC OSX 上,首先出现的帧在 2 秒内有一个灰色区域,然后在剩下的 4 秒内出现图像。图像通常应立即出现并持续 4 秒。您知道为什么图像出现在 MAC 上之前会出现延迟,而在 PC 上一切正常吗? PS:我已将应用程序部署为可执行 Jar,并且在所有计算机上都使用 Java 8。谢谢。

public static void main(String[] args)
{
    SplashScreen splSplashScreen = new SplashScreen();
    //Main window
    FenetrePrincipale fenetrePrincipale = new FenetrePrincipale();
}
public class SplashScreen extends JWindow
{
    /**
     * Numéro de série
     */
    private static final long serialVersionUID = 1592663893301307318L;

    private final static long TEMP_AFFICHAGE = 4000;

    /**
     * Constructeur par initialisation
     * @param p_Frame Frame
     * @param p_TempsAffichage Temps d'affichage en millisecondes
     */
    public SplashScreen()
    {
        super(new Frame());
        JLabel lblImage = new JLabel(new ImageIcon(this.getClass().getResource("/res/ui/splashScreen.jpg")));

        Container container = this.getContentPane();
        container.add(lblImage, BorderLayout.CENTER);
        pack();

        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        Dimension labelSize = lblImage.getPreferredSize();
        this.setLocation(screenSize.width/2 - (labelSize.width/2), screenSize.height/2 - (labelSize.height/2));

        this.setVisible(true);

        try
        {
            Thread.sleep(TEMP_AFFICHAGE);
        }
        catch (InterruptedException ex)
        {
            ApplicationLogger.getInstance().severe(ex.getLocalizedMessage());
        }
        finally
        {
            this.setVisible(false);
        }
    }
}

【问题讨论】:

  • Thread.sleep() 导致 java 应用程序停止处理消息;这意味着 UI 不会更新。遵循this answer 中的前提,这将有助于您适当地创建工人。
  • 但是图像出现在 Thread.sleep() 结束之前。而且,Thread.sleep() 不应停止图像的加载,因为图像是在 Thread.sleep() 之前加载的 (this.setVisible(true);)。
  • 它会停止重绘,这是导致问题的原因。当你阻塞 UI 线程时,这些事情就会发生。解决办法是不阻塞UI线程
  • 好的,那我应该把 Thread.sleep() 放在更合适的地方吗?如果没有,我怎么能让图片在 4 秒后消失?我不想实现 SplashScreen API。我正在寻找一个非常简单的解决方案。我只想在应用程序启动前的 4 秒内显示一张图片。谢谢

标签: java eclipse macos splash-screen executable-jar


【解决方案1】:

编辑这与原来的答案完全不同;我将在下面留下原始答案。

看起来最初的 JLabel 渲染在 OSX 上很慢 - 删除所有睡眠仍然让我有大约 2 秒的暂停,而 java 渲染标签。所以我们改变了规则。

首先我们创建一个 JPanel 类,该类采用缓冲图像:

class ImgPanel extends JPanel {
    private BufferedImage img;

    public ImgPanel(BufferedImage img) {
        this.img = img;
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(img.getWidth(), img.getHeight());
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(img, 0, 0, null);
    }
}

然后我们像这样更改启动屏幕构造函数:

public SplashScreen()
{
    BufferedImage img = null;
    try {
        img = ImageIO.read(this.getClass().getResource("/res/ui/splashScreen.jpg"));
    } catch (Exception ex) {
    }

    ImgPanel panel = new ImgPanel(img);
    Container container = this.getContentPane();
    container.add(panel);
    pack();

    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    this.setLocation(screenSize.width/2 - (img.getWidth()/2), screenSize.height/2 - (img.getHeight()/2));

    this.setVisible(true);
 }

这会在面板出现时立即渲染图像,而不会暂停。

注意 - 我已经删除了整个睡眠代码以减少混乱 - 逻辑最好在单独的 SwingWorker 中捕获,以执行启动屏幕的隐藏和主屏幕的显示:

class worker extends SwingWorker<String, Object> {
    private final static long TEMP_AFFICHAGE = 4000;
    private SplashScreen splash;
    private FenetrePrincipale principale;

    public worker(SplashScreen splash, FenetrePrincipale principale) {
        this.splash = splash;
        this.principale = principale;
        this.splash.setVisible(true);
    }

    @Override
    public String doInBackground() {
        try
        {
            Thread.sleep(TEMP_AFFICHAGE);
        }
        catch (InterruptedException ex)
        {
            ApplicationLogger.getInstance().severe(ex.getLocalizedMessage());
        }
        return "";
    }

    @Override
    protected void done() {
        splash.setVisible(false);
        principale.setVisible(true);
    }
};

那么主要代码如下:

public static void main(String[] args)
{
    SplashScreen splSplashScreen = new SplashScreen();
    //Main window
    FenetrePrincipale fenetrePrincipale = new FenetrePrincipale();
    worker w = new worker(splSplashScreen, fenetrePrincipale);
    w.execute();
}

原始答案 - 将用于休眠的线程放入 SwingWorker 仍然是一个好主意,因为它可以让您在初始化之前执行实际工作。

好的,使用您的启动代码将睡眠从 gui 线程中移除的简单示例 - 此代码位于 this.setVisible(true) 之后,并替换了 try {} catch {} finally {} 子句:

    this.setVisible(true);

    worker do_work = new worker(this);
    do_work.execute();
}

class worker extends SwingWorker<String, Object> {
    private SplashScreen parent;

    public worker(SplashScreen parent) {
        this.parent = parent;
    }

    @Override
    public String doInBackground() {
        try
        {
            Thread.sleep(TEMP_AFFICHAGE);
        }
        catch (InterruptedException ex)
        {
            ApplicationLogger.getInstance().severe(ex.getLocalizedMessage());
        }
        return "";
    }

    @Override
    protected void done() {
        parent.setVisible(false);
    }
};

我只是创建了一个作为doWorkInBackground 睡眠的 SwingWorker,一旦完成,它就会关闭父框架,即启动画面。

【讨论】:

  • 如前所述,主窗口可以在启动画面仍然可见时打开。如果您想推迟主窗口可见,直到启动画面消失,那么您需要将调用放入 SwingWorker 的“完成”部分。这可以通过将 SwingWorker 移到主类中相对容易地处理,这样它就可以同时引用启动屏幕和主窗口;允许它控制一个的打开和另一个的关闭。
  • 我已经实现了你的 SwingWorker 类,你告诉过你,但没有任何改进。它在 Mac OSX 上根本没有解决问题(但它可以在 PC 上运行)。在我双击 Jar 文件后大约 2 秒内仍然有一个灰色窗口,然后在 Thread.Sleep() 的剩余 4 秒内出现图像。所以,问题不在于我阻塞了 UI 线程(毕竟,所有解决方案都可以在 PC 上运行)。它仅不适用于 Mac OSX。如果您有其他想法,请留言!谢谢!
  • 初始灰色窗口是 JVM 在 JLabel 中渲染图像的时候。特别是在这种情况下,它与线程睡眠无关。我会改变我的答案,使用更轻的东西,比如 jpanel。
  • 谢谢!我尝试了您的解决方案,但我不知道出了什么问题,但启动画面的图像没有出现。屏幕上只出现了一个黑色的小方块。不调用 ImgPanel 类的 paintComponent 方法正常吗?
  • 好的!我得到了它。 ImgPanel 类中的paintComponent 和getPreferredSize 方法缺少@Override 语句。现在图像正确显示。我让你知道它是否适用于 OS X。谢谢!
猜你喜欢
  • 1970-01-01
  • 2013-04-02
  • 2017-02-22
  • 2020-12-26
  • 1970-01-01
  • 2022-09-30
  • 1970-01-01
  • 2019-09-05
  • 1970-01-01
相关资源
最近更新 更多