Я пытаюсь отобразить анимацию загрузки поверх 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 с текущим экземпляром 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)
, я не вижу никакого наложения, возможно, потому, что оно нарисовано под столом?
Пробовали new GC(natTable);
? Потому что NatTable как Canvas
также является Drawable
Это звучит как правильное место для его размещения, однако кажется, что оверлей рисуется под фактической таблицей, таким образом скрывая содержимое оверлея.
Возможно, проблема параллелизма при асинхронном рендеринге во втором потоке. Я протестировал простую адаптацию вашего кода, и это сработало для меня. Если у вас все еще есть проблемы, вы можете попробовать PaintListener вместо IOverlayPainter.
не могли бы вы поделиться своим примером? Я мог бы проанализировать различия, если таковые имеются, и посмотреть, не совершаю ли я тонкую ошибку.
Мне удалось решить проблему, я использую предоставленный GC для отрисовки оверла в методе paintOverlay
и создаю новый GC в потоке анимации. Это еще не идеально, но работает
хм, может быть, я должен был скоро заявить, что у меня есть решение. Могу я спросить, где вы создаете свой GC (natTable)?
Давайте продолжить обсуждение в чате.
Даже если GC не был удален, это все равно приведет к ошибке «Недопустимый доступ к потоку», поскольку все операции пользовательского интерфейса (например,
drawImage
) должны выполняться в потоке пользовательского интерфейса.