[Resolvido]Fazer JDialog esperar por uma ação

Olá pessoal, tenho essa seguinte classe que funciona perfeitamente quando não acompanhada de outros componentes swing.

[code]public class MyJOptionPane extends JDialog {

private ActionSim actionSim = null;
private ActionNao actionNao = null;
private static boolean resposta;

private MyJOptionPane() {
String keySim = “actionKeySim”;
String keyNao = “actionKeyNao”;

  actionSim = new ActionSim();  
  actionNao = new ActionNao();
  
  KeyStroke strokeSim = KeyStroke.getKeyStroke("S");  
  KeyStroke strokeNao = KeyStroke.getKeyStroke("N");  
  
  InputMap inputMap = this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);   
  inputMap.put(strokeSim, keySim);   
  inputMap.put(strokeNao, keyNao);
  
  ActionMap actionMap = this.getRootPane().getActionMap();   
  actionMap.put(keySim, actionSim);
  actionMap.put(keyNao, actionNao);
  
  JButton botoes[] = {new JButton(actionSim), new JButton(actionNao)};
  botoes[0].setText("Sim-(S)");
  botoes[1].setText("Não-(N)");
  add(new JOptionPane("Mensagem", JOptionPane.YES_NO_OPTION, JOptionPane.YES_NO_OPTION, null, botoes, null));

  setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
  pack();
  setResizable(false);
  setVisible(true);

}

class ActionSim extends AbstractAction {
public void actionPerformed(ActionEvent e) {
resposta = true;
dispose();
}
}

class ActionNao extends AbstractAction {
public void actionPerformed(ActionEvent e) {
resposta = false;
dispose();
}
}

public static boolean valor(){
MyJOptionPane myJOptionPane = new MyJOptionPane();
while(myJOptionPane.isVisible()){}
return resposta;
}
} [/code]

Acontece que quando eu uso essa classe junto com um jframe, por exemplo, toda a interface gráfica trava sendo necessário reiniciar a jvm.
Descobrir que isso acontece por conta do último while só que ao retirar-lo o método retorna false instantaneamente.
Então oq fazer nesse caso? Como posso esperar por uma ação seja feita antes de enviar a resposta sem que minha interface trave?

Dispare esse while numa thread separada.

O Swing funciona assim:

while (true) { pintaATela(); processaEventos(); }

Como seu código está num evento, ele travou aquele segundo método ali. Portanto, o swing não repintará a tela.

Passando seu código para uma nova thread, o while do swing fica livre para realizar pintura enquanto seu cálculo está sendo realizado.

Pois bem eu implementei Runnable e coloque o while(isvisible) dentro no método run() e chamei o meu thead start dentro do método valor, mas mesmo assim ele retorna false antes de ler qual botão/tecla pressionada. :confused:

Agora entendi o que você quer fazer…

  1. Defina seu JDialog como modal. É so no construtor fazer:
  1. Retire o setVisible(true) do construtor do seu JDialog. É uma péssima prática, pois vc está deixando uma janela não completamente construída visível. O método setVisible irá travar quando seu JDialog for modal, até que a janela feche.

  2. Retire o static da sua variável resposta e do método valor();

  3. Apague o while (ele não é necessário);

  4. Você usa a JDialog assim:

MyJOptionPane dlg = new MyJOptionPane(); dlg.setVisible(true); //Vai travar até a janela fechar if (dlg.valor()) { System.out.println("Usuário pressionou sim"); else System.out.println("Usuário pressionou não");

Muito obrigado, funcionou perfeitamente.
Não conhecia esse metodo setModal().
De qualquer formar muito obrigado, tópico resolvido.

Tem vários problemas no seu código. Você cria toda uma classe de JDialog, mas no final acaba inserindo nele um JOptionPane com a janela.
Isso não faz o menor sentido.

O ideal é você realmente criar toda a dialog.

Veja um exemplo:

[code]import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;

public class MyJOptionPane extends JDialog {
private ActionClose actionSim = new ActionClose(“Sim - S”, true);
private ActionClose actionNao = new ActionClose(“Não - N”, false);

private boolean resposta = false;

private MyJOptionPane() {
String keySim = “actionKeySim”;
String keyNao = “actionKeyNao”;

  InputMap inputMap = this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);   
  inputMap.put(KeyStroke.getKeyStroke("S"), keySim);   
  inputMap.put(KeyStroke.getKeyStroke("N"), keyNao);
  
  ActionMap actionMap = this.getRootPane().getActionMap();   
  actionMap.put(keySim, actionSim);
  actionMap.put(keyNao, actionNao);
  JLabel msg = new JLabel("Mensagem");
  msg.setHorizontalAlignment(SwingConstants.CENTER);
  add(msg, BorderLayout.CENTER);
  
  JPanel pnlButtons = new JPanel(new FlowLayout());
  pnlButtons.add(new JButton(actionSim));
  pnlButtons.add(new JButton(actionNao));
  add(pnlButtons, BorderLayout.SOUTH);
  
  setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
  pack();
  setModal(true);
  setResizable(false);      

}

/**
* Lembre-se que a action também é um objeto. Você pode receber parâmetros e guarda-los em atributos.
* O lugar certo de colocar o texto de botões, hints, teclas de aceleração é também na Action. Assim isso é
* reaproveitado caso vc defina a action em vários locais.
*/
private class ActionClose extends AbstractAction {
private boolean valor;
public ActionClose(String texto, boolean valor) {
this.valor = valor;
putValue(Action.NAME, texto);
}
@Override
public void actionPerformed(ActionEvent e) {
resposta = valor;
dispose();
}
}

/**
* Esse método, além de dar um uso mais natural ao JDialog, garante que
* a resposta sempre será false antes do setVisible ser chamado. Assim fica
* fácil de usar o JDialog 2 vezes.
*
* @return True se sim foi clicado.
*/
public boolean showConfirm() {
resposta = false;
setVisible(true);
return resposta;
}

public static void main(String[] args) {
MyJOptionPane dialog = new MyJOptionPane();
if (dialog.showConfirm())
System.out.println(“Clicou no sim!”);
else
System.out.println(“Não clicou no sim!”);
}
} [/code]

Se você irá usar o JOptionPane dessa forma, aí não teria porque criar outra janela inteira.

Bem o que eu realmente queria era um JOptionPane que os botões respondesem pelas teclas “S” e “N” sem usar o alt.
A solução que eu encontrei foi usar o InputMap e ActionMap, já que não consegui fazer eles fucionarem no JOptionPane fiz no JDialog.
Para ficar mais parecido visualmente com o JOptionPane nada melhor que passar o próprio JOptionPane. Entende?
De qualquer modo foram exelente as digas para melhor organizar a classe, especialmente as do Action, grato.

Sim, mas você nem sempre terá esse recurso, você pode querer fazer um dialog bem mais complexo, como deixar o usuário escolher um valor numa JTable, por exemplo. Como não tinha como saber se você estava sabendo ou não o que estava fazendo, achei mais prudente colocar o código da interface gráfica completa.

Claro, sem dúvida esclareceu bastante.