Рисование анимации в Nattable OverlayPainter

Я пытаюсь отобразить анимацию загрузки поверх nattable. Я использую механизм OverLayPainter, чтобы нарисовать «стеклянную панель» и некоторый текст поверх таблицы, и это работает отлично:

public class MessageOverlay
implements IOverlayPainter {
....
@Override
public void paintOverlay(final GC gc, final ILayer layer) {
  this.currentGC = gc;
  this.currentLayer = layer;
  if (visible) {
    currentGC.setAlpha(200);
    currentGC.fillRectangle(0, 0, currentLayer.getWidth(), currentLayer
      .getHeight());
     drawMessage();

    if (withLoadingAnimation) {
      showAnimation = true;
    }
   } else {
     showAnimation = false;
   }
  }
}

Однако метод paintOverlay вызывается не регулярно, а каждый раз при изменении таблицы.

Чтобы иметь возможность отображать плавную анимацию, я добавил новый поток

final Thread animatorThread = new Thread(new Runnable() {

  @Override
  public void run() {

    while (!Thread.interrupted()) {
      try {
        Thread.sleep(1000 / fps);
      } catch (final InterruptedException e) {
        break;
      }

       display.asyncExec(new Runnable() {
        @Override
        public void run() {
          if (showAnimation && !currentGC.isDisposed()) {
            final Image currentImage = getNextImage();
            final int posX = currentGC.getClipping().width / 2
                - currentImage.getBounds().width;
            final int posY = currentGC.getClipping().height / 2
                - currentImage.getBounds().height;
            currentGC.drawImage(currentImage, posX, posY);
          }
        }
      });
    }
  }
});

animatorThread.start();

Как видите, он пытается получить доступ к графическому контексту this.currentGC, установленному в методе paintOverlay. Моя проблема в том, что currentGC внутри animatorThread всегда disposed.

Как я могу а.) убедиться, что контекст не удален в потоке или б.) решить это альтернативным способом?

Спасибо за помощь.

Даже если GC не был удален, это все равно приведет к ошибке «Недопустимый доступ к потоку», поскольку все операции пользовательского интерфейса (например, drawImage) должны выполняться в потоке пользовательского интерфейса.

greg-449 15.05.2019 16:21

ааа, вы абсолютно правы, я изменил вопрос, чтобы отразить работу в потоке пользовательского интерфейса.

bwright 15.05.2019 16:24
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
2
88
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете попытаться создать новый GC с текущим экземпляром NatTable и при необходимости передать конфигурацию из переданного экземпляра GC. Затем вы отвечаете за удаление экземпляра GC и не должны рисковать удалением GC за пределами вашего потока.

Простой пример может выглядеть как следующий фрагмент, который просто показывает панель на 1000 мс, а затем снова удаляет ее. Конечно, вам нужно изменить логику, чтобы она была более динамичной в отношении вашей операции загрузки:

AtomicBoolean paneThreadStarted = new AtomicBoolean(false);

...

natTable.addOverlayPainter(new IOverlayPainter() {

    @Override
    public void paintOverlay(GC gc, ILayer layer) {
        if (this.paneThreadStarted.compareAndSet(false, true)) {
            Display.getDefault().asyncExec(new Runnable() {

                @Override
                public void run() {
                    GC currentGC = new GC(natTable);
                    currentGC.setForeground(GUIHelper.COLOR_WHITE);
                    currentGC.setBackground(GUIHelper.COLOR_BLACK);
                    currentGC.setAlpha(200);
                    currentGC.fillRectangle(0, 0, layer.getWidth(), layer.getHeight());

                    String load = "Loading data ...";
                    Point textExtent = currentGC.textExtent(load);
                    currentGC.drawText(load,
                            layer.getWidth() / 2 - textExtent.x / 2,
                            layer.getHeight() / 2 - textExtent.y / 2,
                            true);

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    currentGC.dispose();
                    natTable.redraw();
                }
            });
        }
    }
});

Таким образом, вы можете снова отобразить панель, изменив AtomicBoolean снаружи:

    Button showPaneButton = new Button(buttonPanel, SWT.PUSH);
    showPaneButton.setText("Show Pane");
    showPaneButton.addSelectionListener(new SelectionAdapter() {
        @Override
        public void widgetSelected(SelectionEvent e) {
            this.paneThreadStarted.set(false);
            natTable.redraw();
        }
    });

Кажется, это приближает меня к результату, однако слой теперь рисуется не над столом, а над окном. Мне удалось правильно установить отсечение (чтобы оверлей имел правильный размер). Я создаю новый gc с помощью new GC(table.getShell). Если я использую new GC(table.getDisplay), я не вижу никакого наложения, возможно, потому, что оно нарисовано под столом?

bwright 16.05.2019 08:47

Пробовали new GC(natTable);? Потому что NatTable как Canvas также является Drawable

Dirk Fauth 16.05.2019 10:19

Это звучит как правильное место для его размещения, однако кажется, что оверлей рисуется под фактической таблицей, таким образом скрывая содержимое оверлея.

bwright 16.05.2019 13:06

Возможно, проблема параллелизма при асинхронном рендеринге во втором потоке. Я протестировал простую адаптацию вашего кода, и это сработало для меня. Если у вас все еще есть проблемы, вы можете попробовать PaintListener вместо IOverlayPainter.

Dirk Fauth 16.05.2019 14:26

не могли бы вы поделиться своим примером? Я мог бы проанализировать различия, если таковые имеются, и посмотреть, не совершаю ли я тонкую ошибку.

bwright 16.05.2019 14:29

Мне удалось решить проблему, я использую предоставленный GC для отрисовки оверла в методе paintOverlay и создаю новый GC в потоке анимации. Это еще не идеально, но работает

bwright 16.05.2019 15:57

хм, может быть, я должен был скоро заявить, что у меня есть решение. Могу я спросить, где вы создаете свой GC (natTable)?

bwright 16.05.2019 16:27

Давайте продолжить обсуждение в чате.

bwright 17.05.2019 11:23

Другие вопросы по теме