Java 启动画面在 Mac OSX 上不工作,但在 PC 上工作

Java splash screen not working on Mac OSX but working on a PC

下面的代码是我用 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);
        }
    }
}

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

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

首先我们创建一个 JPanel class 来获取缓冲图像:

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);
    }
};

我只是创建了一个 SwingWorker,它像 doWorkInBackground 一样进行睡眠,一旦结束,它就会关闭父框架,即启动画面。