[JSpinner] "Ouvindo" eventos de teclado

Olá:

Estou fazendo um teste com JSpinner. Quando clico em “Seta para Cima” ou em “Seta para Baixo” o valor do JSpinner é incrementado ou decrementado em uma unidade. Meu objetivo final é “acelerar” a variação do valor: Quando clicar CTRL + “Seta para Cima” ou CTRL + “Seta para Baixo” o valor deve ser incrementado ou decrementado em 10 unidades, por exemplo. Entretanto as coisas não estão saido como esperado. Se eu Adicionar um KeyListener para o JSpinner nada acontece. Tentei ainda usar este exemplo e este outro no Example Depot (outrora Java Alamanac), usando KeyStroke mas de novo não aconteceu nada. Abaixo, segue-se o código de teste que fiz (feito no NetBeans 6.8 ):

package testeswing;

import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.Arrays;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.KeyStroke;

/**
 *
 * @author rafael
 */
public class TestaSpinner extends javax.swing.JFrame {

    public static final String CTRL_UP = "CTRL_UP";
    public static final String CTRL_DOWN = "CTRL_DOWN";

    private Action action = new AbstractAction() {

        public void actionPerformed(ActionEvent e) {
            System.out.println(e);
        }
    };

    /** Creates new form TestaSpinner */
    public TestaSpinner() {
        initComponents();
        System.out.println(this.spnValor.getInputMap());
        System.out.println(Arrays.toString(this.spnValor.getRegisteredKeyStrokes()));
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        lblValor = new javax.swing.JLabel();
        spnValor = new javax.swing.JSpinner();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Teste de JSpinner");
        getContentPane().setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 20, 5));

        lblValor.setFont(new java.awt.Font("Tahoma", 0, 48));
        lblValor.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblValor.setMaximumSize(new java.awt.Dimension(100, 58));
        lblValor.setMinimumSize(new java.awt.Dimension(100, 58));
        lblValor.setPreferredSize(new java.awt.Dimension(100, 58));
        getContentPane().add(lblValor);

        spnValor.setFont(new java.awt.Font("Tahoma", 0, 48)); // NOI18N
        spnValor.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(0), null, Integer.valueOf(50), Integer.valueOf(1)));
        spnValor.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                spnValorStateChanged(evt);
            }
        });
		// Adicionando KeyListeners
        spnValor.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                spnValorKeyPressed(evt);
            }
            public void keyReleased(java.awt.event.KeyEvent evt) {
                spnValorKeyReleased(evt);
            }
            public void keyTyped(java.awt.event.KeyEvent evt) {
                spnValorKeyTyped(evt);
            }
        });
        
		// Adicionando KeyStrokes
        getContentPane().add(spnValor);
        InputMap inputMap = new InputMap();
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.CTRL_DOWN_MASK), CTRL_UP);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, KeyEvent.CTRL_DOWN_MASK), CTRL_UP);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.CTRL_DOWN_MASK), CTRL_DOWN);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, KeyEvent.CTRL_DOWN_MASK), CTRL_DOWN);
        inputMap.setParent(this.spnValor.getInputMap(JComponent.WHEN_FOCUSED));
        spnValor.setInputMap(JComponent.WHEN_FOCUSED, inputMap);

        spnValor.getActionMap().put(CTRL_DOWN, this.action);
        spnValor.getActionMap().put(CTRL_UP, this.action);

        pack();
    }// </editor-fold>

    private void spnValorStateChanged(javax.swing.event.ChangeEvent evt) {                                      
        this.lblValor.setText(this.spnValor.getValue().toString());
    }                                     

    private void spnValorKeyPressed(java.awt.event.KeyEvent evt) {
        System.out.println("KeyPressed: " + evt);
    }

    private void spnValorKeyReleased(java.awt.event.KeyEvent evt) {
        System.out.println("KeyReleased: " + evt);
    }

    private void spnValorKeyTyped(java.awt.event.KeyEvent evt) {
        System.out.println("KeyTyped: " + evt);
    }

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new TestaSpinner().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify
    private javax.swing.JLabel lblValor;
    private javax.swing.JSpinner spnValor;
    // End of variables declaration

}

No construtor do JFrame mando imprimir os KeyStrokey registrados. O que aparece é

Bem, alguém tem idéia do que está faltando para que o JSpinner consiga ouvir “meus” eventos?

Grato,

Rafael U. C. Afonso

Que bagunça o NetBeans faz no código, eu nunca tinha visto hehehe.

Bom, vou dar uma idéia, acho que pode funcionar.

Crie 2 SpinnerModel do tipo SpinnerNumberModel, um com incremento de 1 e outro com 10, assim:

SpinnerModel spnModel1 = new SpinnerNumberModel(0,null,50,1);
SpinnerModel spnModel10 = new SpinnerNumberModel(0,null,50,10);

Quando o CTRL for pressionado (keyPressed) você seta o model para spnModel10:
spnValor.setModel(spnModel10);

Quando o CTRL for solto (keyReleased) você seta o model para spnModel1;
spnValor.setModel(spnModel1);

Não testei, só pensei na solução… espero que ajude!

De fato gostava mais da forma que o Visual Editor do Eclipse organizava o código. Principalmente ele não tem essas frescuras de regiões de edição proibida. Infelizmente ele não é tão sofisticado quanto o Matisse do NB e no momento está com o desenvolvimento estagnado.

Aí que está todo o problema: Não consigo fazer com que o Spinner “ouça” o teclado. O comportamento padrão do componente é que quando você teclar seta p/cima ou seta p/baixo, o spinner incremente ou decremente em uma unidade. Tentei fuçar no código original do JSpinner mas não consegui descobrir onde é injetado este comportamento.
Quanto a criar dois modelos diferentes, não é necessário. Criei um MouseWheelListtener que quando você usa a roda do mouse o valor do Spinner é incrementado ou decrementado. Mas se junto com a roda você mantém clicado o botão direito do mouse, você pode variar o valor em dez unidade ou mais (você pode configurar o valor desse passo estendido).

Obrigado de qualquer forma.

Entendi o seu problema agora Rafael, fiz uns testes e também não consegui fazer o keylistener funcionar…
O JSpinner é meio estranho, eu também to apanhando pra usar ele, quero desabilitar o editor dele, permitindo que o valor seja alterado apenas pelas setas. Se eu usar setEnable(false) ele desabilita as setas também, tentei desabilitar só o editor mas também não fez nada… Parece tão simples de usar :?

ei nao falem mau do meu Netbeans ein, amo ele de montao kkkkk :lol:

moleza cara, coloca isso depois de

initComponents();

do codigo do Netbeans



((javax.swing.JSpinner.DefaultEditor) TeuObjetojSpinner.getEditor()).getTextField().addKeyListener(
                new java.awt.event.KeyListener() {

                    public void keyTyped(java.awt.event.KeyEvent e) {
                        //Exemplo
                        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                            JOptionPane.showMessageDialog(null, "Pressionou ENTER");
                        }
                    }

                    public void keyPressed(java.awt.event.KeyEvent e) {
                    }

                    public void keyReleased(java.awt.event.KeyEvent e) {
                    }
                });


vlwwwwwwwwww

[quote=ambuzr]ei nao falem mau do meu Netbeans ein, amo ele de montao kkkkk :lol:

moleza cara, coloca isso depois de

initComponents();

do codigo do Netbeans



((javax.swing.JSpinner.DefaultEditor) TeuObjetojSpinner.getEditor()).getTextField().addKeyListener(
                new java.awt.event.KeyListener() {

                    public void keyTyped(java.awt.event.KeyEvent e) {
                        //Exemplo
                        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                            JOptionPane.showMessageDialog(null, "Pressionou ENTER");
                        }
                    }

                    public void keyPressed(java.awt.event.KeyEvent e) {
                    }

                    public void keyReleased(java.awt.event.KeyEvent e) {
                    }
                });


vlwwwwwwwwww[/quote]
ambuzr:

Funcionou perfeitamente. A diferença é que coloquei o código não depois do initComponents() mas sim dentro. Mais especificamente na parte em que o NB a personalização do código depois da inicialização do componente.

Grato,

Rafael U. C. Afonso

Boa dica ambuzr! Eu estava tentando pegar esse TextField! Com isso consegui resolver outro problema que eu tinha (desabilitar só a TextField do Spinner). Valeu!