提问者:小点点

在java中显示Gif动画


你好,我写一个GUI应用程序Java1.6与摇摆。

我有一个弹出屏幕,应该显示一个gif动画,而我的Swing gui正在加载,也有点之后。

我的弹出屏幕是一个JDialog。动画应该显示在以以下方式添加到Jdialog的JLabel上:

ImageIcon myImgIcon = getMyImgIcon();
JLabel imageLbl = new JLabel(myImgIcon);
add(imageLbl, BorderLayout.CENTER); 

现在的问题是动画只在gui加载后显示。我相信当GUI加载时(这在我的应用程序中是一个繁重的操作),EDT太忙了,无法运行动画。

请参见如何使用线程显示动画GIF图像。

现在的问题是,让GUI加载在不同的线程(不是EDT)上是错误的,所以我不知道如何解决这个问题。

有人有主意吗?


共3个答案

匿名用户

您只需将一些繁重的任务从EDT线程中释放出来,并在单独的线程中执行它们。在这种情况下,gif动画将与其他正在运行的进程一起工作。

您也可以在单独的线程中创建您的应用程序界面(是的,是的,不在EDT内),但仅在您显示它之前。之后,您应该在EDT内进行所有更改,否则您可能会遇到很多问题。

您也可以稍后在单独的线程中加载更多的UI元素,只需确保将它们添加到EDT内显示的框架/容器中-这是最重要的事情。

这是一个“重样”界面加载的小例子:

public static void main ( String[] args ) throws InvocationTargetException, InterruptedException
{
    // Main window

    final JFrame frame = new JFrame ();

    final JPanel panel = new JPanel ( new FlowLayout ( FlowLayout.LEFT, 5, 5 ) )
    {
        public Dimension getPreferredSize ()
        {
            Dimension ps = super.getPreferredSize ();
            ps.width = 0;
            return ps;
        }
    };
    frame.add ( new JScrollPane ( panel ) );

    frame.setSize ( 600, 500 );
    frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
    frame.setLocationRelativeTo ( null );

    SwingUtilities.invokeAndWait ( new Runnable ()
    {
        public void run ()
        {
            frame.setVisible ( true );
        }
    } );

    // Load dialog

    final JDialog load = new JDialog ( frame );

    JPanel panel2 = new JPanel ( new BorderLayout () );
    panel2.setBorder ( BorderFactory.createEmptyBorder ( 15, 15, 15, 15 ) );
    load.add ( panel2 );

    final JProgressBar progressBar = new JProgressBar ( 0, 100 );
    panel2.add ( progressBar );

    load.setModal ( false );
    load.pack ();
    load.setLocationRelativeTo ( frame );

    SwingUtilities.invokeAndWait ( new Runnable ()
    {
        public void run ()
        {
            load.setVisible ( true );
        }
    } );

    // Heavy task (takes approx. 10 seconds + some time on buttons creation) 

    for ( int i = 0; i < 100; i++ )
    {
        Thread.sleep ( 100 );

        final JButton button = new JButton ( "Button" + i );
        final int finalI = i;

        // Updating panel and progress in EDT
        SwingUtilities.invokeLater ( new Runnable ()
        {
            public void run ()
            {
                panel.add ( button );
                button.revalidate ();
                progressBar.setValue ( finalI );
            }
        } );
    }
}

如您所见-所有接口更新操作都是在EDT中进行的,其他所有操作都在另一个线程中运行。

还要注意主线程不是EDT线程,所以我们可以马上在那里做一些重要的事情。

在某些情况下,不需要立即显示加载的界面部分,因此您可以在“繁重”操作结束时将它们全部添加在一起。这将节省一些加载时间,并使初始化代码更加简单。

关于EDT的简要说明和我在答案中所说的…

…这是我在Swing L工作三年后发现的东西

如您所知——界面更新的单线程(Swing中的EDT)的整个想法是将每个单独的组件视觉更新(及其事件)保存在一个队列中,并在该线程中一个接一个地执行它们。这主要是为了避免绘画问题,因为单个框架内的每个组件都被绘制到保存在内存中的单个图像中。那里的绘画顺序是严格的,因此一个组件不会在最终图像上覆盖另一个组件。绘画顺序取决于通过在另一个容器中添加一些组件或容器来创建的组件树(这是在Swing上创建任何应用程序接口时所做的基本事情)。

总而言之,您必须将所有视觉更新(可能导致它们的方法/操作)保留在EDT中。其他任何事情都可以在EDT之外完成——例如,您可以在EDT之外准备应用程序界面(同样,除非您在已经可见的容器中添加/删除/移动组件)。

在一些非常非常罕见的情况下,仍然可能存在一些内部问题。很久以前,这个问题在这里有一个很好的讨论:
http://www.velocityreviews.com/forums/t707173-why-does-jdk-1-6-recommend-creating-swing-components-on-the-edt.html

简而言之:自第六JDK版本以来,Sun在文档中表示,即使是Swing组件的创建也应该在EDT中进行,以避免可能出现的问题。由于组件创建时发生的事件,它们可能会出现在某些接口创建繁重的特定情况下。

无论如何,我想说,在某些情况下,你可以在EDT之外创建界面,以避免加载器/应用程序卡住。在其他情况下,当应用程序在界面创建时间内卡住并不重要时,你应该使用EDT。我不能说任何更具体的事情,因为一切都取决于你的情况…

匿名用户

也许您正在尝试制作一个动画,该动画将在应用程序开始时播放,而不会干扰即将发生的事件或组件。因此,您可能想尝试启动屏幕。从这里阅读:http://docs.oracle.com/javase/tutorial/uiswing/misc/splashscreen.html

在上面的链接中,它演示了一个名为SplashScreen的类的用法,该类刚刚从Frame类派生而来。所以机制是这样的:你显示一个单独的框架(闪屏,你的动画在这里),一段时间后你的主应用程序启动。

匿名用户

“ImageIcon”类允许您加载gif动画。我使用“getResource()”加载图像。为此,我通常URL类来传递文件路径。正如URL的名称所暗示的那样,该路径在远程机器中不需要。

URL url = this.getClass().getResource(path);
Icon myImgIcon = new ImageIcon(url);
JLabel imageLbl = new JLabel(myImgIcon);
component.add(imageLbl, BorderLayout.CENTER);

path将是class文件夹内gif的路径。

参考:http://docs.oracle.com/javase/tutorial/uiswing/components/icon.html#getresource