Dúvida sobre formulários no swing

14 respostas
T

Boa tarde^^

eu estava tentando fazer uma formulário pra inserção de dados em um banco de dados no swing em java, porém estou travado na validação dos dados, eu gostaria que o jFormattedTextField fosse mais específico como aceitar somente um certo número de dígitos (8 pro CEP, por exemplo) e que mostrasse alguma mensagem de erro que somente indicasse os campos errados no próprio formulário sem travar a edição mas que travasse o envio do formulário pro banco de dados…

Eu sou muito iniciante ainda, então qualquer dica é muito bem-vinda!

vlw!

14 Respostas

doug

Olá
Tenta ver sobre listener, mais específico o keylistener ou focuslistener… para sua caixa de
edição.
Guia de referencia da sun, Link1, link2

Espero ter ajudado
Flwsss

T

vlw thiago, acho q vou usar mto isso^^

mas ainda não sei como começar, na verdade, no caso do campo CEP por exemplo, eu gostaria que não fosse possivel adicionar mais de 8 digitos, então não seria necessário uma mensagem de validação…

O que eu gostaria de saber é se tem como eu verificar o campo assim que ele perder o foco, e, caso necessário, mostar uma mensagem indicado o campo incorreto…

renzonuccitelli

Eu também não curtia a forma de validação do FormatedTextField, então eu construi um textField para validar moeda Real. Acho que dá pra te dar uma idéia de como fazer.

public class TextFieldMoedaReal extends JTextField implements KeyListener {
	private StringBuilder lastValidNumber;
	private int maxDigits=10;
	
	
	/**
	 * Aceita um BigDecimal com escala 2. 
	 * Outras escalas não são serão aceitas
	 * 
	 * @param BigDecimal
	 */
	 
	public void setNumber(BigDecimal decimal){
		if(decimal.scale()==2&&decimal.unscaledValue().toString().length()<maxDigits){
			lastValidNumber=new StringBuilder(decimal.unscaledValue().toString());
			super.setText(getRealFormat());
		}
	}
	/**Retorna um BigDecimal com o valor encontrando no campo
	 * e com escala igual a 2
	 * 
	 * @return BigDecimal
	 */
	public BigDecimal getNumber(){
		BigDecimal number;
		if(lastValidNumber.length()==0)
			number=new BigDecimal("0");
		else
			number=new BigDecimal(lastValidNumber.toString());
		number.setScale(2);
		return number.divide(new BigDecimal(100));
	}
	
	/**Aceita valor em centavos. 
	 * O valor não deve possuir ponto ou vírgula
	 * 
	 * @param String
	 */
	public void setText(String number){
		if(isNumber(number)&&number.length()<maxDigits)
			lastValidNumber=new StringBuilder(number);
		else if("".equals(number))
			lastValidNumber=new StringBuilder();
		super.setText(getRealFormat());
		
	}
	
	private boolean isNumber(String number){
		for(char c: number.toCharArray()){
			if(!Character.isDigit(c))
				return false;
		}
		return true;
	}
	public TextFieldMoedaReal() {
		super();
		this.setCaretPosition(this.getText().length());
		this.addKeyListener(this);
		lastValidNumber=new StringBuilder();
		super.setText(getRealFormat());
	}
	
	@Override
	public void keyPressed(KeyEvent e) {
		
	}

	@Override
	public void keyReleased(KeyEvent e) {
		if(Character.isDigit(e.getKeyChar())&&lastValidNumber.length()<maxDigits){
			lastValidNumber.append(e.getKeyChar());
		}
		else if(e.getKeyCode()==KeyEvent.VK_BACK_SPACE||e.getKeyCode()==KeyEvent.VK_DELETE){
			if(lastValidNumber.length()>0){
				lastValidNumber.deleteCharAt(lastValidNumber.length()-1);
			}
		}

		super.setText(getRealFormat());
		
	}

	@Override
	public void keyTyped(KeyEvent e) {
		
	}
	
	private String getRealFormat(){
		if(lastValidNumber.length()==0)
			return "0,00";
		else if(lastValidNumber.length()==1)
			return "0,0"+lastValidNumber;
		else if(lastValidNumber.length()==2)
			return "0,"+lastValidNumber;
		else
			return buildPrefixSeparatedWithDots()+lastValidNumber.substring(lastValidNumber.length()-2);
	}
	
	private String buildPrefixSeparatedWithDots(){
		StringBuilder builder=new StringBuilder();
		for(int index=0;index<lastValidNumber.length()-2;++index){
			builder.append(lastValidNumber.charAt(index));
			if((lastValidNumber.length()-index)%3==0&&lastValidNumber.length()-index>5)
				builder.append(".");
		}
		builder.append(',');
		return builder.toString();
	}
	public int getMaxDigits() {
		return maxDigits;
	}
	public void setMaxDigits(int maxDigits) {
		this.maxDigits = maxDigits;
	}
	
}
T

Renzo, vc usa o NetBeans??

sf.marcius

usa a classe javax.swing.text.MaskFormatter

no construtor do form faz algo como:

...
        try {
            MaskFormatter formatter = new MaskFormatter("#####-###");
            formatter.setPlaceholderCharacter('_');
            formatter.install(cepFormattedTextField);
        } catch (ParseException ex) {
            // tratar excessao
        }
        ...
        // mostrar form
renzonuccitelli

Eu uso Eclipse, não gosto dos códigos que as ferramentas geram para GUI's. Para formulários eu utilizo o SwingBean. Ele já apresenta algumas formas de controle. Contudo, eu alterei o fonte dele para melhorar o uso de máscaras. Isso porque quando vc vai limpar o formulário, o JFormattedTextField apresenta comportamento que eu não gosto. Por isso eu faço com que o usuário não possa deixar o campo se ele não estiver corretamente preenchido ou vazio. Pra isso coloco além da máscara um InputListener e deixo o fild com modo de atuação Persist, em vez de COMMIT. Vai aí o código do meu InputListener, caso vc use a máscara:

public class FormattedTextFieldVerifier extends InputVerifier {
	private String inicialValue;
    public FormattedTextFieldVerifier(JFormattedTextField ftf) {
		super();
		inicialValue=new String(ftf.getText());
	}
	public boolean verify(JComponent input) {
		
        if (input instanceof JFormattedTextField) {
            JFormattedTextField ftf = (JFormattedTextField)input;
            AbstractFormatter formatter = ftf.getFormatter();
            if (formatter != null) {
                String text = ftf.getText();
                try {
                     formatter.stringToValue(text);
                     return true;
                 } catch (ParseException pe) {
                     return inicialValue.equals(text);
                 }
             }
         }
         return true;
     }
     public boolean shouldYieldFocus(JComponent input) {
         return verify(input);
     }
 }

Esse InputListener eu tirei do site da SUN e fiz uma pequena alteração para aceitar ficar "vazio".

Como eu disse, eu seto o comportamento do field assim:

textField = new JFormattedTextField(new MaskFormatter(mask));
				JFormattedTextField ftf=(JFormattedTextField)textField;
				ftf.setInputVerifier(new FormattedTextFieldVerifier(ftf));
				ftf.setFocusLostBehavior(JFormattedTextField.PERSIST);
T

vlw marcius mas essa máscara é ruim pois ela permite a inserção fora de ordem e a validação é limitada tb…

rezo,

cara vc me deu exatamente o que eu queria!!!

eu usei a sua classe pra real como modelo e fiz uma pra CEP, ainda não está perfeita, mas funciona!!

olha o código:

package learn;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.math.BigDecimal;
import javax.swing.JTextField;

/**
 *
 * @author Wellington
 */
public class TextFieldCEP extends JTextField implements KeyListener {

    private StringBuilder lastValidNumber;
    private int maxDigits=8;

    //<editor-fold defaultstate="collapsed" desc="Sobrescirção dos métodos da implementação 'KeyListener'">
    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
    }
    /*Código executado quando se solta a tecla*/
    @Override
    public void keyReleased(KeyEvent e) {
        if(Character.isDigit(e.getKeyChar())&&lastValidNumber.length()<maxDigits){
             lastValidNumber.append(e.getKeyChar());
         }
         else if(e.getKeyCode()==KeyEvent.VK_BACK_SPACE||e.getKeyCode()==KeyEvent.VK_DELETE){
             if(lastValidNumber.length()>0){
                 lastValidNumber.deleteCharAt(lastValidNumber.length()-1);
             }
         }

         super.setText(getRealFormat());
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Sobrescrição dos métodos da herança 'JtextField'">
    public TextFieldCEP() {
         super();
         this.setCaretPosition(this.getText().length());
         this.addKeyListener(this);
         lastValidNumber=new StringBuilder();
         super.setText(getRealFormat());
     }

    public void setNumber(BigDecimal decimal){
         if(decimal.scale()==2&&decimal.unscaledValue().toString().length()<maxDigits){
             lastValidNumber=new StringBuilder(decimal.unscaledValue().toString());
             super.setText(getRealFormat());
         }
    }

    public BigDecimal getNumber(){
         BigDecimal number;
         if(lastValidNumber.length()==0)
             number=new BigDecimal("0");
         else
             number=new BigDecimal(lastValidNumber.toString());
         number.setScale(2);
         return number.divide(new BigDecimal(100));
     }
    //</editor-fold>

    /*Método responsável pela criação da string que será impressa no TextField*/
    private String getRealFormat(){
        StringBuilder builder=new StringBuilder();    
         if(lastValidNumber.length()==0)
             return "Digite o CEP";
         else if(lastValidNumber.length()<=3)
             return "-"+lastValidNumber;         
         else{
            for(int index=0;index<lastValidNumber.length()-3;++index)
                builder.append(lastValidNumber.charAt(index));
            builder.append('-');
            return builder.toString()+lastValidNumber.substring(lastValidNumber.length()-3);
         }  
     }
}

eu gostaria de deixar uma mensagem quando o campo estiver vazio como ‘Digite o CEP’, mas gostaria que ficasse em uma cor mais clara tipo uma cinza claro ou azul…

eu não entendi o seu 2o post da classe FormattedTextFieldVerifier, vc usa ele pra verificar se todos os campos estão preenchidos?

renzonuccitelli

Eu uso ela pra não deixar a pessoa sair do campo com máscara enquanto ele não estiver corretamente preenchido. Isso porque eu tinha alguns problemas quando ia limpar um formulário no SwingBean.
Mas voltando ao assunto validação, eu li esse tutorial do GUJ, porque gostaria que não aparecessem os caracteres invalidos e depois desaparecessem. Mas ainda nao tive tempo de fazer uns testes. Se vc fizer alguma coisa do gênero, posta aí…

T

vou dar uma olhada nesse tuto sim

=]

eu tava tentando fazer o TextField pra data de nascimento, mas ao invés dele validar dinamicamente, o que seria complicado pela restrição do dia (que deve ser menor que 32) e mes (menor que 13), eu tava pensando em fazer a validação com a perda de foco, mas não está funcionando…

vc já usou o focuslistener?

a minha tentativa deu nesse código:

package Principal.newFolder;

import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.math.BigDecimal;
import javax.swing.JTextField;

/**
 *
 * @author Wellington
 */
public class TextFieldNascimento extends JTextField implements KeyListener, FocusListener {

    private StringBuilder lastValidNumber;
    private int maxDigits=8;

    //<editor-fold defaultstate="collapsed" desc="Sobrescrição dos métodos da implementação 'KeyListener'">
    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
    }
    /*Código executado quando se solta a tecla*/
    @Override
    public void keyReleased(KeyEvent e) {
        if(Character.isDigit(e.getKeyChar())&&lastValidNumber.length()<maxDigits){
             lastValidNumber.append(e.getKeyChar());
         }
         else if(e.getKeyCode()==KeyEvent.VK_BACK_SPACE||e.getKeyCode()==KeyEvent.VK_DELETE){
             if(lastValidNumber.length()>0){
                 lastValidNumber.deleteCharAt(lastValidNumber.length()-1);
             }
         }

         super.setText(getRealFormat());
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Sobrescrição dos métodos da implementação 'FocusListener'">
    @Override
    public void focusGained(FocusEvent e) {
        
    }
    /*Código executado quando move-se o cursor para outro campo*/
    @Override
    public void focusLost(FocusEvent e) {
        StringBuilder dia = new StringBuilder();
        StringBuilder mes = new StringBuilder();
        StringBuilder ano = new StringBuilder();

        if (lastValidNumber.length()==8){
            for (int index = 0;index<=7;index++){
                if (index<=1){
                   dia.append(lastValidNumber.charAt(index)); 
                }
                else if (index > 1 && index<=3){
                   mes.append(lastValidNumber.charAt(index)); 
                }
                else if (index > 3 && index<=7){
                   ano.append(lastValidNumber.charAt(index)); 
                }                
            }
            if (Integer.parseInt(dia.toString())>31 || Integer.parseInt(mes.toString())>12){
                lastValidNumber.delete(0,lastValidNumber.length());
                getRealFormat();
            }
            
        }
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Sobrescrição dos métodos da herança 'JtextField'">
    public TextFieldNascimento() {
         super();
         this.setCaretPosition(this.getText().length());
         this.addKeyListener(this);
         lastValidNumber=new StringBuilder();
         super.setText(getRealFormat());
     }

    public void setNumber(BigDecimal decimal){
         if(decimal.scale()==2&&decimal.unscaledValue().toString().length()<maxDigits){
             lastValidNumber=new StringBuilder(decimal.unscaledValue().toString());
             super.setText(getRealFormat());
         }
    }

    public BigDecimal getNumber(){
         BigDecimal number;
         if(lastValidNumber.length()==0)
             number=new BigDecimal("0");
         else
             number=new BigDecimal(lastValidNumber.toString());
         number.setScale(2);
         return number.divide(new BigDecimal(100));
     }
    //</editor-fold>

    /*Método responsável pela criação da string que será impressa no TextField*/
    private String getRealFormat(){
        StringBuilder builder=new StringBuilder();
         if(lastValidNumber.length()==0)
             return "";
         else if(lastValidNumber.length()<=4)
             return ""+lastValidNumber;
         else{
            for(int index=0;index<lastValidNumber.length()-3;++index){
                builder.append(lastValidNumber.charAt(index));
                if (index!=0 && index%2==1)
                builder.append('/');
            }
            return builder.toString()+lastValidNumber.substring(lastValidNumber.length()-3);
         }
     }

}

^^

e vc sabe pq tem ‘@Override’ antes dos métodos das interfaces??

desculpa a avalanche de perguntas, mas eu to começando agora e to totalmente a deriva^^

vlw!

renzonuccitelli

Bom, como eu uso o SwingBean ele já coloca um componente gráfico muito legal para datas. Mas se vc não kiser utilizar o framework, eu olhei o fonte e ele usa o componente encontrado nesse link para datas. Dá uma olhada aí.

T

muito bom o calendário renzo, porém, ele não tem nenhuma validação caso a data seja inserida diretamente no campo, o que permitiria a inserção de datas malucas^^

eu consegui usar o focuslistener! faltava eu colocar this.addFocusListener(this); na sobrescrição do construtor -.-’

iauhsdhasiud

agora ficou bom, ele não permite dias maiores que 31 e nem meses maiores que 12, caso haja, ele apaga todo o campo.
teve um outro efeito que não foi intencional, quando vc aperta DELETE ou BACKSPACE ele apaga todo o campo ao invés de apagar somente um caractere, mas eu achei melhor assim, pq antes vc tinha que apertar um DELETE pra cada char^^

testa o codigo ae:

package Principal.newFolder;

import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.math.BigDecimal;
import javax.swing.JTextField;

/**
 *
 * @author Wellington
 */
public class TextFieldNascimento extends JTextField implements KeyListener, FocusListener {

    private StringBuilder lastValidNumber;
    private int maxDigits=8;

    
    //Sobrescrição dos métodos da implementação 'KeyListener'
    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
    }
    /*Código executado quando se solta a tecla*/
    @Override
    public void keyReleased(KeyEvent e) {
        if(Character.isDigit(e.getKeyChar())&&lastValidNumber.length()<maxDigits){
             lastValidNumber.append(e.getKeyChar());
         }
         else if(e.getKeyCode()==KeyEvent.VK_BACK_SPACE||e.getKeyCode()==KeyEvent.VK_DELETE){
             if(lastValidNumber.length()>0){
                 lastValidNumber.deleteCharAt(lastValidNumber.length()-1);
             }
         }

         super.setText(getRealFormat());
    }
    
    //Sobrescrição dos métodos da implementação 'FocusListener'
    @Override
    public void focusGained(FocusEvent e) {
        
    }
    /*Código executado quando move-se o cursor para outro campo*/
    @Override
    public void focusLost(FocusEvent e) {
        VerificaCampo();
    }
    

   //Sobrescrição dos métodos da herança 'JtextField'
    public TextFieldNascimento() {
         super();
         this.setCaretPosition(this.getText().length());
         this.addKeyListener(this);
         this.addFocusListener(this);
         lastValidNumber=new StringBuilder();
         super.setText(getRealFormat());
     }

    public void setNumber(BigDecimal decimal){
         if(decimal.scale()==2&&decimal.unscaledValue().toString().length()<maxDigits){
             lastValidNumber=new StringBuilder(decimal.unscaledValue().toString());
             super.setText(getRealFormat());
         }
    }

    public BigDecimal getNumber(){
         BigDecimal number;
         if(lastValidNumber.length()==0)
             number=new BigDecimal("0");
         else
             number=new BigDecimal(lastValidNumber.toString());
         number.setScale(2);
         return number.divide(new BigDecimal(100));
     }
    

    /*Método responsável pela criação da string que será impressa no TextField*/
    private String getRealFormat(){
        StringBuilder builder=new StringBuilder();
         if(lastValidNumber.length()==0)
             return "";
         else if(lastValidNumber.length()<=4)
             return ""+lastValidNumber;
         else{
            for(int index=0;index<lastValidNumber.length()-3;++index){
                builder.append(lastValidNumber.charAt(index));
                if (index!=0 && index%2==1)
                builder.append('/');
            }
            return builder.toString()+lastValidNumber.substring(lastValidNumber.length()-3);
         }
     }
    
    private void VerificaCampo(){
        StringBuilder dia = new StringBuilder();
        StringBuilder mes = new StringBuilder();
        StringBuilder ano = new StringBuilder();

        if (lastValidNumber.length()==8){
            for (int index = 0;index<=7;index++){
                if (index<=1){
                   dia.append(lastValidNumber.charAt(index));
                }
                else if (index > 1 && index<=3){
                   mes.append(lastValidNumber.charAt(index));
                }
                else if (index > 3 && index<=7){
                   ano.append(lastValidNumber.charAt(index));
                }
            }
            if (Integer.parseInt(dia.toString())>31 || Integer.parseInt(mes.toString())>12){
                lastValidNumber.delete(0,lastValidNumber.length());
                for (int index=0; index<=7; index++)
                    lastValidNumber.append(' ');
            }
            super.setText(getRealFormat());
            lastValidNumber.delete(0,lastValidNumber.length());
        }
    }

}
renzonuccitelli

Só algumas ressavas:

  1. Se não me engano o componente é setado via classe Date, entao nao vai ter como setar datas inválidas.
  2. Se o seu sistema for bem feito o suficiente, ele não vai deixar uma data mal formada ser inserida no BD, e por isso nunca vai ter data inválida para ser inserida.
  3. Vc poderia fazer sus própria validação na hora de setar
  4. O controle de datas é um pouco mais complexo que nao permitir dias maiores que 31. Há os meses que possuem máximos de dias diferentes e ainda há os anos bissextos.

Flw.

T

coloquei mais condições no IF de validação^^

if (Integer.parseInt(dia.toString())==0
                    || Integer.parseInt(mes.toString())==0
                    || Integer.parseInt(mes.toString())>12
                    || (Integer.parseInt(ano.toString())%4==0 && Integer.parseInt(mes.toString())==2 && Integer.parseInt(dia.toString())>29)
                    || (Integer.parseInt(ano.toString())%4!=0 && Integer.parseInt(mes.toString())==2 && Integer.parseInt(dia.toString())>28)
                    || (Integer.parseInt(mes.toString())%2==0 && Integer.parseInt(mes.toString())<8 && Integer.parseInt(dia.toString())>30)
                    || (Integer.parseInt(mes.toString())%2==1 && Integer.parseInt(mes.toString())<8 && Integer.parseInt(dia.toString())>31)
                    || (Integer.parseInt(mes.toString())%2==0 && Integer.parseInt(mes.toString())>7 && Integer.parseInt(dia.toString())>31)
                    || (Integer.parseInt(mes.toString())%2==1 && Integer.parseInt(mes.toString())>7 && Integer.parseInt(dia.toString())>30))

a validação na hora de setar teria que ser depois do programa ter se comunicado com o banco de dados, não é? Se sim, seria algo mais lento, eu gostaria de uma validação dinamica (no estilo JavaScript) ^^

eu vou tentar fundir os dois, o meu validador, com aquele calendário, mas pra isso eu precisaria de uma forma de informar o erro pro usuário, pq por exemplo, quando ele inserir 29/02/2006, pode ser que ele não perceba que não é ano bissexto e continue tentando até cansar…

vc usa alguma mensagem (além de labels^^) pra informar os erros?
algo assim:

renzonuccitelli

Eu uso as máscaras e estava usando algumas opções do SwingBean para fazer a validação. Mas aí desisti por algumas razões e vou deixar pra fazer a validação de forma não dinâmica mesmo, só depois que o usuário tentar cadastrar. Aí eu coloco o validador pra funcionar e ele retorna as mensagens de erro. Quanto ao fato de setar, ele não precisa ir pro BD não. Ele passa para pela camada de validação antes, então se ele não passar pelos testes vc lança a mensagem de erro antes de haver interação com o BD.
Com relação ao componente, ele já mostra os dias disponíveis da semana, inclusive mostrando os dias da semana em que caem os dias, o que eu acho interessante. Mas enfim, cada usa o que acha melhor.

Criado 27 de dezembro de 2008
Ultima resposta 9 de jan. de 2009
Respostas 14
Participantes 4