Alternando foco entre jTextField´s

Nossa mano que humildade! :hugs: GUJ is life

Achei o problema!!

if (evt.getKeyCode() == KeyEvent.VK_TAB) {

Ele não reconhece o que é “VK_TAB”, mesmo estando na lista de parâmetros. Testei mudando e colocando o VK_DOWN (seta para baixo) e deu certo! Na verdade so não ta dando certo com o TAB kk com o TAB ele pula direto pro botão que o usuário aperta depois de preencher todos os campos.

Mas no caso eu preciso do TAB porque é o padrão de mudança de campos.

Nossa, aí para cada campo novo vai ter que repetir esses códigos?

Eu criaria uma classe reaproveitável FocusHandler para tratar essas características de foco e teclas dessa forma:

    FocusHandler focusHandler = new FocusHandler();
    focusHandler.add(jTextField1);
    focusHandler.add(jTextField2);
    focusHandler.add(jTextField3);
    focusHandler.add(jTextField4);
    focusHandler.add(jTextField5);

Classe FocusHandler:

import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.text.JTextComponent;

public class FocusHandler {

    private final FocusListener focusListener = new FocusAdapter() {

        @Override
        public void focusGained(FocusEvent fe) {
            onFocusGained((JTextComponent) fe.getSource());
        }
    };

    private final KeyListener keyListener = new KeyAdapter() {
        @Override
        public void keyTyped(KeyEvent ke) {
            if (ke.getKeyCode() == KeyEvent.VK_TAB) {
                onTabTyped((JTextComponent) ke.getSource(), ke.isShiftDown());
            }
        }
    };

    private final List<JTextComponent> components = new ArrayList<>();

    public void add(JTextComponent component) {
        component.addFocusListener(focusListener);
        component.addKeyListener(keyListener);
        components.add(component);
        onFocusGained(component);
    }

    public void remove(JTextComponent component) {
        component.removeFocusListener(focusListener);
        component.removeKeyListener(keyListener);
        components.remove(component);
    }

    private void onFocusGained(JTextComponent source) {
        for (JTextComponent component : components) {
            component.setEditable(component == source);
        }
        source.requestFocus();
    }

    private void onTabTyped(JTextComponent source, boolean shiftPressed) {
        int offset = shiftPressed ? components.size() - 1 : 0;
        int increment = shiftPressed ? -1 : +1;
        boolean editable = false;
        for (int i = 0; i < components.size(); i++) {
            JTextComponent component = components.get(offset);
            component.setEditable(editable);
            if (editable) {
                component.requestFocus();
            }
            editable = component == source;
            offset += increment;
        }
    }
}

Tela de exemplo (TAB pula para o próximo e SHIFT + TAB para o anterior):

import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;

import javax.swing.JFrame;
import javax.swing.JTextField;

public class Exemplo extends JFrame {

    public static void main(String[] args) {
        try {
            Exemplo programa = new Exemplo();
            programa.setDefaultCloseOperation(EXIT_ON_CLOSE);
            programa.setVisible(true);
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    private JTextField jTextField1;
    private JTextField jTextField2;
    private JTextField jTextField3;
    private JTextField jTextField4;
    private JTextField jTextField5;

    public Exemplo() {
        super("Exemplo");
        setSize(640, 480);

        jTextField1 = createTextField();
        jTextField2 = createTextField();
        jTextField3 = createTextField();
        jTextField4 = createTextField();
        jTextField5 = createTextField();

        Container container = getContentPane();
        container.setLayout(new FlowLayout(FlowLayout.CENTER));
        container.add(jTextField1);
        container.add(jTextField2);
        container.add(jTextField3);
        container.add(jTextField4);
        container.add(jTextField5);

        // usar um gerenciador para o foco dos componentes
        FocusHandler focusHandler = new FocusHandler();
        focusHandler.add(jTextField1);
        focusHandler.add(jTextField2);
        focusHandler.add(jTextField3);
        focusHandler.add(jTextField4);
        focusHandler.add(jTextField5);
    }

    private JTextField createTextField() {
        JTextField textField = new JTextField();
        textField.setPreferredSize(new Dimension(100, 36));
        return textField;
    }
}
2 curtidas

O cara é um gênio mesmo!

Só pra postar mesmo, eu acabei conseguindo de outra forma, a pessoa tem que escrever primeiro pra poder detectar:

    private void tf1KeyReleased(java.awt.event.KeyEvent evt) { 
        tf1.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, Collections.EMPTY_SET);
        if (evt.getKeyCode() == KeyEvent.VK_TAB) {
            tf2.setEnabled(true);
            tf1.setEnabled(false);
        }
    }

Só indo na onda do Staroski, se quiser um método reutilizável:

private void tf1KeyReleased(KeyEvent evt) { //evento de key released                                
    proxCampo(evt, tf1, tf2); //tf1 e tf2 são dois textfields
}                               

public void proxCampo(KeyEvent evt, JTextField txt1, JTextField txt2) {
    txt1.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, Collections.EMPTY_SET);
    if (evt.getKeyCode() == KeyEvent.VK_TAB) {
        txt2.setEnabled(true);
        txt1.setEnabled(false);
    }
}

Raramente você precisa programar algum código para controlar o foco dos seus componentes. O requestFocusInWindow() ou o grabFocus() vão ser usados somente em casos muitíssimo específicos, como quando vc quiser que ao clicar num botão, um componente qualquer receba o foco.

Para a troca de foco em si, não use esses métodos. O ideal é estudar um pouco como funcionam a TransversalFocusPolicy e o sistema de foco do java. Isso fará com que os saltos saiam corretos, que o CTRL+TAB também retorne e que você não perca tempo programando eventos. E o melhor, fica tudo multiplataforma e praticamente não envolve codificação nenhuma.

Siga esses tutoriais: https://docs.oracle.com/javase/tutorial/uiswing/misc/focus.html

Trocar foco em evento (principalmente se for um evento relacionado também a foco) é uma fórmula certa para o fracasso. Os eventos de foco são ligeiramente diferentes entre as várias plataformas. Não é à toa que o Java fornece dezenas de classes para evita-los (como os InputVerifiers, TransversalPolicy, etc).

1 curtida

Na minha humilde opinião, @staroski e @ViniGodoy são uns dos mestres de Java que tem no GUJ atualmente! Entendem tanto que chega a fazer raiva pq tudo parece simples para eles :rofl::rofl:

Obrigado pela ajuda de todos!