JTextField - para entrada de data

Uma JTextField com controle e validações para entrada de data não foi uma coisa fácil de se fazer como foi no caso de entrada de valores e inteiros. Mas aí está.

JDateTextField

import java.awt.Font;

import java.awt.Point;

import java.awt.event.FocusEvent;

import java.awt.event.FocusListener;

import java.awt.event.KeyEvent;

import java.awt.event.KeyListener;

import java.text.DateFormat;

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Calendar;

import java.util.Date;

 

import javax.swing.JFormattedTextField;

import javax.swing.JFrame;

import javax.swing.JOptionPane;

import javax.swing.text.MaskFormatter;

 

/**

 * Componente power para entrada de data/hora. Possuir calendário opcional ao pressionar F12 e

 * recurso de validar e completar data inválida

 * @author junior

 *

 */

public class DateTextField extends JFormattedTextField {

 

 // este formatador faz a máscara de entrada de dados " /  / "

 public static MaskFormatter formatador;

 // constantes usadas para completar a data caso usuário deixe parte sem preencher

 private final String mesAtual = new SimpleDateFormat("MM").format(new Date());

 private final String anoAtual = new SimpleDateFormat("yy").format(new Date());

 

 // necessário a inicialização estática da variável maskformater pq ela é chamada no construtor (super())

 static {

         try {

                 formatador = new MaskFormatter("##/##/## LLL");

         catch(Exception ex) { 

                 ex.printStackTrace(); 

         }        

 }

 

 DateTextField() {

         super(formatador);

         // fonte mono espaçada para visualmente ficar bem melhor

         setFont(new Font("Courier", Font.PLAIN, 12));

         // define um tamanho máximo 13 colunas 12 para conteúdo + 1 para não ficar "justo"

         setColumns(13);

         // adiciona um listener para foco, quando "perder" o foco vamos completar/validar a data

         addFocusListener(new DateFieldFocusListener());

         // adiciona um listener para teclado, + e - para mudar a data e F12 para calendário

         addKeyListener(new DateFieldKeyListener());

 }

 

 /**

  * Retorna a data já convertida para Date 

  * @return O conteúdo da JDateTextField em formato java.util.Date

  */

 public Date getDate() {

         // não pega a parte que tem o dia da semana

         String dataStr = getText().substring(08);

         // vazio? tchau

         if (dataStr.equals("  /  /  ")) return null;

         // setLenient é para não aceitar datas inválidas, 

         // senão o java converte para data válida mais próxima

         DateFormat df = new SimpleDateFormat("dd/MM/yy");

         df.setLenient(false);

         try {

                 return df.parse(dataStr);

         }

         catch(ParseException e) {

                 e.printStackTrace();

                 return null;

         }                

 }

 

 /**

  * Modifica o conteúdo da JDateTextField para uma Data que o programa quiser

  * @param data entra como valor a ser "setado" na tela

  */

 public void setDate(Date data) {

         setText(new SimpleDateFormat("dd/MM/yy EEE").format(data));

 }

 

 /**

  * Método privado para completar a data quando estiver parcialmente preenchida pelo usuario

  * após completar, também valida a fim de a data não ser digitada errada

  */

 private void completaData() {

 

         String[] partes = getText().split("/");

         String dataStr = getText().substring(08);

         DateFormat df = new SimpleDateFormat("dd/MM/yy");

 

         // primeira parte, dia

         if (partes[0].equals("  ")) 

                 dataStr = df.format(new Date());

         else

                 // mes

                 if (partes[1].equals("  ")) 

                         dataStr = partes[0] + "/" + mesAtual + "/" + anoAtual;

                 else // ano

                         if (partes[2].substring(02).equals("  ")) // note o substring por causa do dia da semana

                                 dataStr = partes[0] + "/" + partes[1] + "/" + anoAtual;

 

         // não converter datas erradas para valor mais aproximado válido (setLenient)

         df.setLenient(false);

         try {

                 Date d = df.parse(dataStr);

                 // atualiza conteúdo do campo com o dia da semana

                 setText(new SimpleDateFormat("dd/MM/yy EEE").format(d));

         }

         catch(ParseException e) {

                 JOptionPane.showMessageDialog(null"O valor de data digitado não é válido""Erro de conversão", JOptionPane.ERROR_MESSAGE);

                 // atualiza conteúdo do campo com o dia da semana

                 setText(new SimpleDateFormat("dd/MM/yy EEE").format(new Date()));

                 requestFocus();

         }                

 }

 

 /** 

  * classe interna privata para controle de foco, quando detectado a perda de foco

  * o programa chama a função completaData()

  * @author junior

  * @see completaData()

  */

 public class DateFieldFocusListener implements FocusListener {

 

         public void focusGained(FocusEvent e) {}

 

         public void focusLost(FocusEvent e) {

                 completaData();

         }

 }

 

 /** 

  * classe interna priavata para controle de teclado, no qual o usuário usa + e -

  * ou então chama um calendário pressionando F12

  * @author junior

  *

  */

 private class DateFieldKeyListener implements KeyListener {

 

         public void keyPressed(KeyEvent e) {

                 // pressionou F12?

                 if (e.getKeyCode() == KeyEvent.VK_F12 ) {

                         // pega posição do JTextField em relação a tela do S.O.

                         Point p = getLocationOnScreen();

                         // chama um DataChooser posicionando logo abaixo da JTextField

                         DateChooser dc = new DateChooser((JFrame)null,"Escolha uma data", p.x, p.y + getHeight());

                         Date newDate = dc.select(getDate());

                         if (newDate != null

                                 setText(new SimpleDateFormat("dd/MM/yy EEE").format(newDate));

                         // envia o foco para o início da edit

                         setSelectionStart(0);

                         setSelectionEnd(0);

                 }

 

                 // pressionado + ou -?

                 if (e.getKeyCode() == KeyEvent.VK_ADD || (e.getKeyCode() == KeyEvent.VK_SUBTRACT)) {

                         Date d = getDate();

                         if (d == null)

                                 setDate(new Date());

                         else {

                                 // usa a classe Calendar para adicionar/subtrair 1 dia

                                 Calendar calendar = Calendar.getInstance();

                     calendar.setTime(d);

                     calendar.add(Calendar.DATE, (e.getKeyCode() == KeyEvent.VK_ADD ? 1 : -1));

                     setDate(calendar.getTime());

                     // verificar SHIFT / ALT

                         }

                         // envia o foco para o início da edit

                         setSelectionStart(0);

                         setSelectionEnd(0);

                 }

         }

         public void keyReleased(KeyEvent e){}

         public void keyTyped(KeyEvent e){}

 

 }

}

 

Exemplo de uso

import java.awt.FlowLayout;

import javax.swing.JFrame;

 

public class Geral extends JFrame {

 

 private DateTextField campo1;

 private DateTextField campo2;

 

 public Geral() {

 

         setSize(200110);

         setDefaultCloseOperation(EXIT_ON_CLOSE);

         setLayout(new FlowLayout());

 

         campo1 = new DateTextField();

         campo2 = new DateTextField();

 

         add(campo1);

         add(campo2);

 

         setVisible(true);

 }

 

 public static void main(String[] args) {

         new Geral();

 }        

}

 

Tela

Pressione F12 para abrir o calendário

Se deixar a data incompleta (apenas com dia ou mês e dia) o programa completa a data

Ao pressionar + e - a data aumenta e diminui.

 

Veja também

Classe DateChooser