Re: KeyEvent consume() not working on JTextField, what am I doing
wrong?
After reading some more post I finally found one that solved the
problem. In the addKeyListener there are 3 actions that can be
overwritten: keyPressed, keyTyped, and keyReleased. I was only
overriding keyPressed. However; the keyTyped was also going off and
doing the second character insert. To stop this you have to
synchronize all three actions so that they all consume the event.
Here is the fix that stopped the double insert.
myDateTimeSpinner.java
package timespinner;
import java.awt.event.KeyEvent;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerDateModel;
public class myDateTimeSpinner extends JPanel {
private Date date = null;
private JSpinner spinner = null;
JSpinner.DateEditor dateEditor = null;
private SpinnerDateModel sm = null;
boolean consumeKey = false; // scyncronize the consume calls for
the three types of keyevent actions
// Our Constants that I like the other people being able to see
public static final int YEAR_1 = 0;
public static final int YEAR_2 = 1;
public static final int YEAR_3 = 2;
public static final int YEAR_4 = 3; // (note: this is the 4th
char)
public static final int SL_1 = 4;
public static final int DAY_1 = 5;
public static final int DAY_2 = 6;
public static final int DAY_3 = 7; // (note: this is the 8th
char)
public static final int SP_1 = 8;
public static final int HR_1 = 9;
public static final int HR_2 = 10; // (note: this is the 11th
char)
public static final int CO_1 = 11;
public static final int MM_1 = 12;
public static final int MM_2 = 13; // (note: this is the 14th
char)
public static final int CO_2 = 14;
public static final int SS_1 = 15;
public static final int SS_2 = 16; // (note: this is the 17th
char)
private static final int BASE_LEAP_YEAR = 1972; // This number
gives us the base year (since 1972 had a 29th day of Feb)
public myDateTimeSpinner() {
date = new Date();
jbInit();
}
public myDateTimeSpinner(Date myDate) {
date = myDate;
jbInit();
}
public long getDate() {
return date.getTime();
}
public String getDateString() {
return dateEditor.getTextField().getText();
}
public void jbInit() {
sm = new SpinnerDateModel(date, null, null,
Calendar.HOUR_OF_DAY);
spinner = new JSpinner(sm);
dateEditor = new JSpinner.DateEditor(spinner, "yyyy/DDD
HH:mm:ss");
spinner.setEditor(dateEditor);
dateEditor.getTextField().addKeyListener(new
java.awt.event.KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
consumeKey = keyPressed_actionPerformed(e);
if (consumeKey) {
e.consume();
}
}
@Override
public void keyTyped(KeyEvent ke) {
if (consumeKey) {
ke.consume();
}
}
@Override
public void keyReleased(KeyEvent ke) {
if (consumeKey) {
ke.consume();
}
consumeKey = false;
}
});
add(spinner);
}
private boolean keyPressed_actionPerformed(KeyEvent e) {
// Let's just allow only Arrow keys and nothing else
// Trap the keys that we allow them to have KEY_TYPED
boolean consume = false;
int intKeyCode = e.getKeyCode();
switch (intKeyCode) {
// Let them do numbers
case KeyEvent.VK_0:
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
case KeyEvent.VK_8:
case KeyEvent.VK_9:
checkString(intKeyCode);
consume = true;
break;
// Let them do number pad as well
case KeyEvent.VK_NUMPAD0:
case KeyEvent.VK_NUMPAD1:
case KeyEvent.VK_NUMPAD2:
case KeyEvent.VK_NUMPAD3:
case KeyEvent.VK_NUMPAD4:
case KeyEvent.VK_NUMPAD5:
case KeyEvent.VK_NUMPAD6:
case KeyEvent.VK_NUMPAD7:
case KeyEvent.VK_NUMPAD8:
case KeyEvent.VK_NUMPAD9:
// Need to convert them back to key code
// (Use the magic number!!)
intKeyCode = intKeyCode - 48;
checkString(intKeyCode);
consume = true;
break;
case KeyEvent.VK_LEFT:
case KeyEvent.VK_RIGHT:
// Let them go left or right
nextPosition(intKeyCode, cursorAt());
break;
case KeyEvent.VK_DOWN:
case KeyEvent.VK_UP:
case KeyEvent.VK_ESCAPE:
case KeyEvent.VK_TAB:
break;
default:
break;
}
return consume;
}
public int cursorAt() {
// Always returns the LEFT MOST position of the selected chars
return dateEditor.getTextField().getSelectionStart();
}
/**
* This basically tells us if the use is on the right spot
* @param intKeyCode The key that was pressed
* @param intCharPos The position it was in
*/
//
-------------------------------------------------------------------------
public void nextPosition(int intKeyCode, int intCharPos) {
if (intKeyCode == KeyEvent.VK_LEFT) {
// Do the left key case
switch (intCharPos) {
case SS_1:
intCharPos = MM_2 + 1;
break;
case MM_1:
intCharPos = HR_2 + 1;
break;
case HR_1:
intCharPos = DAY_3 + 1;
break;
case DAY_1:
intCharPos = YEAR_4 + 1;
break;
default:
break;
}
// Reposition the Cursor (But advance it one char
setCursorPos(intCharPos);
} else if (intKeyCode == KeyEvent.VK_RIGHT) {
// Do the right key case
switch (intCharPos) {
case YEAR_4:
intCharPos = DAY_1 - 1;
break;
case DAY_3:
intCharPos = HR_1 - 1;
break;
// All the " day "
case HR_2:
intCharPos = MM_1 - 1;
break;
case MM_2:
intCharPos = SS_1 - 1;
break;
default:
break;
}
// Reposition the Cursor (But advance it one char
setCursorPos(intCharPos);
} else {
// Do nothing
}
}
/**
* Checks to see if the <code>String</code> just entered is
correct
* (Note: this one allows "invalid" strings)
* If it is correct, the appropriate data storage and GUI elements
are
* updated.
*
* @param intKeyCode The key that was pressed, so we can either
reject/accept.
*
* <P>
* author John Kerich<br>
* version 2.0, IDR 225 04/12/2006 Add class:member and verbose
level to printLogMsg.
*/
public void checkString(int intKeyCode) {
// The goal of this procedure is to allow user to type
// in ANY "String" (Well, close to any) so they won't
// be stopped when they want to continue on
// Therefore, we don't even need a UtDate or UtDuration object
String strTempNewValue;
// This is where the last char was changed
// Since everytime a key is press the cursor advances
int intCharPos = cursorAt();
// 1. Check to see if it's even worth consider
if (isReplaceable(intCharPos)) {
// 2. Get the string we need
String strOriginal = dateEditor.getTextField().getText();
strTempNewValue = replaceIndex(intCharPos,
KeyEvent.getKeyText(intKeyCode));
if (!isValidString(strTempNewValue)) {
dateEditor.getTextField().setText(strOriginal);
setCursorPos(intCharPos);
return;
}
// 3. Call the User Logic Object with this value
Date d = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/DDD
HH:mm:ss");
d = sdf.parse(strTempNewValue);
} catch (ParseException e) {
dateEditor.getTextField().setText(strOriginal);
setCursorPos(intCharPos);
return;
}
// we're decide how many space to skip
date = d;
dateEditor.getTextField().setText(strTempNewValue);
switch (intCharPos) {
// According to this: 1998/240 14:14:45
case YEAR_4:
case DAY_3:
case HR_2:
case MM_2:
intCharPos = intCharPos + 2;
break;
default:
// By default, don't let them type anything
intCharPos = intCharPos + 1;
break;
}
setCursorPos(intCharPos);
}
}
/**
* Set the position of the cursor on the text field
* @param intCurPos The interger position of the text field
*/
//
-------------------------------------------------------------------------
public void setCursorPos(int intCurPos) {
dateEditor.getTextField().setSelectionStart(intCurPos);
dateEditor.getTextField().setSelectionEnd(intCurPos);
}
/**
* This basically tells us if the use is on the right spot
*
* @return true - valid or false - invalid
*
* @param intKeyCode The key that was pressed
* @param intCharPos The position
*/
//
-------------------------------------------------------------------------
public boolean isReplaceable(int intCharPos) {
boolean bResult = false;
// Check the position of the cursor
switch (intCharPos) {
// Let them do numbers
case YEAR_1:
case YEAR_2:
case YEAR_3:
case YEAR_4:
case DAY_1:
case DAY_2:
case DAY_3:
case HR_1:
case HR_2:
case MM_1:
case MM_2:
case SS_1:
case SS_2:
bResult = true;
break;
default:
bResult = false;
break;
}
return bResult;
}
/**
* Given a String (array of char) and an arbitrary char,
* replace the given index spot with the
* given char in 2nd argu
* @param strOriginal The Original string
* @param intIndex The index position to replace the
original String
* @param strSomeString The new charactor we're about to
intruduce
* @return The New String that has been replaced
*/
//
-------------------------------------------------------------------------
public String replaceIndex(int intIndex, String strSomeString) {
String strResult = new String();
String strHead = new String();
String strTail = new String();
String strOriginal = dateEditor.getTextField().getText();
int intOriginalLength = strOriginal.length();
// Check for Index bounds
if (intIndex < intOriginalLength) {
if (intIndex == 0) {
// Just replace the first char (since "zero" is a
special case)
strResult =
strSomeString.concat(strOriginal.substring(1));
} else {
// Get the beginning of the subString
strHead = strOriginal.substring(0, intIndex);
// Then concat the rest
strTail =
strOriginal.substring(intIndex + 1);
strResult =
strHead + strSomeString + strTail;
}
} else {
strResult = strOriginal;
}
return strResult;
}
/**
* This code is to decouple the reliance on "UtDate"
* The story is, it (UtDate) used to able set a "date" with a
String
* e.g. 1998/240 25:00:15 will become 1998/241 02:00:15
* this "implicit" (automatic) rolling FEATURE as of Oct 9, 1998
* So therefore, I've decided NOT to rely on such and use
* "isValidString" to validate user input
* Date Format 1998/240 14:14:45
*
* @return true - Date was valid or false - Date was invalid
*
* @param strSomeDate The <code>String</code> that is going to
be displayed
*
*/
public boolean isValidString(String strSomeDate) {
boolean bResult = false;
boolean bGoodDay = false;
boolean bGoodHour = false;
boolean bGoodMinute = false;
boolean bGoodSecond = false;
int intDaysOfaYear = 365;
// Get our current Date string's Year part
Integer intDaysInThisYear =
Integer.valueOf(strSomeDate.substring(YEAR_1, YEAR_4 + 1));
// Tells us if this current year is a leap year
if (isLeapYear(intDaysInThisYear.intValue())) {
// This way we can have 366 in our leap years
intDaysOfaYear = 366;
}
// Figure out what our Day value is
Integer intSomeDay =
Integer.valueOf(strSomeDate.substring(DAY_1, DAY_3 + 1));
if ((intSomeDay.intValue() < intDaysOfaYear) &&
(intSomeDay.intValue() > 0)) {
bGoodDay = true;
}
// Figure out what our Hour value is
Integer intSomeHour =
Integer.valueOf(strSomeDate.substring(HR_1, HR_2 + 1));
if (intSomeHour.intValue() < 24) {
bGoodHour = true;
}
// Figure out what our Minute value is
Integer intSomeMinute =
Integer.valueOf(strSomeDate.substring(MM_1, MM_2 + 1));
if (intSomeMinute.intValue() < 60) {
bGoodMinute = true;
}
// Figure out what our Second value is
Integer intSomeSecond =
Integer.valueOf(strSomeDate.substring(SS_1, SS_2 + 1));
if (intSomeSecond.intValue() < 60) {
bGoodSecond = true;
}
// Finally decide our return value
if (bGoodDay && bGoodHour && bGoodMinute && bGoodSecond) {
bResult = true;
}
return bResult;
}
// Tells us if this is a leap year
public boolean isLeapYear(int intSomeYear) {
boolean bResult = false;
int intDifference = intSomeYear - BASE_LEAP_YEAR;
// Check to see if they're four year apart
if (intDifference % 4 == 0) {
bResult = true;
}
return bResult;
}
}