Лучше всего разделить код на зоны ответственности. Пусть go с тремя: 1. рабочий (ie загрузка); 2. дисплей (ie обновление JLabel); 3. интеграция двух (первые два независимы друг от друга, поэтому вам понадобится что-то, чтобы ie их вместе).
Абстрагируясь от реальной работы, вы можете использовать стандартные интерфейсы. Первый - это просто Runnable
, ie, не принимающий никаких параметров и ничего не возвращающий. Второй - Consumer<String>
, потому что он принимает String
(для отображения), но ничего не возвращает. Третий будет вашим основным элементом управления.
Давайте начнем с элемента управления, потому что это просто:
Consumer<String> display = createDisplay();
Runnable worker = createWorker();
CompletableFuture.runAsync(worker);
Это запустит рабочий процесс в отдельном потоке, который звучит так, как вы хотите.
Итак, вот ваш загрузчик:
Consumer<String> display = // tbd, see below
Runnable worker = () -> {
String[] progress = {"start", "middle", "finish"};
for (String pr : progress) {
display.accept(pr);
Thread.sleep(1000); // insert your code here
}
}
Обратите внимание, что этот воркер действительно зависит от потребителя; это несколько "нечисто", но подойдет.
Теперь о дисплее. Определив его как Consumer<String>
, он достаточно абстрактен, чтобы мы могли просто распечатать прогресс на консоли.
Consumer<String> display = s -> System.out.printf("upload status: %s%n", s);
Однако вы хотите обновить JLabel
; так что потребитель будет выглядеть как
Consumer<String> display = s -> label.setText(s);
// for your code
s -> pleaseWaitWindow.getPleaseWaitLabel().setText(s);
Ваш фактический вопрос
Итак, если вы это сделаете, вы заметите, что текст вашего ярлыка не обновляется, когда вы ожидать. Это потому, что label.setText(s)
выполняется в потоке, в котором выполняется рабочий; его нужно вставить в резьбу Swing. Вот где появляется SwingWorker
.
В SwingWorker
есть поле progress
, которое вы можете использовать для своих меток; у него также есть doInBackground()
, который является вашим фактическим рабочим потоком загрузки. Итак, вы получаете
class UploadSwingWorker {
public void doInBackground() {
for(int i = 0; i < 3; i++) {
setProgress(i);
Thread.sleep(1000); // again, your upload code
}
}
}
Так как это обновляет ваш лейбл? setProgress
поднимает PropertyChangeEvent
, который вы можете перехватить; это делается с помощью PropertyChangeListener
с подписью
void propertyChange(PropertyChangeEvent e)
Это функциональный интерфейс, поэтому вы можете реализовать это с помощью лямбда, в вашем случае
String[] displays = {"start", "middle", "finish"};
updateLabelListener = e -> {
int index = ((Integer) e.getNewValue()).intValue(); // the progress you set
String value = displays[index];
label.setText(value);
}
, и вы можете добавить его в SwingWorker
, используя
SwingWorker uploadWorker = new UploadSwingWorker();
uploadWorker.addPropertyChangeListener(updateLabelListener);
uploadWorker.execute(); // actually start the worker
Simpler
Обратите внимание, что я никогда не использовал SwingWorker
таким образом. Гораздо более простой способ обойти проблему, заключающуюся в том, что GUI не обновляется изнутри вашего рабочего потока, - это вызвать обновление GUI, используя SwingUtilities.invokeLater()
.
Возвращение к исходному Consumer<String>
I поднял, вы можете сделать
Consumer<String> display = s -> SwingUtilities.invokeLater(
() -> pleaseWaitWindow.getPleaseWaitLabel().setText(s)
);
, и это должно сработать. Это позволяет вам держать вашего рабочего в более абстрактном Runnable
и использовать обычные механизмы планирования для его запуска (ExecutorService.submit()
или CompletableFuture.runAsync()
, например), при этом позволяя обновлять GUI на столь же простом уровне.