将 Swing Timer 与 gif 图像同步
Posted
技术标签:
【中文标题】将 Swing Timer 与 gif 图像同步【英文标题】:synchronize Swing Timer with gif Image 【发布时间】:2016-03-12 09:44:43 【问题描述】:我有一个 gif 图像以无限循环的形式显示在 JPanel 上。现在我需要在随机数量的帧之后停止动画。实际上,我生成了一个可以是 0 或 1 的随机数。假设 gif 由 6 帧组成。如果数字是 0,我想在第 3 帧停止,如果是 1,动画应该在第 6 帧停止。
为了实现这一点,我尝试使用 Swing 计时器,它会在下一帧到来时准确触发事件。因此,如果帧有 50 毫秒的延迟,我会像
那样构造定时器new Timer(50, this);
遗憾的是,这似乎不起作用,实际上动画似乎比定时器慢。 (我认为这与加载时间有关。)无论如何,我添加了一些代码来说明问题和(失败)解决方法。
import java.awt.event.*;
import javax.swing.*;
public class GifTest extends JPanel implements ActionListener
ImageIcon gif = new ImageIcon(GifTest.class.getResource("testgif.gif"));
JLabel label = new JLabel(gif);
Timer timer = new Timer(50, this);
int ctr;
public GifTest()
add(label);
timer.setInitialDelay(0);
timer.start();
@Override
public void actionPerformed(ActionEvent e)
ctr++;
if (ctr == 13)
timer.stop();
try
Thread.sleep(1000);
catch (InterruptedException i)
public static void main(String[] args)
JFrame frame = new JFrame("Gif Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new GifTest());
frame.setSize(150,150);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
对于giftest.gif,它是一个简单的6层,上面有数字1到6,延迟50ms保存。
如果有任何帮助,我将不胜感激。
Ps:如果事实证明没有优雅的方法可以做到这一点,那么检索当前显示的 Frame 也足够了。这样我就可以要求它并在它是第 3 帧(分别是第 6 帧)时停止。由于任务的上下文,我更喜欢我的解决方案的修改版本。
【问题讨论】:
最坏的情况,从 GIF 中解压图像并使用 Swing Timer 显示每个图像。 你为什么要 Thread.sleep(1000) ? 只是为了防止动画很快继续,所以我可以看到第 13 帧。 “为了实现这一点,我尝试使用 Swing 计时器,它会在下一帧到来时触发事件” - 这不是真的,SwingTimer
只保证它会至少在指定时间后调用
感谢 MadProgrammer。现在,每次执行 actionPerformed 方法时,我都会调用 System.currentTimeMillis(),实际上,在 Timer 延迟为 50 毫秒的情况下,实际方法调用在最后一次调用后的 20 到 130 毫秒之间变化(100 次观察)。因此,我认为不能以这种方式完成。那么有没有办法检索当前显示的 gif 图像的 Frame 呢?
【参考方案1】:
您可以如上所述解压并存储在一组图像中(简单明了)。 您还可以使用 ImageObserver 接口 使用更高级的选项:ImageObserver 通过名为的特殊 API 提供对加载过程的监控:imageUpdate(Image img, int infoflags, int x, int y, int width, int height) 您可以使用此 API 跟踪进度,如下所示:
ImageIcon gif = new ImageIcon();
JLabel label = new JLabel(gif);
ImageObserver myObserver = new ImageObserver()
public boolean imageUpdate(Image image, int flags, int x, int y, int width, int height)
if ((flags & HEIGHT) != 0)
System.out.println("Image height = " + height);
if ((flags & WIDTH) != 0)
System.out.println("Image width = " + width);
if ((flags & FRAMEBITS) != 0)
System.out.println("Another frame finished.");
if ((flags & SOMEBITS) != 0)
System.out.println("Image section :" + new Rectangle(x, y, width, height));
if ((flags & ALLBITS) != 0)
System.out.println("Image finished!");
if ((flags & ABORT) != 0)
System.out.println("Image load aborted...");
label.repaint();
return true;
;
gif.setImageObserver( myObserver );
gif.setImage(GifTest.class.getResource("testgif.gif"));
您可以使用return false;
停止加载过程
更新:(使用 ImageReader) ImageObserver 使用起来不太直观。 每次需要重新绘制时都会更新,并会触发完整的动画序列。 尽管您可以在某个点停止它,但它每次都会从第一张图像开始执行。
另一种解决方案是使用ImageReader:ImageReader
可以将 GIF 解包为BufferedImages
的序列。
然后,您可以根据需要使用 Timer 控制整个序列。
String gifFilename = "testgif.gif";
URL url = getClass().getResource(gifFilename);
ImageInputStream iis = new FileImageInputStream(new File(url.toURI()));
ImageReader reader = ImageIO.getImageReadersByFormatName("GIF").next();
// (reader is actually a GIFImageReader plugin)
reader.setInput(iis);
int total = reader.getNumImages(true);
System.out.println("Total images: "+total);
BufferedImage[] imgs = new BufferedImage[total];
for (int i = 0; i < total; i++)
imgs[i] = reader.read(i);
Icon icon = new ImageIcon(imgs[i]);
// JLabel l = new JLabel(icon));
【讨论】:
谢谢@Leet-Falcon。据我了解,只要显示下一帧,就应该调用 imageUpdate 方法?!这对我来说真的不起作用。在方法中打印一个递增的计数器并与我的 JPanel 上显示的实际 gif 进行比较,表明这两个仍然是异步的。以上是关于将 Swing Timer 与 gif 图像同步的主要内容,如果未能解决你的问题,请参考以下文章
GIF图像格式简介(87a和89a)(C语言生成GIF图像)
用Python编写录屏程序将播放的视频用截屏方法转换为多帧图像编辑后保存为GIF格式动图文件