ActionListener x KeyListener

Tenho um JFrame principal (tela inicial do aplicativo) que faz chamada às outras classes. Uma outra classe Calculadora (que é chamada da tela inicial) é tb um JFrame e é composta de botões que compõem o teclado da calculadora e têm suas funcionalidades escritas numa subclasse que implementa a ActionListener.

O problema é que agora inclui uma outra subclasse que implementa a KeyListener mas nada funciona via teclado.

Tentei incluir inicialmente apenas a função do KeyEvent dentro da subclasse que implementa a ActionListener e não funcionou pois precisei implementar o KeyListener no JFrame, só que fazendo isso tenho erro de compilação pois a classe Calculadora não é abstrata. Seu eu colocar ela como abstrata sou impedido de chamar ela pela tela principal pois a tela principal passa parâmetros para essa classe.

Alguém pode me dizer como devo estruturar o código para ter a funcionalidade dos botões e do teclado (de preferência sem precisar reescrever as funções já existentes na subclasse que implementa a ActionListener)?

Seguem trechos do código como está agora, ou seja, uma classe separada estendendo da classe KeyAdapter:

public class CalcConvFrame extends JFrame {
	...
	...
Treatment09 treat36 = new Treatment09();
	this.addKeyListener(treat36);
	...
class Treatment09 extends KeyAdapter {

	public void keyPressed(KeyEvent ccfk) {
			
	// ----------------------------------------------------------------
	// Eventos do Teclado
			
	// Saída do CalcConvFrame
	if(ccfk.getKeyCode() == ccfk.VK_ESCAPE) {
	Object Butns[] = {" Sim ", " Não "};
	int closing = JOptionPane.showOptionDialog(null, "Deseja mesmo encerrar o aplicativo?", "Sair", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, Butns, Butns[1]);
	if(closing == JOptionPane.YES_OPTION) CalcConvFrame.this.dispose();				
	}
			
	if((ccfk.getKeyCode() >= ccfk.VK_NUMPAD0) && (ccfk.getKeyCode() <= ccfk.VK_NUMPAD9)) knumbr(""+ccfk.getKeyChar());

	if(ccfk.getKeyCode() == 110)	kcomma();		
	if(ccfk.getKeyCode() == 8)	kbkspc();
	if(ccfk.getKeyCode() == 107)	knplus();
	if(ccfk.getKeyCode() == 109)	knless();
	if(ccfk.getKeyCode() == 111)	kdivis();
	if(ccfk.getKeyCode() == 106)	ktimes();
	if(ccfk.getKeyCode() == 80)	kprcnt();
	if(ccfk.getKeyCode() == 10)	kenter();
			
	}
		
	// ----------------------------------------------------------
	// Entrada de Números pelo Teclado
		
	public void knumbr(String s) {

	if(vldisplay == "0,00") vldisplay = "";
	vldisplay = vldisplay + s;
	display.setText(formatStrNum(vldisplay, vldecimals));

	}

...

Cara, eu costumo fazer da seguinte forma: crio uma classe interna (não-anônima) chamada ControladorEventos e essa classe implementa todos os listeners que preciso no frame, dentro dessa classe eu faço o tratamento dos eventos, e os botões e demais componentes adicionar essa classe como ouvinte. Mais ou menos assim:

ControladorEventos controladorEventos = new ControladorEventos();
txtNome.addActionListener(controladorEventos);

[code]private class ControladorEventos implements ActionListener, ChangeListener, MouseListener, KeyListener {

    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == txtNome) {
                JOptionPane.showMessageDialog(null, Mensagem",
                        "Editar Hóspede", JOptionPane.PLAIN_MESSAGE);
        }

    public void mouseClicked(MouseEvent e) {
        if(e.getSource() == btnAdicionar) {
                JOptionPane.showMessageDialog(null, Mensagem",
                        "Editar Hóspede", JOptionPane.PLAIN_MESSAGE);
        }
    }
    
    public void keyTyped(KeyEvent e) {
        if(e.getKeyChar() == KeyEvent.VK_ENTER) {
            if(e.getSource() == btnEditar) {
                JOptionPane.showMessageDialog(null, Mensagem",
                        "Editar Hóspede", JOptionPane.PLAIN_MESSAGE);
            }
        }
    }

}[/code]

Prezado FredMP

Muito obrigado pela resposta!

Vou tentar aplicar desta forma.

Um Abraço,

ffranco

Prezado FredMP,

Tentei desta forma mas ainda não consigo usar o teclado.

Seguem os trechos de código:

1º) Exemplo de um dos botões (os demais estão da mesma forma);

			btn083 = new JButton("Entrar");
			btn083.setFont(new Font("Georgia", Font.BOLD, 18));
			Treatment08 treat14 = new Treatment08();
			btn083.addActionListener(treat14);

2º) Classe dos Listeners inlcuindo uma das funções (entrada dos números);

	class Treatment08 implements ActionListener, KeyListener {
		
		public void actionPerformed(ActionEvent ccfb) {

			// -----------------------------------------------------------------
			// Eventos via Botões

			// Saída do CalcConvFrame
			if(ccfb.getSource() == btn095) {
				Object Butns[] = {" Sim ", " Não "};
				int closing = JOptionPane.showOptionDialog(null, "Deseja mesmo encerrar o Power Tools?", "Sair", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, Butns, Butns[1]);
				if(closing == JOptionPane.YES_OPTION) CalcConvFrame.this.dispose();				
			}

				 if(ccfb.getSource() == btn070) numbr("7");
			else if(ccfb.getSource() == btn071)	numbr("8");
			else if(ccfb.getSource() == btn072)	numbr("9");
			else if(ccfb.getSource() == btn073)	numbr("4");
			else if(ccfb.getSource() == btn074)	numbr("5");
			else if(ccfb.getSource() == btn075)	numbr("6");
			else if(ccfb.getSource() == btn076)	numbr("1");
			else if(ccfb.getSource() == btn077)	numbr("2");
			else if(ccfb.getSource() == btn078)	numbr("3");
			else if(ccfb.getSource() == btn079)	numbr("0");
			else if(ccfb.getSource() == btn080)	numbr("00");
			else if(ccfb.getSource() == btn081)	numbr("000");
			else if(ccfb.getSource() == btn082)	comma();
			else if(ccfb.getSource() == btn083)	enter();
			else if(ccfb.getSource() == btn084)	cldpy();
			else if(ccfb.getSource() == btn085)	clall();
			else if(ccfb.getSource() == btn086)	store();
			else if(ccfb.getSource() == btn087)	memry();
			else if(ccfb.getSource() == btn088)	prcnt();
			else if(ccfb.getSource() == btn089)	divis();
			else if(ccfb.getSource() == btn090)	times();
			else if(ccfb.getSource() == btn091)	nless();
			else if(ccfb.getSource() == btn092)	nplus();
			else if(ccfb.getSource() == btn093)	chsgn();
			else if(ccfb.getSource() == btn094)	bkspc();
			else if(ccfb.getSource() == btn096)	nsqrt();
			else if(ccfb.getSource() == btn097)	ncbrt();
			else if(ccfb.getSource() == btn098)	onedv();
			else if(ccfb.getSource() == btn099)	xpowy();
			else if(ccfb.getSource() == btn100)	dcpls();
			else if(ccfb.getSource() == btn101)	dclss();
			else if(ccfb.getSource() == btn102)	lognt();
			else if(ccfb.getSource() == btn103)	numpi();

		}
		
		public void keyPressed(KeyEvent ccfk) {	}
		
		public void keyReleased(KeyEvent ccfk) { }
		
		public void keyTyped(KeyEvent ccfk) {
			
			// -----------------------------------------------------------------
			// Eventos via Teclado
			
			// Saída do CalcConvFrame
			if(ccfk.getKeyCode() == ccfk.VK_ESCAPE) {
				Object Butns[] = {" Sim ", " Não "};
				int closing = JOptionPane.showOptionDialog(null, "Deseja mesmo encerrar o Power Tools?", "Sair", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, Butns, Butns[1]);
				if(closing == JOptionPane.YES_OPTION) CalcConvFrame.this.dispose();				
			}
			
			if((ccfk.getKeyCode() >= ccfk.VK_NUMPAD0) && (ccfk.getKeyCode() <= ccfk.VK_NUMPAD9)) numbr(""+ccfk.getKeyChar());

			if(ccfk.getKeyCode() == 110)	comma();
			
			if(ccfk.getKeyCode() == 8)		bkspc();
			
			if(ccfk.getKeyCode() == 107)	nplus();
			
			if(ccfk.getKeyCode() == 109)	nless();
			
			if(ccfk.getKeyCode() == 111)	divis();
			
			if(ccfk.getKeyCode() == 106)	times();
			
			if(ccfk.getKeyCode() == 80)		prcnt();
			
			if(ccfk.getKeyCode() == 10)		enter();
			
		}		
		
		// ---------------------------------------------------------------------
		// Entrada de Números
		
		public void numbr(String s) {

			if(vldisplay == "0,00") vldisplay = "";
			vldisplay = vldisplay + s;
			display.setText(formatStrNum(vldisplay, vldecimals));

		}

Obrigado!

ffranco

Ah… me esqueci… tb inlcuí o trecho abaixo após os botões:

			Treatment08 treat36 = new Treatment08();
			this.addKeyListener(treat36);

Valeu!!!

ffranco

Por favor, alguém pode me ajudar com isso?

Obrigado,

ffranco

Dá uma olhada, é um projeto do NetBeans versão 5.0

Prezado davidbuzzato,

Muito obrigado pela resposta, porém desta forma os Actions e os Keys ficam interdependentes. Além do mais, desta forma vou precisar reescrever toda a classe.

Meu objetivo é fazer os eventos trabalharem paralelamente, ou seja, os Actions tratam unicamente dos eventos dos botôes e os Keys unicamente dos eventos do teclado.

Vc sabe o que está impedindo o funcionamento no código acima?

Obrigado,

ffranco

Olá,

Alguém poderia me dar uma força com isso?

Obrigado,

ffranco

Pessoal,

Alguém pode tentar me ajudar com isso?

Obrigado,

ffranco

Opa! Só vi sua msg agora. Vou testar seu código e te passo um feedback assim que puder. Por hora, tente usar outros métodos da interface KeyListener (como o keyPressed) ao invés de usar o keyTyped.

att,
Fred

Valeu Fred!!!

Aguardo seu retorno.

Muito Obrigado,

ffranco

Prezados Fred e demais amigos do GUJ,

Ainda estou sem solução para este problema.

Se alguém puder me ajudar vou ficar muito grato.

Um Abraço,

ffranco

Já tentou, ao invés de usar um KeyListener, usar um ActionMap e um InputMap?

Talvez o problema que você esteja tendo é que o componente só recebe eventos de KeyStroke se tiver o foco.

Para associar ações a um KeyStroke, você primeiro precisa criar objetos da classe KeyStroke. A classe KeyStroke é uma classe de conveniência que encapsula a descrição de uma tecla. Para pegar um KeyStroke, ao invés do construtor, use um método estático getKeyStroke. Por exemplo, para o ctrl+b:

KeyStroke ctrlBKey = KeyStroke.getKeyStroke(KeyEvent.VK_B, InputEvent.CTRL_MASK);

Ou, alternativamente:

KeyStroke ctrlBKey = KeyStroke.getKeyStroke("ctrl b");

Cada JComponent tem três input maps, cada um mapeando objetos associados para ações. Os três InputMaps representam cada um três condições diferentes, que são processadas nessa ordem.
1 - WHEN_FOCUSED: Quando o componente tem o foco, é a primeira a ser processada;
2 - WHEN_ANCESTOR_OF_FOCUSED_COMPONENT: Quando esse componente contém o componente que tem o foco;
3 - WHEN_IN_FOCUSED_WINDOW: Quando esse componente está na mesma janela que tem o foco.

Você obtém o InputMap de um componente chamando getInputMap, por exemplo:
InputMap imap = panel.getInputMap(JComponent.WHEN_FOCUSED);

Esse mapa será consultado apenas quando o componente tiver o foco do teclado. No caso da calculadora, você deveria usar, no painel que contém os botões, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT.

O problema é que o InputMap não mapeia objetos diretamente para o um Action. Para isso, você tem que usar uma segunda classe, a ActionMap. O código fica meio esquisitinho mas basicamente é assim:

[code]
InputMap imap = panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
imap.put(KeyStroke.getKeyStroke(“1”), “panel.button1press”);

//Mapeamos o botão 1 a ação descrita pela string.
ActionMap amap = panel.getActionMap();

//Mapeia a string para a ação a ser executada.
amap.put(“panel.button1press”, pressedAction); [/code]

Como faço para associar uma ação à teclas que não sejam numéricas? qual string devo passar em KeyStroke.getKeyStroke(str) para associar a tecla * do teclado a uma ação?

Vini boa tarde estou começando agora pra acaso vc teria algum tutorial de actionmap e inputMap e keyListerner para eu estar estudando.