Swing应用程序初始化和加载屏幕方法 [英] Swing application initialization and loading screen approach

查看:162
本文介绍了Swing应用程序初始化和加载屏幕方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我制作了很多不同的Swing应用程序,它们的加载时间通常在几秒到几分钟之间变化,具体取决于应用程序UI /数据大小。此外,在某些情况下,应用程序数据加载与UI加载混合在一起。

I have made quite a lot of various Swing apps and their loading time usually vary between just a few seconds and minutes depending on application UI/data size. Also in some cases application data loading is mixed with UI loading.

几秒钟的加载时间不是问题,但是当它超过让我们说10秒时 - 它很明显,在UI /数据完全初始化之前,应该显示某种加载屏幕。

A few seconds load time is not an issue, but when it is longer than let's say 10 seconds - it is obvious that some kind of loading screen should be displayed until UI/data is fully initialized.

您通常会做什么 - 首先创建某种加载屏幕(例如,带有徽标的窗口和一些在加载应用程序时正在更新的标签)并从中更新各种加载点在您的应用程序中。

What you would usually do - create some kind of loading screen first (for example window with logo and some label that is being updated while application is loading) and update it from various loading "points" in your application.

问题是 - 应用程序通常在排队到EDT的单个调用中加载,很难将其分成对EDT的多次调用没有使应用程序的代码复杂化。因此,由于应用程序加载是在排队到EDT的单个调用中执行的,因此无法正确更新加载屏幕 - 在EDT忙于加载应用程序之前,应用程序初始化之前不会显示更新。

The problem is - application is usually loaded within single call queued into EDT and it is hard to separate it into multiply calls to EDT without complicating the application's code. So since application loading is performed in a single call queued to EDT you simply cannot update the loading screen properly - update won't be displayed until the application is initialized since the EDT is busy with loading the application.

因此,为了在某些情况下实现加载屏幕,我已经在EDT之外移动了应用程序UI初始化,并且设计它们的方式是在执行加载时不会执行任何UI更新。应用程序的框架显示和所有应用程序UI操作仍将在EDT中执行。这通常不太好,但经过大量测试并查看Swing代码后,我确信它不会导致任何问题,即使在大型应用程序上也是如此。尽管如此,即使它没有引起任何问题,这也不是一件好事。

So to implement loading screen in some cases I have moved application UI initialization outside of the EDT and have designed them in the way that it won't do any UI update stuff while loading is performed. Application's frame display and all application UI actions would still be performed in EDT. This is not too good generally, but after lots of testing and looking through the Swing code I know for sure that it doesn't cause any issues even on large applications. Still, that isn't a good to do generally even if it doesn't cause any issues.

所以问题是:可以使用哪些方法来正确显示和更新应用程序加载屏幕,同时在EDT中保持应用程序初始化?

希望它不是太广泛。

这是一个虚拟应用程序,它展示了一种坏方法:

Here is a "dummy" application that showcases a "bad" approach:

import javax.swing.*;
import java.awt.*;

public class DummyApplication extends JFrame
{
    private static JDialog loadingDialog;
    private static JLabel loadingProgress;

    public DummyApplication ()
    {
        super ( "Dummy application" );

        dummyProgressUpdate ( "Loading content...", 3000 );

        final JLabel label = new JLabel ( "Custom content" );
        label.setBorder ( BorderFactory.createEmptyBorder ( 100, 100, 100, 100 ) );
        getContentPane ().add ( label );

        dummyProgressUpdate ( "Loading settings...", 3000 );

        setDefaultCloseOperation ( WindowConstants.EXIT_ON_CLOSE );
        pack ();
        setLocationRelativeTo ( null );

        dummyProgressUpdate ( "Opening application...", 1000 );
    }

    private static void dummyProgressUpdate ( final String status, final int time )
    {
        SwingUtilities.invokeLater ( () -> loadingProgress.setText ( status ) );
        dummyLoadTime ( time );
    }

    private static void dummyLoadTime ( final long time )
    {
        try
        {
            Thread.sleep ( time );
        }
        catch ( final InterruptedException e )
        {
            e.printStackTrace ();
        }
    }

    public static void main ( final String[] args ) throws Exception
    {
        // Displaying loading screen from EDT first
        SwingUtilities.invokeAndWait ( () -> {
            loadingDialog = new JDialog ( ( Window ) null, "Loading screen" );
            loadingProgress = new JLabel ( "Initializing application...", JLabel.CENTER );
            loadingProgress.setBorder ( BorderFactory.createLineBorder ( Color.LIGHT_GRAY ) );
            loadingDialog.getContentPane ().setLayout ( new BorderLayout () );
            loadingDialog.getContentPane ().add ( loadingProgress );
            loadingDialog.setUndecorated ( true );
            loadingDialog.setAlwaysOnTop ( true );
            loadingDialog.setModal ( false );
            loadingDialog.setSize ( 400, 100 );
            loadingDialog.setLocationRelativeTo ( null );
            loadingDialog.setVisible ( true );
        } );

        // Initializing application outside of the EDT
        final DummyApplication applicationFrame = new DummyApplication ();

        // Displaying application from the EDT
        SwingUtilities.invokeLater ( () -> {
            loadingDialog.setVisible ( false );
            applicationFrame.setVisible ( true );
        } );
    }
}

前段时间我发现这个有趣的东西在JDK7:

http:/ /sellmic.com/blog/2012/02/29/hidden-java-7-features-secondaryloop/

Also some time ago I found this interesting stuff implemented in JDK7:
http://sellmic.com/blog/2012/02/29/hidden-java-7-features-secondaryloop/

那个 SecondaryLoop 功能允许在EDT线程中阻止进一步的代码执行,而不会导致UI卡住。它与在EDT中打开时的模态JDialog基本相同。

That SecondaryLoop feature allows to block futher code execution in EDT thread without causing the UI to stuck. It is basically the same what modal JDialog does when opened within EDT.

因此,通过此功能,我发现可能有更好的方法来解决此案例:

So with this feature I found probably a better way to solve the case:

import javax.swing.*;
import java.awt.*;

public class DummyApplication extends JFrame
{
    private static JDialog loadingDialog;
    private static JLabel loadingProgress;

    public DummyApplication ()
    {
        super ( "Dummy application" );

        dummyProgressUpdate ( "Loading content...", 3000 );

        final JLabel label = new JLabel ( "Custom content" );
        label.setBorder ( BorderFactory.createEmptyBorder ( 100, 100, 100, 100 ) );
        getContentPane ().add ( label );

        dummyProgressUpdate ( "Loading settings...", 3000 );

        setDefaultCloseOperation ( WindowConstants.EXIT_ON_CLOSE );
        pack ();
        setLocationRelativeTo ( null );

        dummyProgressUpdate ( "Displaying application...", 1000 );
    }

    private static void dummyProgressUpdate ( final String status, final int time )
    {
        // Use SecondaryLoop to block execution and force loading screen update
        final SecondaryLoop loop = Toolkit.getDefaultToolkit ().getSystemEventQueue ().createSecondaryLoop ();
        SwingUtilities.invokeLater ( () -> {
            loadingProgress.setText ( status );
            loop.exit ();
        } );
        loop.enter ();

        // Perform dummy heavy operation
        dummyLoadTime ( time );
    }

    private static void dummyLoadTime ( final long time )
    {
        try
        {
            Thread.sleep ( time );
        }
        catch ( final InterruptedException e )
        {
            e.printStackTrace ();
        }
    }

    public static void main ( final String[] args ) throws Exception
    {
        // Displaying loading screen from EDT first
        SwingUtilities.invokeAndWait ( () -> {
            loadingDialog = new JDialog ( ( Window ) null, "Loading screen" );
            loadingProgress = new JLabel ( "Initializing application...", JLabel.CENTER );
            loadingProgress.setBorder ( BorderFactory.createLineBorder ( Color.LIGHT_GRAY ) );
            loadingDialog.getContentPane ().setLayout ( new BorderLayout () );
            loadingDialog.getContentPane ().add ( loadingProgress );
            loadingDialog.setUndecorated ( true );
            loadingDialog.setAlwaysOnTop ( true );
            loadingDialog.setModal ( false );
            loadingDialog.setSize ( 400, 100 );
            loadingDialog.setLocationRelativeTo ( null );
            loadingDialog.setVisible ( true );
        } );

        // Initializing and displaying application from the EDT
        SwingUtilities.invokeLater ( () -> {
            final DummyApplication applicationFrame = new DummyApplication ();
            loadingDialog.setVisible ( false );
            applicationFrame.setVisible ( true );
        } );
    }
}

如你所见 - 在 dummyProgressUpdate 方法我已经为我的案例做了一些棘手的解决方法 - 基本上我阻止在EDT中执行的执行并等待排队到EDT的单独调用将更新加载屏幕。

As you can see - in dummyProgressUpdate method I've done some tricky workaround for my case - basically I am blocking execution that performed in EDT and waiting for a separate call queued into EDT that would update loading screen.

这实际上有效 - 虽然我不确定这是一件好事,是否会引起更大规模的副作用。此外,只有在进行强制更新时才会更新加载屏幕,这意味着如果加载屏幕(例如)某些动画正在运行 - 它将无法正常显示,并且只会与文本一起更新。 / p>

And that actually works - though I am not sure that this is a good thing to do and whether it might cause any side-effects on a larger scale. And also the loading screen in that case will only be updated when that force update is made which means that if the loading screen has (for example) some animation running - it won't display properly and will only be updated together with text.

推荐答案

如果您正在加载数据或必须连接到远程服务或其他冗长的应用程序,那么 SwingWorker 是可用的approches的简单方法,因为它通过 setProgress 方法和 PropertyChangeListener 支持。

If you're loading data or having to connect to remote services or other lengthy applications, then a SwingWorker is the simpler of the available approches, as it provides progress support via its setProgress method and PropertyChangeListener support.

它通过它的进程提供UI同步 / 发布方法,允许您安全地更新UI和完成方法和/或 PropertyChangeListener 可以用于确定工作完成的时间

It provides UI synchronisation via it's process/publish methods, allowing you to safely update the UI and the done method and/or the PropertyChangeListener can be used to determine when the worker is done

如果您正在尝试加载UI元素,那么您应该考虑延迟加载方法。

If all you're doing is trying to loading UI elements then you should consider a lazy loading approach.

所以不要一次加载所有的UI元素,特别是当用户可能实际上没有看到UI的各个部分时,只有当你必须加载并保存时才更好地加载这些元素你自己的时间。

So rather then loading ALL the UI elements at once, especially when the user may not actually look at sections of the UI, it is a much better idea to load those elements only when you absolutely have to and save yourself the time.

使用 CardLayout JTabbedPane ,只有当视图的UI和数据实际可见时,才能更好地初始化视图。

When using things like CardLayout or JTabbedPane, this is a much better to only initialise the UI and data of the views when they actually become visible.

如果你真的想要,你也可以卸载它当视图不再可见时,断开侦听器与数据源的连接,将其停止以响应对用户实际看不到的数据源的更改...

If you really want, you could also have it unload when the view is no longer visible, disconnecting listeners from the data source, stopping it form responding to changes to the data source that the user will not actually see...

这篇关于Swing应用程序初始化和加载屏幕方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆