SWT 表/树查看器单元格中的动画 GIF
题
http://www.java2s.com/Code/Java/SWT-JFace-Eclipse/DisplayananimatedGIF.htm 描述了如何在 SWT 中显示动画 GIF - 一般情况。虽然代码可以工作并且很容易理解,但我在使用该技术在 SWT/JFace 表/树查看器单元格中显示动画 GIF 时遇到了严重的问题。-> 下面的所有代码
本质上,我实现了自己的 OwnerDrawLabelProvider,它在 Paint(Event, Object) 中创建一个 ImageLoader 并启动一个动画线程。问题似乎是这个动画线程是 不是 UI 线程,我不知道在其 run() 方法中使用哪个 GC 或 Display 实例。
我尝试在线程的构造函数中创建一个单独的 GC 实例(派生自 event.gc),但是一旦我退出调试器,线程就无法写入该 GC...
Sat Jan 9 22:11:57 192.168.1.6.local.home java[25387] : CGContextConcatCTM: invalid context 0x0 2010-01-09 22:12:18.356 java[25387:17b03] It does not make sense to draw an image when [NSGraphicsContext currentContext] is nil. This is a programming error. Break on _NSWarnForDrawingImageWithNoCurrentContext to debug. This will be logged only once. This may break in the future. Sat Jan 9 22:12:41 192.168.1.6.local.home java[25387] : CGContextConcatCTM: invalid context 0x0
我需要如何处理这种情况?
以下是相关代码部分:
/* Called by paint(Event, Object). */ private void paintAnimated(final Event event, final ImageLoader imageLoader) { if (imageLoader == null || ArrayUtils.isEmpty(imageLoader.data)) { return; } final Thread animateThread = new AnimationThread(event, imageLoader); animateThread.setDaemon(true); animateThread.start(); } private class AnimationThread extends Thread { private Display display; private GC gc; private ImageLoader imageLoader; private Color background; public AnimationThread(final Event event, final ImageLoader imageLoader) { super("Animation"); this.display = event.display; /* * If we were to simply reference event.gc it would be reset/empty by the time it's being used * in run(). */ this.gc = new GC(event.gc.getDevice()); this.imageLoader = imageLoader; this.background = getBackground(event.item, event.index); } @Override public void run() { /* * Create an off-screen image to draw on, and fill it with the shell background. */ final Image offScreenImage = new Image(this.display, this.imageLoader.logicalScreenWidth, this.imageLoader.logicalScreenHeight); final GC offScreenImageGC = new GC(offScreenImage); offScreenImageGC.setBackground(this.background); offScreenImageGC.fillRectangle(0, 0, this.imageLoader.logicalScreenWidth, this.imageLoader.logicalScreenHeight); Image image = null; try { /* Create the first image and draw it on the off-screen image. */ int imageDataIndex = 0; ImageData imageData = this.imageLoader.data[imageDataIndex]; image = new Image(this.display, imageData); offScreenImageGC.drawImage(image, 0, 0, imageData.width, imageData.height, imageData.x, imageData.y, imageData.width, imageData.height); /* * Now loop through the images, creating and drawing each one on the off-screen image before * drawing it on the shell. */ int repeatCount = this.imageLoader.repeatCount; while (this.imageLoader.repeatCount == 0 || repeatCount > 0) { switch (imageData.disposalMethod) { case SWT.DM_FILL_BACKGROUND: /* Fill with the background color before drawing. */ offScreenImageGC.setBackground(this.background); offScreenImageGC.fillRectangle(imageData.x, imageData.y, imageData.width, imageData.height); break; case SWT.DM_FILL_PREVIOUS: // Restore the previous image before drawing. offScreenImageGC.drawImage(image, 0, 0, imageData.width, imageData.height, imageData.x, imageData.y, imageData.width, imageData.height); break; } imageDataIndex = (imageDataIndex + 1) % this.imageLoader.data.length; imageData = this.imageLoader.data[imageDataIndex]; image.dispose(); image = new Image(this.display, imageData); offScreenImageGC.drawImage(image, 0, 0, imageData.width, imageData.height, imageData.x, imageData.y, imageData.width, imageData.height); // Draw the off-screen image. this.gc.drawImage(offScreenImage, 0, 0); /* * Sleeps for the specified delay time (adding commonly-used slow-down fudge factors). */ try { int ms = imageData.delayTime * 10; if (ms
我将同样的问题发布到 SWT 新闻组 http://www.eclipse.org/forums/index.php?t=tree&th=160398
解决方案
经过几个小时令人沮丧的反复试验后,一位同事想出了一个可行的解决方案。我最初在完全独立的 LabelProvider 中实现此方法的方法惨败。
一种不起作用的方法是重写 LabelProvider#update() 并调用timerExec(100, new Runnable() {...viewer.update()...从该方法内部。它的“生命”周期很难控制,并且它使用了太多的 CPU 周期(在我的 MacBook 上为 10%)。
同事的想法之一是实现一个自定义的 TableEditor:带有图像(GIF 动画的一帧)但没有文本的标签。每个 TableEditor 实例都会启动自己的线程,在其中更新标签的图像。这工作得很好,但是每个动画图标都有一个单独的“动画”线程。此外,这还是一个性能杀手,在我的 MacBook 上消耗了 25% 的 CPU。
最终方法包含三个构建块
- 绘制静态图像或动画 GIF 帧的 OwnerDrawLabelProvider
- 一个动画线程(节奏制定者),它为包含动画 GIF 的列调用 redraw(),并且还调用 update()
- 以及控制动画线程的查看器内容提供者。
详情见我的博客 http://www.frightanic.com/2010/02/09/animated-gif-in-swt-tabletree-viewer-cell/.
其他提示
你能不能让LabelProvider返回不同的图像,然后你想动画的元素调用viewer.update(...)。您可以使用Display.timerExec得到一个回调而不是一个单独的线程的。
这里见我的回答你可以如何改变颜色。你应该能够做到与图像类似的东西。