您好我正在使用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是错误的,所以我不知道如何解决这个问题。
有没有人有想法?
答案 0 :(得分:7)
你只需要释放一些繁重任务的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线程,因此我们可以立即做一些重做。
在某些情况下,不需要立即显示界面的加载部分,因此您可以在&#34; heavy&#34;的末尾添加它们。操作。这将节省一些加载时间,并使初始化代码更加简单。
关于EDT的简要解释以及我在答案中所说的内容......
......这是我在Swing L&amp; F和大量基于Swing的应用程序工作三年后找到的东西。我挖掘了很多Swing资源,发现了很多有趣的东西,这些东西并不广为人知。
如您所知 - 用于接口更新的单线程(其在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
简而言之:自第6个JDK版本Sun在文档中声明,即使Swing组件创建也应在EDT内完成,以避免可能的问题。它们可能出现在某些特定情况下,由于在创建组件时发生的事件,因此会创建大量的接口。
无论如何,我说在某些情况下你可能会在EDT之外创建你的界面,以避免加载器/应用程序卡住。在其他情况下,如果应用程序在创建接口时遇到问题并不重要 - 您应该使用EDT。我不能说更具体的事情,因为一切都取决于你的情况......
答案 1 :(得分:3)
也许您正在尝试制作一个仅在应用程序开始时播放的动画,而不会干扰即将发生的事件或组件。因此,您可能想尝试启动屏幕。从这里阅读:http://docs.oracle.com/javase/tutorial/uiswing/misc/splashscreen.html
在上面的链接中,它演示了一个名为SplashScreen
的类的用法,它只是派生自Frame类。所以机制是这样的:你显示一个单独的框架(启动画面,你的动画在这里),并在一段时间后你的主应用程序启动。
答案 2 :(得分:1)
'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
答案 3 :(得分:0)
在您的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);