Question:
I have a small swing
application where I would like to display a progress when certain actions are performed
As an example, I have the listener
below that is executed when a JComboBox
is changed. It takes the selected item (which in this case is a Setor
object with id and name ), and passes the id to CadastranteComboModel
, which in turn searches the database for the list of registrants of that sector and displays it in another JComboBox
of registrants:
private void comboSetorItemStateChanged(java.awt.event.ItemEvent evt) {
if (evt.getStateChange() == ItemEvent.SELECTED) {
final int setorId = ((Setor) evt.getItem()).getId();
CadastranteComboModel cadComboModel = new CadastranteComboModel(setorId);
comboUsuario.setModel(cadComboModel);
The problem is that this communication with the database (which is in HSQL) takes a while, as the application is executed from a network location in Stand-alone mode. So I created a JDialog
just to display an infinite JProgressBar
, but I don't know how to pass the execution of the line CadastranteComboModel cadComboModel = new CadastranteComboModel(setorId);
, for example, for the SwingWorker
in doInBackground()
.
Trying to work around this problem, I created this code within the aforementioned listener
:
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
// esta é a janela que fiz a parte
//com a JProgressBar infinita
ProgressDialog progress;
//esse método que ativa o progresso pelo publish()
// e executa a linha a seguir em uma Thread separada
@Override
protected Void doInBackground() throws Exception {
publish();
CadastranteComboModel cadComboModel = new CadastranteComboModel(setorId);
comboUsuario.setModel(cadComboModel);
return null;
}
@Override
protected void process(List<Void> chunks) {
// getInstance() é o frame da tela como referencia
//esse método é "decorativo"
changeStatusComponent(getInstance(), false);
//chama e exibe a JDialog com a JProgressBar
progress = new ProgressDialog(getInstance(), true);
progress.setLocationRelativeTo(getInstance());
progress.setVisible(true);
}
@Override
protected void done() {
//quando termina a execução no doInBackground
// fecho a tela de progresso
progress.dispose();
changeStatusComponent(getInstance(), true);
}
};
worker.execute();
It works perfectly for this case, but I'll have to repeat the same block of code in other 7 or 8 methods (some even return values), which have some action that depends on a query in the database, like this one that excludes an office from the table and bench:
private void btnExcluirOficioActionPerformed(java.awt.event.ActionEvent evt) {
int indiceRowModel = this.tabela.getRowSorter().convertRowIndexToModel(this.tabela.getSelectedRow());
int intOf = (int) this.tabela.getModel().getValueAt(indiceRowModel, 0);
Date date = (Date) this.tabela.getModel().getValueAt(indiceRowModel, 3);
String strAno = new SimpleDateFormat("yyyy").format(date);
String strSetor = (String) this.tabela.getModel().getValueAt(indiceRowModel, 5);
String strOficio = strSetor + " " + intOf + "-" + strAno;
int confirma = JOptionPane.showConfirmDialog(getInstance(), "Excluir o oficio " + strOficio + "?",
ListaDeOficiosUI.TITULO, JOptionPane.YES_NO_OPTION);
if (confirma == JOptionPane.YES_OPTION) {
try {
//por causa desta chamada, vou ter que inserir aquele bloco
// do swingworker
this.tableModel.removeRow(indiceRowModel);
PrintMessageUI.exibirMsg(this.getInstance(), "Oficio" + strOficio + " excluído.");
} catch (ExcecaoPadrao ex) {
PrintMessageUI.exibirError(this, ex.getMessage());
}
}
}
What I need to do is a class that inherits from swingworker
and can take these "problematic" methods as an argument, passing their execution to doInBackground
, but I don't know how I can do that.
Is there any way to pass a method as an argument to another, and its execution is not performed in the call, but inside the method that received it as an argument?
Note: the application (the jar, in this case) is in java7, I can't do it in java8 due to IT restrictions (but suggestions in java8 are welcome).
Answer:
If the methods you want to execute in doInBackground()
all had the same signature, the solution would be to declare an interface and pass it in the constructor of a class inherited from SwingWorker .
public class Worker extends SwingWorker<Void, Void>{
interface Operation{
void execute(int valor); //Altere a assinatura de acordo com a sua necessidade
}
Operation operation;
int valor;
public Worker(Operation operation, int valor){
this.operation = operation;
this.valor = valor;
}
@Override
protected Void doInBackground() throws Exception {
operation.execute(valor);
}
@Override
protected void process(List<Void> chunks) {
-----
-----
}
@Override
protected void done() {
-----
-----
}
}
Any class that implements the Operation interface can be passed in the constructor and its method executed in doInBackground()
:
Worker worker = new Worker(myOperationClass, 10);
worker.execute();
As this does not happen, a possible solution is, since what varies is only the doInBackground()
code, create an abstract class that inherits from SwingWorker and implement the process()
and done()
methods, leaving the doInBackground()
method to implement.
public abstract class Worker extends SwingWorker<Void, Void>{
//Implemente os métodos process e done
@Override
protected void process(List<Void> chunks) {
-----
-----
}
@Override
protected void done() {
-----
-----
}
}
When you need to run one of the tasks create an instance of that class and then implement the doInBackground()
method.
Worker worker = new Worker(){
@Override
protected Void doInBackground() throws Exception {
//implemente de acordo com a situação.
}
};
worker.execute();