Java启动屏幕不适用于Mac OSX,但适用于PC

Java启动屏幕不适用于Mac OSX,但适用于PC,java,eclipse,macos,splash-screen,executable-jar,Java,Eclipse,Macos,Splash Screen,Executable Jar,下面的代码是我用Eclipse开发的一个小型Java应用程序的启动屏幕的实现。启动屏幕在PC机上工作得非常好,但在MAC机上却不行。在MAC OSX上,帧首先在2秒内出现灰色区域,然后图像在剩余的4秒内出现。图像通常应立即出现,持续4秒。你知道为什么图像在MAC上出现之前会有延迟,而在PC上一切正常吗?PS:我已经将应用程序部署为可执行Jar,并且在所有计算机上使用Java8。多谢各位 public static void main(String[] args) { SplashScre

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

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

编辑这与原始答案完全不同;我在下面留下最初的答案

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

首先,我们创建一个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);
    }
};

原始答案-将睡眠线程放入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);
    }
};
this.setVisible(true);
工人完成工作=新工人(本);
执行();
}
类worker扩展了SwingWorker{
私人屏幕父母;
公共工作者(SplashScreen家长){
this.parent=parent;
}
@凌驾
公共字符串doInBackground(){
尝试
{
线程睡眠(临时附加);
}
捕获(中断异常例外)
{
ApplicationLogger.getInstance()严重(例如getLocalizedMessage());
}
返回“”;
}
@凌驾
受保护的void done(){
parent.setVisible(false);
}
};

我只是创建了一个SwingWorker,它作为
doWorkInBackground
进行睡眠,一旦睡眠结束,它就会关闭父框架,即启动屏幕。

Thread.sleep()导致java应用程序停止处理消息;这意味着UI不会得到更新。遵循premiss中的操作,该操作应有助于您正确创建辅助线程。但是,该图像显示在线程结束之前。sleep()。而且,Thread.sleep()不应该停止加载映像,因为映像是在Thread.sleep()之前加载的(this.setVisible(true);)。它会停止重新绘制,这是导致问题的原因。当您阻塞UI线程时,这些事情就会发生。解决方案是不阻塞UI线程OK,那么我应该将Thread.sleep()放在更合适的位置吗?如果没有,我如何使图片在4秒后消失?我不希望实现SplashScreen API。我正在寻找一个非常简单的解决方案。我只想在应用程序启动前4秒内显示一张图片。如前所述,主窗口可以在启动屏幕仍然可见时打开。如果您想将主窗口延迟到启动屏幕消失后才显示,那么您需要将调用放入SwingWorker的“完成”部分。通过将SwingWorker移动到main类中,这样就可以相对容易地处理这个问题,这样它就可以同时引用启动屏幕和主窗口;允许它控制一个的打开和另一个的关闭。我已经实现了你告诉我的SwingWorker类,没有任何改进。它在MacOSX上根本解决不了这个问题(但它在PC上工作)。在我双击Jar文件后的大约2秒钟内,仍然会出现一个灰色窗口,然后在Thread.Sleep()的剩余4秒钟内会出现该图像。所以,问题不在于我阻塞了UI线程(毕竟,所有的解决方案都在PC上工作)。它只在MacOSX上不起作用。如果你有其他想法,请发一条消息!非常感谢。当JVM在JLabel中渲染图像时,初始灰色窗口为。在这种情况下,这与线程睡眠无关。我将改变我的答案,使用一些更轻的东西,比如jpanel。谢谢!我尝试了你的解决方案,我不知道出了什么问题,但启动屏幕的图像没有出现。屏幕上只出现一个小小的黑色正方形。不调用ImgPanel类的paintComponent方法是正常的吗?好的!我得到了它。ImgPanel类中的paintComponent和getPreferredSize方法缺少@Override语句。现在图像显示正确。我会让你知道它在OSX上是否有效。谢谢!
    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);
    }
};