Alternando foco entre jTextField´s

25 respostas
netbeansjava
S

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

R

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();
}
S

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

R

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!

S

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

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

R

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

R

Isso!

S

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:

S

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:

R

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

S

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

R

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.

S

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?

R

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

S

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();
    }
}
R

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

S

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.

R

Tenta com o grabFocus():

S

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

R

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

S

Nossa mano que humildade! :hugs: GUJ is life

S

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.

S

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

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

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

S

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