【问题标题】:Displaying Gif animation in java在java中显示Gif动画
【发布时间】:2012-09-15 23:43:14
【问题描述】:

您好,我正在使用 Swing 在 Java 1.6 上编写一个 GUI 应用程序。

我有一个弹出屏幕,它应该在我的 Swing gui 加载时以及稍后显示一个 gif 动画。

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

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

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

How do I show a animated GIF image using a thread

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

有人有想法吗?

【问题讨论】:

标签: java swing gif


【解决方案1】:

您只需释放一些繁重任务的 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&F 和许多基于 Swing 的应用程序下工作三年后发现的。我挖掘了很多 Swing 的资源,发现了很多鲜为人知的有趣的东西。

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

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

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

简而言之:自从 JDK 版本 6 以来,Sun 在文档中声明即使是 Swing 组件的创建也应该在 EDT 内完成以避免可能的问题。由于在创建组件时发生的事件,它们可能会出现在某些创建大量接口的特定情况下。

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

【讨论】:

  • 嗯,谢谢!那会奏效。不过我有一个问题要问你。网上找到的每一个官方文档+教程都明确地反复说摇摆组件只能在 EDT 上操作。这是我第一次看到有人说只要他们是隐形的就没有关系。您能否向我解释一下,如果组件不可见,为什么可以打破 EDT 规则。谢谢!
  • @user1155122 我在答案中添加了描述,因为它对于单个评论来说太大了
  • 我像疯了一样在网上搜索,你是唯一给我一些关于这个主题的实际有用信息的来源。谢谢!!
  • @user1155122 如果您对推进 Swing 和 Graphics 感兴趣,我建议您查找/购买一本名为“Filthy Rich Clients”(filthyrichclients.org)的书并从头到尾阅读。您将在那里找到完整的 Swing/Java2D 描述 - Sun/Oracle 文档和其他在线资源永远不会为您提供如此完整和有用的解释。
【解决方案2】:

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

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

【讨论】:

  • 我真的很想用这个闪屏!我真的愿意!但是,有一个小问题。我希望在 main() 方法退出后,当我想要它时处理启动画面。根据文档,当 main() 退出时它会自动退出!我怎样才能禁用它。有什么想法吗?
  • @user1155122:当 main() 存在时,启动屏幕应该被处理掉。 main() 应该做的就是启动 Swing 事件调度线程,然后退出。
【解决方案3】:

'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 将是类文件夹中 gif 的路径。

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

【讨论】:

  • 请更正:this.getClass().getResource(path)
【解决方案4】:

在你的 JFrame 或其他任何东西中,使用这个:

Icon imgIcon = new ImageIcon(this.getClass().getResource("ajax-loader.gif"));
JLabel label = new JLabel(imgIcon);
label.setBounds(669, 42, 45, 15); // You can use your own values
frame.getContentPane().add(label);

来源:How to display an animated gif in java swing

【讨论】:

    猜你喜欢
    • 2011-02-25
    • 2013-03-14
    • 1970-01-01
    • 2012-06-07
    • 2013-03-22
    • 2019-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多