Alternando foco entre jTextField´s

25 respostas
netbeansjava
smatt

Galera, bom dia!

Estou precisando de uma ajuda para colocar o foco nos campos de forma correta.

Tenho 4 jTextFields (que vamos chamar de jTF1, jTF2, jTF3, jTF4). Quando executo o programa, o foco está no jTF1, e os outros campos estão “travados”, onde só serão liberados respectivamente em sua ordem logo após o seu antecessor ser preenchido e o usuário apertar TAB e mudar de campo.

Preenche o jTF1, TAB, bloqueia o jTF1 e libera o JTF2, preenche-o, TAB, bloqueia o jTF2 e libera o jTF3 e assim sucessivamente.

A princípio usei os eventos FocusLost e FocusGained, mas quando preciso voltar em algum campo o programa fica todo bugado kkk

Alguém para dar uma luz nesse processo?

25 Respostas

rodriguesabner

Você pode colocar um evento de KeyReleased em cada textField, por exemplo, no jTF1:

if (evt.getKeyCode() == KeyEvent.VK_TAB) { //Quando o foco estiver no jTF1 e vc apertar tab, ele vai mudar pro jTF2
    jTF2.setEnabled(true);
    jTF2.grabFocus();
}
smatt

Eu já vi em um post aqui bem antigo onde o ViniGodoy (que na minha humilde opinião, manja muito) disse que usar grabFocus() não é aconselhável para nossas aplicações e que o melhor eh usar o requestFocusInWindow().

Segue o link da matéria:

Ta la em baixo ele falando sobre isso.

Mas na sua opinião, devo usar o grabFocus() mesmo?

PS.: Não que você também não manje muito kk

rodriguesabner

Pô que legal, eu não havia lido este post ainda. É bem interessante, pra ser sincero eu uso o grabFocus(); e nunca obtive um erro ou algo que de diferente no sistema.

Vou começar a trocar para ver se noto alguma diferença, mas já que ele mostrou bastante exemplo com fontes sobre o assunto, creio que seria mais ideal usar o requestFocusInWindow(); mesmo!

smatt

Ai eu vi a humildade lá no teto! :hugs:

Mas no caso eu so trocaria o grabFocus() > requestFocusInWindow() certo?

rodriguesabner

Aproveitando o post, resolvi dar uma procurada sobre o assunto:

Um usuário perguntou: Eu gostaria de saber a diferença entre os métodos requestFocusInWindow(); e grabFocus();. Ambos funcionam bem para pegar o foco para mim neste programa. Portanto, não consegui entender a diferença.”


"Um usuário respondeu: A resposta é simples, grabFocus(); agarra o foco, não importa se o ancestral de nível superior é a janela focada. Se a janela não estiver ativa, ela será ativada para permitir que o componente obtenha o foco.

Considerando que, requestFocusInWindow(); obtém o foco para o componente no qual ele é chamado apenas quando seu ancestral de nível superior é a janela focada. "


Então acho que nesse caso dá pra dar uma entendida mais clara sobre o assunto, ficando assim, a critério do desenvolvedor!!

fonte: https://stackoverflow.com/questions/17680817/difference-between-requestfocusinwindow-and-grabfocus-in-swing

rodriguesabner

Isso!

smatt

Outra pergunta: la no initComponents() eu dou um setEnabled(false) no jTF2, jTF3 e no jTF4, deixando somente o primeiro disponível? Porque se eu não travar os outros, todos ja iniciam disponiveis.

E logo após eu dar o jTF2.setEnabled(true), não preciso por o jTF1 como false não?

Porque testando aqui não mudou nada, daí me veio essas dúvidas. :upside_down_face:

smatt

Resumindo: o grabFocus() é folgado kkkkk ele coloca o foco e não ta nem ai. Já o requestFocusInWindow() é educado kkkk e só é chamado se a janela correspondente ao campo onde irá ter foco estiver aberta.

Talvez seja pelo fato do grabFocus() ser folgado que o ViniGodoy disse que não é bom usarmos :thinking:

rodriguesabner

Cara, você pode fazer isso setando todos os componentes como setEnabled(false); direto neles mesmos, ou fazer como você disse, abaixo do initComponents():.

Mas você precisa bloquear o textField1 mesmo? Caso precise, tem que colocar ele como false mesmo

smatt

Ahh certo

No caso eu quero de fato é alternar o foco. Não está usando, deixa ele false e true o próximo, e assim vai.

Talvez você nunca fez um programa em que usou de fato esse aspecto folgado dele kkk

rodriguesabner

Entendi! Então teria que ser assim, só que não tem como editar assim. Só se utilizar um botão pra corrigir o campo e colocá-lo como true de novo, segue:

if (evt.getKeyCode() == KeyEvent.VK_TAB) {
    jTF1.setEnabled(false);
    jTF2.setEnabled(true);
    jTF2.grabFocus();
}

Sim! Geralmente eu uso apenas em JDialog, então como o foco é só dele, eu nunca percebi a diferença.

smatt

Você quer dizer no caso de o usuário errar na digitação e passar para o próximo campo, se ele der um SHIFT + TAB o campo não voltará com o foco, certo?

rodriguesabner

Pode ser também, não pensei nessa hipótese. Mas voltaria com o foco sim, com o foco e true

smatt

Eu fiz isso aqui nos meus 4 campos e não libera o próximo jTF. Vou te mostrar o código aqui:

private void jTFContaKeyReleased(java.awt.event.KeyEvent evt) {       // Aqui é o jTF1                              
    if (evt.getKeyCode() == KeyEvent.VK_TAB) { 
        jTFNome.setEnabled(true);
        jTFConta.setEnabled(false);
        jTFNome.requestFocusInWindow();
    }
}                                    

private void jTFNomeKeyReleased(java.awt.event.KeyEvent evt) {             // Aqui é o jTF2                        
    if (evt.getKeyCode() == KeyEvent.VK_TAB) { 
        jTFSaldo.setEnabled(true);
        jTFNome.setEnabled(false);
        jTFSaldo.requestFocusInWindow();
    }
}                                   

private void jTFSaldoKeyReleased(java.awt.event.KeyEvent evt) {                 // Aqui é o jTF3                     
    if (evt.getKeyCode() == KeyEvent.VK_TAB) { 
        jTFLimite.setEnabled(true);
        jTFSaldo.setEnabled(false);
        jTFLimite.requestFocusInWindow();
    }
}                                    

private void jTFLimiteKeyReleased(java.awt.event.KeyEvent evt) {                // Aqui é o jTF4                       
    if (evt.getKeyCode() == KeyEvent.VK_TAB) { 
        jTFSaldo.setEnabled(false);  // Aqui não tem mais campos para setar como "true", então eu apenas seto "false" o jTF3.
        jTFNome.requestFocusInWindow();
    }
}
rodriguesabner

O foco está no jTFConta pra vc começar a trocar os campos?

smatt

Exatamente! Nele, eu não deixei o enabled como false. Executando o programa ele começa como true, e os outros 3 false, dai vai passando.

rodriguesabner

Tenta com o grabFocus():

smatt

Não mudou nada. Continua false. (Tirei aquela parte que coloco false o anterior. Se assim der certo ai eu volto com essa parte de deixar false o que já foi digitado.)

rodriguesabner

To instalando o netbeans aqui no trampo kkkkkk, já testo aqui!

smatt

Nossa mano que humildade! :hugs: GUJ is life

smatt

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.

staroski

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;
    }
}
rodriguesabner

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);
    }
}
ViniGodoy

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).

smatt

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!

Criado 14 de maio de 2019
Ultima resposta 15 de mai. de 2019
Respostas 25
Participantes 4