SWT表/树查看器单元格中的动画GIF

时间:2010-01-10 14:55:34

标签: eclipse graphics swt jface

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

发布了同样的问题

2 个答案:

答案 0 :(得分:2)

经过数小时令人沮丧的反复试验后,一位同事想出了一个可行的解决方案。我在一个完全独立的LabelProvider中实现这一点的初步方法失败了。

一种无效的方法是覆盖LabelProvider#update()并从该方法中调用timerExec(100,new Runnable(){... viewer.update()...。“生命” - 这个循环难以控制,它使用了太多的CPU周期(我的MacBook上有10%)。

同事的一个想法是实现自定义TableEditor:带有图像的标签(动画GIF的一帧)但没有文本。每个TableEditor实例都会启动自己的线程,在该线程中更新标签的图像。这很有效,但每个动画图标都有一个单独的“动画”线程。此外,这是一个性能杀手,在我的MacBook上消耗了25%的CPU。

最后的方法有三个构建块

  • 一个OwnerDrawLabelProvider,用于绘制静态图像或动画GIF的帧
  • 一个动画线程(起搏器),它为包含动画GIF的列调用redraw(),它还调用update()
  • 以及控制动画线程的观众内容提供商。

我的博客http://www.frightanic.com/2010/02/09/animated-gif-in-swt-tabletree-viewer-cell/中的详细信息。

答案 1 :(得分:1)

你不能让LabelProvider返回不同的图像,然后在想要设置动画的元素上调用viewer.update(...)。您可以使用Display.timerExec来获取回调,而不是使用单独的线程。

请参阅我的回答here,了解如何更改颜色。你应该可以用图像做类似的事情。