Existe uma maneira fácil de alterar o comportamento de um controle Java/Swing quando ele recebe foco?

StackOverflow https://stackoverflow.com/questions/66455

  •  09-06-2019
  •  | 
  •  

Pergunta

Para a maioria das GUIs que usei, quando um controle que contém texto recebe o foco, todo o conteúdo do controle é selecionado.Isso significa que se você começar a digitar, substituirá completamente o conteúdo anterior.

Exemplo:Você tem controle de rotação inicializado com o valor zero.Você acessa ele e digita "1". O valor no controle agora é 1.

Com o Swing isso não acontece.O texto no controle não é selecionado e o quilate aparece em uma extremidade ou outra do texto existente.Continuando o exemplo acima:

Com um Swing JSpinner, quando você clica no controle de rotação, o quilate fica à esquerda.Você digita “1” e o valor no controle agora é 10.

Isso me deixa (e meus usuários) enlouquecidos e gostaria de mudar isso.Ainda mais importante, gostaria de alterá-lo globalmente para que o novo comportamento se aplique a JTextField, JPasswordField, JFormattedTextField, JTextArea, JComboBox, JSpinner e assim por diante.A única maneira que encontrei de fazer isso é adicionar um FocusAdapter a cada controle e substituir o método focusGained() por Do The Right Thing[tm].

Deve haver uma maneira mais fácil e menos frágil.Por favor?

EDITAR:Uma informação adicional para este caso específico.O formulário com o qual estou trabalhando foi gerado usando o designer de formulários do Idea.Isso significa que normalmente não escrevo o código para criar os componentes.É possível dizer ao Idea que você mesmo deseja criá-los, mas isso é um incômodo que gostaria de evitar.

Lema:Todos os bons programadores são basicamente preguiçosos.

Foi útil?

Solução 4

Depois de ler as respostas até agora (Obrigado!) Passei o JPanel mais externo para o seguinte método:

void addTextFocusSelect(JComponent component){
    if(component instanceof JTextComponent){
        component.addFocusListener(new FocusAdapter() {
                @Override
                public void focusGained(FocusEvent event) {
                    super.focusGained(event);
                    JTextComponent component = (JTextComponent)event.getComponent();
                    // a trick I found on JavaRanch.com
                    // Without this, some components don't honor selectAll
                    component.setText(component.getText());
                    component.selectAll();
                }
            });

    }
    else
    {
        for(Component child: component.getComponents()){
            if(child instanceof JComponent){
                addTextFocusSelect((JComponent) child);
            }
        }
    }
}

Funciona!

Outras dicas

Quando precisei disso no passado, criei subclasses dos componentes aos quais queria adicionar também a funcionalidade de "limpeza automática".por exemplo:

public class AutoClearingTextField extends JTextField {
   final FocusListener AUTO_CLEARING_LISTENER = new FocusListener(){
      @Override
      public void focusLost(FocusEvent e) {
         //onFocusLost(e);
      }

      @Override
      public void focusGained(FocusEvent e) {
         selectAll();
      }
   };

   public AutoClearingTextField(String string) {
      super(string);
      addListener();
   }

   private void addListener() {
      addFocusListener(AUTO_CLEARING_LISTENER);      
   }
}

O maior problema é que não encontrei uma maneira "boa" de obter todos os construtores padrão sem escrever substituições.Adicioná-los e forçar uma chamada para addListener é a abordagem mais geral que encontrei.

Outra opção é observar ContainerEvents em um contêiner de nível superior com um ContainerListeer para detectar a presença de novos widgets e adicionar um ouvinte de foco correspondente com base nos widgets que foram adicionados.(por exemplo:se o evento de contêiner for causado pela adição de um TextField, adicione um ouvinte de foco que saiba como selecionar todo o texto em um TextField e assim por diante.) Se um Container for adicionado, será necessário adicionar recursivamente o ContainerListener a esse novo subcontêiner também.

De qualquer forma, você não precisará se preocupar com ouvintes de foco em seu código de UI real - tudo será resolvido em um nível superior.

Eu não tentei isso sozinho (só me envolvi nisso há um tempo), mas você provavelmente pode obter o componente atual em foco usando:KeyboardfocusManager (existe um método estático getCurrentKeyboardfocusManager ()) e adicionando um PropertyChangelistener a ele.A partir daí, você pode descobrir se o componente é um JTextComponent e selecionar todo o texto.

Uma classe separada que anexa um FocusListener ao campo de texto desejado pode ser escrita.Tudo o que o ouvinte de foco faria seria chamar selectAll() no widget de texto quando ele ganhasse o foco.

public class SelectAllListener implements FocusListener {
  private static INSTANCE = new SelectAllListener();

  public void focusLost(FocusEvent e) { }

  public void focusGained(FocusEvent e) {
    if (e.getSource() instanceof JTextComponent) {  
      ((JTextComponent)e.getSource()).selectAll();
    }
  };

  public static void addSelectAllListener(JTextComponent tc) {
    tc.addFocusListener(INSTANCE);
  }

  public static void removeSelectAllListener(JTextComponent tc) {
    tc.removeFocusListener(INSTANCE);
  }
}

Ao aceitar um JTextComponent como argumento, esse comportamento pode ser adicionado diretamente a JTextArea, JPasswordField e todos os outros componentes de edição de texto.Isso também permite que a classe adicione selecionar tudo a caixas de combinação editáveis ​​e JSpinners, onde seu controle sobre o componente do editor de texto pode ser mais limitado.Métodos de conveniência podem ser adicionados:

public static void addSelectAllListener(JSpinner spin) {
  if (spin.getEditor() instanceof JTextComponent) {
    addSelectAllListener((JTextComponent)spin.getEditor());
  }
}

public static void addSelectAllListener(JComboBox combo) {
  JComponent editor = combo.getEditor().getEditorComponent();
  if (editor instanceof JTextComponent) {
    addSelectAllListener((JTextComponent)editor);
  }
}

Além disso, os métodos remove listener provavelmente são desnecessários, já que o listener não contém referências externas a quaisquer outras instâncias, mas podem ser adicionados para tornar as revisões de código mais suaves.

A única maneira que conheço é criar um FocusListener e anexá-lo ao seu componente.Se você deseja que este FocusListener seja global para todos os componentes do seu aplicativo, você pode considerar o uso de Programação Orientada a Aspectos (AOP).Com AOP é possível codificá-lo uma vez e aplicar seu ouvinte de foco a todos os componentes instanciados em seu aplicativo sem precisar copiar e colar o componente.addFocusListener(ouvinte) código em todo o seu aplicativo.

Seu aspecto teria que interceptar a criação de um JComponent (ou as subclasses às quais você deseja adicionar esse comportamento) e adicionar o ouvinte de foco à instância recém-criada.A abordagem AOP é melhor do que copiar e colar o FocusListener em todo o seu código porque você mantém tudo em um único trecho de código e não cria um pesadelo de manutenção quando decide alterar seu comportamento global, como remover o ouvinte para JSpinners.

Existem muitas estruturas AOP disponíveis para você escolher.Eu gosto JBossAOP já que é Java 100% puro, mas você pode querer dar uma olhada AspectJ.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top