In one of my java applications, there is a field where the user is supposed to enter a time. I know, I can simply keep it as a normal JTextField and at the time of validation I can check the value and so on...
But I think there is a better way than that..
For example, if this is a matter with a date, then JXDatePicker (which comes with swingx) can be used very handily. Eventually, the users selection is guaranteed to be a date.
Likewise, there must be a better way for time selection too. Will someone share your knowledge if you know a better solution.
Any thought is appreciated. Thank you!
you could use a JFormattedTextField have a look at the example below, this will create a JFormattedTextField which will accept only numbers and put them in the form XXhXXminXXs then add a ActionListener to the JFormattedTextField and in that attempt to parse to a valid time object when ENTER for now I only show the use of the JFormattedTextField and the ActionListener I didn't attempt to convert or any converting and parsing for valid time:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.text.MaskFormatter;
public class FormattedTextFieldExample {
public FormattedTextFieldExample() {
initComponents();
}
private void initComponents() {
JFrame frame = new JFrame("JFormattedTextField Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MaskFormatter mask = null;
try {
mask = new MaskFormatter("##h##min##s");//the # is for numeric values
mask.setPlaceholderCharacter('#');
} catch (ParseException e) {
e.printStackTrace();
}
//
// Create a formatted text field that accept a valid time.
//
final JFormattedTextField timeField = new JFormattedTextField(mask);
//Add ActionListener for when enter is pressed
timeField.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
Object source = ae.getSource();
if (source == timeField) {
//parse to a valid time here
System.out.println(timeField.getText());
}
}
});
frame.add(timeField);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new FormattedTextFieldExample();
}
});
}
}
Multiple JSpinner instances allowing to select hours, minutes and seconds
Use a JFormattedTextField with some improvements to provide immediate user feedback (for example color the background red as soon as the input becomes invalid)
I did a prototype of a TimeField some time ago, it still needs a little work, but the concept is pretty basic.
It basically presents two JTextFields, one for the hour and one for the minutes and makes them appear to be a single field.
Through the use of some DocumentFilters, it restricts the users input.
I did this some time ago and it needs work, but the basic idea is there...if you have problems, you will need to try and figure it out yourself ;)
/**
*
* #author MadProgrammer
*/
public class TimeField extends javax.swing.JPanel {
// The time of day...
public enum TimeOfDay {
AM,
PM
}
private HourDocumentFilter hourDocumentFilter;
private MinuteDocumentFilter minDocumentFilter;
private HourKeyHandler hourKeyHandler;
private MinuteKeyHandler minuteKeyHandler;
private HourFocusHandler hourFocusHandler;
private MinuteFocusHandler minuteFocusHandler;
private boolean use24HourClock;
private ActionHandler actionHandler;
/**
* Creates new form TimeField
*/
public TimeField() {
initComponents();
pnlFields.setBorder(new CompoundBorder(UIManager.getBorder("TextField.border"),new EmptyBorder(0, 2, 0, 2)));
set24HourClock(false);
setTime(new Date());
fldHour.addKeyListener(new HourKeyHandler());
}
#Override
public void addNotify() {
super.addNotify();
// Add all the required functionality to make this thing work...
((AbstractDocument) fldHour.getDocument()).setDocumentFilter(getHourDocumentFilter());
((AbstractDocument) fldMin.getDocument()).setDocumentFilter(getMinuteDocumentFilter());
fldHour.addFocusListener(getHourFocusHandler());
fldMin.addFocusListener(getMinuteFocusHandler());
fldHour.addKeyListener(getHourKeyHandler());
fldMin.addKeyListener(getMinuteKeyHandler());
fldHour.addActionListener(getActionHandler());
fldMin.addActionListener(getActionHandler());
cmbTimeOfDay.addActionListener(getActionHandler());
}
#Override
public void removeNotify() {
// Clean up our listeners...
((AbstractDocument) fldHour.getDocument()).setDocumentFilter(null);
((AbstractDocument) fldMin.getDocument()).setDocumentFilter(null);
fldHour.removeFocusListener(getHourFocusHandler());
fldMin.removeFocusListener(getMinuteFocusHandler());
fldHour.removeKeyListener(getHourKeyHandler());
fldMin.removeKeyListener(getMinuteKeyHandler());
fldHour.removeActionListener(getActionHandler());
fldMin.removeActionListener(getActionHandler());
cmbTimeOfDay.removeActionListener(getActionHandler());
super.removeNotify();
}
/**
* Adds an action listener to the component. Actions are fired when the user
* presses the enter key
*
* #param listener
*/
public void addActionListener(ActionListener listener) {
listenerList.add(ActionListener.class, listener);
}
public void removeActionListener(ActionListener listener) {
listenerList.remove(ActionListener.class, listener);
}
/**
* Returns the field that is acting as the hour editor
*
* #return
*/
public JTextField getHourEditor() {
return fldHour;
}
/**
* Returns the field that is acting as the minute editor
*
* #return
*/
public JTextField getMinuteEditor() {
return fldMin;
}
/**
* Returns the combo box that provides the time of day selection
*
* #return
*/
public JComboBox getTimeOfDayEditor() {
return cmbTimeOfDay;
}
/**
* Returns the internal action handler. This handler monitors actions on the
* individual components and merges them into one.
*
* #return
*/
protected ActionHandler getActionHandler() {
if (actionHandler == null) {
actionHandler = new ActionHandler();
}
return actionHandler;
}
/**
* Returns the hour key listener
*
* #return
*/
protected HourKeyHandler getHourKeyHandler() {
if (hourKeyHandler == null) {
hourKeyHandler = new HourKeyHandler();
}
return hourKeyHandler;
}
/**
* Returns the minute key listener
*
* #return
*/
protected MinuteKeyHandler getMinuteKeyHandler() {
if (minuteKeyHandler == null) {
minuteKeyHandler = new MinuteKeyHandler();
}
return minuteKeyHandler;
}
/**
* Returns the document filter used to filter the hour field
*
* #return
*/
protected HourDocumentFilter getHourDocumentFilter() {
if (hourDocumentFilter == null) {
hourDocumentFilter = new HourDocumentFilter();
}
return hourDocumentFilter;
}
/**
* Returns the document filter user to filter the minute field
*
* #return
*/
protected MinuteDocumentFilter getMinuteDocumentFilter() {
if (minDocumentFilter == null) {
minDocumentFilter = new MinuteDocumentFilter();
}
return minDocumentFilter;
}
/**
* Returns the focus listener used to monitor the hour field
*
* #return
*/
protected HourFocusHandler getHourFocusHandler() {
if (hourFocusHandler == null) {
hourFocusHandler = new HourFocusHandler();
}
return hourFocusHandler;
}
/**
* Used the focus listener used to monitor the minute field
*
* #return
*/
protected MinuteFocusHandler getMinuteFocusHandler() {
if (minuteFocusHandler == null) {
minuteFocusHandler = new MinuteFocusHandler();
}
return minuteFocusHandler;
}
/**
* Sets the time based on the supplied date
*
* #param date
*/
public void setTime(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int hour = cal.get(Calendar.HOUR);
int min = cal.get(Calendar.MINUTE);
int dayPart = cal.get(Calendar.AM_PM);
TimeOfDay timeOfDay = TimeOfDay.AM;
switch (dayPart) {
case Calendar.PM:
timeOfDay = TimeOfDay.PM;
break;
}
setTime(hour, min, timeOfDay);
}
/**
* Sets the time based on a 24 hour clock. The field does not need to be in 24
* hour mode to use this method, the method will automatically correct the
* hour appropriately.
*
* #param hour
* #param min
*/
public void setTime(int hour, int min) {
hour = correctHour(hour);
min = correctMinute(min);
TimeOfDay timeOfDay = TimeOfDay.AM;
if (hour >= 12) {
timeOfDay = TimeOfDay.PM;
}
setTime(hour, min, timeOfDay);
}
/**
* Corrects the minute value to make sure it is within allowable ranges.
*
* For example, if you pass in 90 the method, it will automatically correct
* the value to 30, discard the overflow.
*
* This will not effect the hour value...although this might be worth
* consideration in the future
*
* #param min
* #return
*/
protected int correctMinute(int min) {
// Make sure the value is positive.
// If we were interested in altering the hour value as well, we wouldn't
// want to do this...
if (min < 0) {
min += (min * -2);
}
// Correct the minute value....
if (min > 59) {
// How many hours fit into this value
float part = min / 60f;
part = (float) (part - Math.floor(part)); // Get remainder
min = (int) (60 * part); // Calculate the number of minutes...
}
return min;
}
/**
* Basically, this method will attempt to correct the hour value and bring the
* it into range of a single day.
*
* We are basically going to try and figure out how many parts of the day that
* the hour falls in and make it equal to a single day...
*
* That is, if the hour is 35, it's actually 1.458... days, which is roughly 1
* day and 11 hours. We are only interested in the 11 hours, cause the date is
* irrelevant to us
*
* #param hour
* #return
*/
protected int correctHour(int hour) {
if (hour < 0) {
hour += (hour * -2);
}
if (hour > 23) {
float part = hour / 24f;
part = (float) (part - Math.floor(part));
hour = (int) (24 * part);
}
return hour;
}
/**
* Sets the time value for this field...
*
* #param hour
* #param min
* #param timeOfDay
*/
public void setTime(int hour, int min, TimeOfDay timeOfDay) {
hour = correctHour(hour);
min = correctMinute(min);
// Now that we have a correct hour value, we need to know if it will
// actually fit within the correct part of the day...
switch (timeOfDay) {
case AM:
cmbTimeOfDay.setSelectedIndex(0);
break;
case PM:
cmbTimeOfDay.setSelectedIndex(1);
break;
}
if (!is24HourClock()) {
if (hour > 12) {
hour -= 12;
}
} else {
if (hour < 12 && timeOfDay.equals(TimeOfDay.PM)) {
hour += 12;
}
}
fldHour.setText(pad(Integer.toString(hour), 2));
fldMin.setText(pad(Integer.toString(min), 2));
}
public int getHour() {
return Integer.parseInt(getHourEditor().getText());
}
public int getMinute() {
return Integer.parseInt(getMinuteEditor().getText());
}
public TimeOfDay getTimeOfDay() {
TimeOfDay tod = null;
switch (cmbTimeOfDay.getSelectedIndex()) {
case 0:
tod = TimeOfDay.AM;
break;
case 1:
tod = TimeOfDay.PM;
break;
}
return tod;
}
/**
* Sets if we should be using 24 or 12 hour clock. This basically configures
* the time of day field and the validation ranges of the various fields
*
* #param value
*/
public void set24HourClock(boolean value) {
if (value != use24HourClock) {
use24HourClock = value;
cmbTimeOfDay.setVisible(!use24HourClock);
if (cmbTimeOfDay.getSelectedIndex() == 1) {
setTime(getHour() + 12, getMinute(), getTimeOfDay());
}
invalidate();
firePropertyChange("24HourClock", !use24HourClock, value);
}
}
/**
* Returns if this is using a 24 or 12 hour clock
*
* #return
*/
public boolean is24HourClock() {
return use24HourClock;
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
#SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
cmbTimeOfDay = new javax.swing.JComboBox();
pnlFields = new javax.swing.JPanel();
lblSeperator = new javax.swing.JLabel();
fldHour = new javax.swing.JTextField();
fldMin = new javax.swing.JTextField();
addFocusListener(new java.awt.event.FocusAdapter() {
public void focusGained(java.awt.event.FocusEvent evt) {
doFocusGained(evt);
}
});
setLayout(new java.awt.GridBagLayout());
cmbTimeOfDay.setModel(new javax.swing.DefaultComboBoxModel(new String[]{"am", "pm"}));
cmbTimeOfDay.setBorder(null);
cmbTimeOfDay.setEditor(null);
cmbTimeOfDay.setOpaque(false);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
add(cmbTimeOfDay, gridBagConstraints);
pnlFields.setBackground(new java.awt.Color(255, 255, 255));
pnlFields.setLayout(new java.awt.GridBagLayout());
lblSeperator.setText(":");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.insets = new java.awt.Insets(0, 2, 0, 2);
pnlFields.add(lblSeperator, gridBagConstraints);
fldHour.setBorder(null);
fldHour.setColumns(2);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
pnlFields.add(fldHour, gridBagConstraints);
fldMin.setBorder(null);
fldMin.setColumns(2);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 0;
pnlFields.add(fldMin, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
add(pnlFields, gridBagConstraints);
}// </editor-fold>
private void doFocusGained(java.awt.event.FocusEvent evt) {
fldHour.requestFocus();
}
// Variables declaration - do not modify
private javax.swing.JComboBox cmbTimeOfDay;
private javax.swing.JTextField fldHour;
private javax.swing.JTextField fldMin;
private javax.swing.JLabel lblSeperator;
private javax.swing.JPanel pnlFields;
// End of variables declaration
/**
* Moves the focus forward to the next field.
*
* This is used to provide "automatic" focus movement
*/
protected void moveFocusForward() {
if (fldHour.hasFocus()) {
fldMin.requestFocus();
} else if (fldMin.hasFocus()) {
cmbTimeOfDay.requestFocus();
}
}
/**
* Moves the focus backwards to the previous field.
*
* This is used to provide "automatic" focus movement
*/
protected void moveFocusBackward() {
if (fldMin.hasFocus()) {
fldHour.requestFocus();
} else if (cmbTimeOfDay.hasFocus()) {
fldMin.requestFocus();
}
}
/**
* Fires the action performed event to all registered listeners
*
* #param evt
*/
protected void fireActionPerformed(ActionEvent evt) {
List<ActionListener> lstListeners = Arrays.asList(listenerList.getListeners(ActionListener.class));
if (!lstListeners.isEmpty()) {
Collections.reverse(lstListeners);
for (ActionListener listener : lstListeners) {
listener.actionPerformed(evt);
}
}
}
/**
* Hour key handler, used to monitor "special" keys for the hour field.
*
* This looks for the user pressing the ":" key and the right arrow key in
* order to perform special navigation
*/
protected class HourKeyHandler extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
boolean numLock = false;
try {
// Get the state of the nums lock
numLock = Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_NUM_LOCK);
} catch (Exception exp) {
}
// Move focus forward if the user presses the ":"
if (e.getKeyCode() == KeyEvent.VK_SEMICOLON && e.isShiftDown()) {
moveFocusForward();
// Move focus forward if the user pressed the left arrow key
} else if ((e.getKeyCode() == KeyEvent.VK_NUMPAD6 && !numLock) || e.getKeyCode() == KeyEvent.VK_RIGHT) {
// If we are in the last edit position
if (fldHour.getCaretPosition() >= 2) {
moveFocusForward();
// Or we are in the first edit position and the field only contains a single character
} else if (fldHour.getText().trim().length() == 1 && fldHour.getCaretPosition() == 1) {
moveFocusForward();
}
}
}
}
/**
* Minute key handler, used to monitor "special" keys for the hour field.
*
* This looks for the user pressing the left arrow key in order to perform
* special navigation
*/
protected class MinuteKeyHandler extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
boolean numLock = false;
try {
numLock = Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_NUM_LOCK);
} catch (Exception exp) {
}
if ((e.getKeyCode() == KeyEvent.VK_NUMPAD4 && !numLock) || e.getKeyCode() == KeyEvent.VK_LEFT) {
// Only want to move backwards if we are at the first edit position
if (fldMin.getCaretPosition() == 0) {
moveFocusBackward();
}
}
}
}
/**
* Hour field focus handler. This watches for focus lost events a
* automatically pads the field with a leading "0" if the field is only 1
* character in length
*/
protected class HourFocusHandler extends FocusAdapter {
#Override
public void focusLost(FocusEvent e) {
String text = fldHour.getText();
if (text.length() < 2) {
text = pad(text, 2);
fldHour.setText(text);
}
}
}
/**
* Minute field focus handler, watches for focus lost events and automatically
* adds a "0" to the end of the field if it is only 1 character in length
*/
protected class MinuteFocusHandler extends FocusAdapter {
#Override
public void focusLost(FocusEvent e) {
String text = fldMin.getText();
if (text.length() < 2) {
fldMin.setText(text + "0");
}
}
}
/**
* The document filter used to filter the hour field.
*/
protected class HourDocumentFilter extends DocumentFilter {
#Override
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
System.out.println("insert: offset = " + offset + "; text = " + text);
super.insertString(fb, offset, text, attr);
}
#Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
try {
boolean isAcceptable = false;
boolean passOnFocus = false;
int strLength = text.length();
// We convert the value here to make sure it's a number...
int value = Integer.parseInt(text);
// If the length of the string been replaced is only 1 character
if (strLength == 1) {
// If we are at the start of the editing position
if (offset == 0) {
// What clock type are we using...
if (!is24HourClock()) {
// only accept 0 or 1...
if (value <= 1) {
isAcceptable = true;
}
} else if (value <= 2) {
isAcceptable = true;
}
// If we are at the second editing position
} else if (offset == 1) {
// Get the preceeding value, should be 0, 1 or 2
String upperPart = fb.getDocument().getText(0, 1);
// Convert the value to an int
int upperValue = Integer.parseInt(upperPart);
// The acceptable range of values for the given position
int lowerRange = 0;
int upperRange = 9;
// Which clock are we using
if (is24HourClock()) {
// If the first value is 2, we can only accept values from 0-3 (20-23)
if (upperValue == 2) {
upperRange = 3;
}
} else {
// 12 hour clock
// If the first value is 1, we can only accept values from 0-2 (10-12)
if (upperValue == 1) {
upperRange = 2;
}
}
// Is the value within accpetable range...
if (value >= lowerRange && value <= upperRange) {
isAcceptable = true;
}
// Pass on focus (only if the value is accepted)
passOnFocus = true;
}
} else {
// First, we need to trim the value down to a maximum of 2 characters
// Need to know at what offest...
// 2 - offset..
// offset == 0, length = 2 - offset = 2
// offset == 1, length = 2 - offset = 1
strLength = 2 - offset;
String timeText = text.substring(offset, strLength);
value = Integer.parseInt(timeText);
// this will only work if we are using a 24 hour clock
if (value >= 0 && value <= 23) {
while (value > 12 && is24HourClock()) {
value -= 12;
}
// Pad out the text if required
text = pad(value, 2);
isAcceptable = true;
}
}
if (isAcceptable) {
super.replace(fb, offset, length, text, attrs);
if (passOnFocus) {
moveFocusForward();
}
}
} catch (NumberFormatException exp) {
}
}
}
/**
* The document filter used to filter the minute field.
*/
protected class MinuteDocumentFilter extends DocumentFilter {
#Override
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
System.out.println("insert: offset = " + offset + "; text = " + text);
super.insertString(fb, offset, text, attr);
}
#Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
try {
boolean isAcceptable = false;
boolean passOnFocus = false;
// How long is the text been added
int strLength = text.length();
// Convert the value to an integer now and save us the hassel
int value = Integer.parseInt(text);
// If the length is only 1, probably a new character has been added
if (strLength == 1) {
// The valid range of values we can accept
int upperRange = 9;
int lowerRange = 0;
if (offset == 0) {
// If we are at the first edit position, we can only accept values
// from 0-5 (50 minutes that is)
upperRange = 5;
} else if (offset == 1) {
// Second edit position...
// Every thing is valid here...
// We want to pass on focus if the clock is in 12 hour mode
passOnFocus = !is24HourClock();
}
// Is the value acceptable..
if (value >= lowerRange && value <= upperRange) {
isAcceptable = true;
}
} else {
// Basically, we are going to trim the value down to at max 2 characters
// Need to know at what offest...
// 2 - offset..
// offset == 0, length = 2 - offset = 2
// offset == 1, length = 2 - offset = 1
strLength = 2 - offset;
String timeText = text.substring(offset, strLength);
value = Integer.parseInt(timeText);
if (value >= 0 && value <= 59) {
// Pad out the value as required
text = pad(value, 2);
isAcceptable = true;
}
}
if (isAcceptable) {
super.replace(fb, offset, length, text, attrs);
if (passOnFocus) {
moveFocusForward();
}
}
} catch (NumberFormatException exp) {
}
}
}
/**
* This is a simple "pass" on action handler...
*/
protected class ActionHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
ActionEvent evt = new ActionEvent(TimeField.this, e.getID(), e.getActionCommand(), e.getModifiers());
fireActionPerformed(evt);
}
}
public static String pad(long lValue, int iMinLength) {
return pad(Long.toString(lValue), 2);
}
public static String pad(int iValue, int iMinLength) {
return pad(Integer.toString(iValue), iMinLength);
}
public static String pad(String sValue, int iMinLength) {
StringBuilder sb = new StringBuilder(iMinLength);
sb.append(sValue);
while (sb.length() < iMinLength) {
sb.insert(0, "0");
}
return sb.toString();
}
}
why should someone chose this bulky implementation over above given DavidKroukamp's simple solution. Also, this has some bugs when manually entering numbers. I prefer DavidKroukamp's way..
Simple answer, validation. JFormattedTextField does not valid the values, but only limits the fact that you can enter digits, you could just as easily enter 99:99 into a JFormattedTextField. The intention of this implementation is to provide real time validation while providing simple data entry requirements
I don't see any reasons for multiplay JSpinner, or JFormattedTextField, this doens't make me any sence, those concepts aren't user-friendly, have look at:
use Java Calendar
use simple JSpinner with SpinnerDateModel
Related
I just learned the the unit testing this week and I'm supposed to find the at least a bug in the following piece of code. I have done some unit testing, but I'm not sure how I can actually find the bug. Every test that I have done has turned out correct so far. If someone can help me by showing me how I'm supposed to find the bug, I would greatly appreciate in advance.
The give code is:
import java.util.Calendar;
public class Time {
private static final int SECONDS_PER_MINUTE = 60;
private static final int MINUTES_PER_HOUR = 60;
private static final int SECONDS_PER_HOUR = 3600;
private static final int HOURS_PER_DAY = 24;
private int hours;
private int minutes;
private int seconds;
/**
* Creates a Time object representing the current time.
*/
public Time()
{
Calendar now = Calendar.getInstance();
hours = now.get(Calendar.HOUR_OF_DAY);
minutes = now.get(Calendar.MINUTE);
seconds = now.get(Calendar.SECOND);
}
/**
* Creates a Time object representing a user-specified time.
*
* Note that the hours component must be specified using the
* 24-hour clock.
*
* #param h hours
* #param m minutes
* #param s seconds
*
*/
public Time(int h, int m, int s)
{
setHours(h);
setMinutes(m);
setSeconds(s);
}
/**
* Creates a Time object representing a user-specified time.
*
* The time is supplied as a string containing two integer values
* representing hours and minutes or three integer values representing
* hours, minutes and seconds, hours being specified using the 24-hour
* clock in either case. The values must be separated from each other by
* a colon.
*
* #param timeString string containing hours, minutes and seconds
*
*/
public Time(String timeString)
{
String[] parts = timeString.split(":");
if (parts.length < 2 || parts.length > 3) {
System.out.println("invalid time ");
}
else{
setHours( Integer.parseInt(parts[0]));
setMinutes( Integer.parseInt(parts[1]));
setSeconds( Integer.parseInt(parts[2]));
}
}
/**
* #return Hours component of this time
*/
public int getHours()
{
return hours;
}
/**
* #return Minutes component of this time
*/
public int getMinutes()
{
return minutes;
}
/**
* #return Seconds component of this time
*/
public int getSeconds()
{
return seconds;
}
/**
* #param hours the hours to set
*/
public void setHours(int hours) {
if (hours < 0 || hours > HOURS_PER_DAY) {
//System.out.println("invalid hours number");
this.hours = 1;
}
else{
this.hours = hours;
}
}
/**
* #param minutes the minutes to set
*/
public void setMinutes(int minutes) {
if (minutes < 0 || minutes >= MINUTES_PER_HOUR){
//System.out.println("invalid minutes number");
this.minutes = 1;
}
else{
this.minutes = minutes;
}
}
/**
* #param seconds the seconds to set
*/
public void setSeconds(int seconds) {
if (seconds < 0 || seconds >= SECONDS_PER_MINUTE) {
//System.out.println("invalid seconds number");
this.seconds = 1;
}
else{
this.seconds = seconds;
}
}
/**
* Formats this time as a string containing hours, minutes and seconds.
*
* Leading zeroes are added to values if necessary and components are
* separated from each other by a colon.
*
* #return String representation of this time
*/
public String getTimeString()
{
return String.format("%02d:%02d:%02d", getHours(), getMinutes(), getSeconds());
}
/**
* Expresses this time as a single integer value - the number of seconds
* since midnight.
*
* #return Number of seconds
*/
public int inSeconds()
{
int hourSec = SECONDS_PER_HOUR * hours;
int minSec = SECONDS_PER_MINUTE * minutes;
return hourSec + minSec + seconds;
}
/**
* Creates a Time object advanced in time relative to this one by
* a user-specified number of seconds.
*
* #param sec number of seconds to advance
* #return String object advanced in time relative to this one
*
*/
public String getTimePlus(int sec)
{
int h = 0;
int m = 0;
int s = 0;
int future = 0;
int remainder = 0;
if (sec < 0) {
System.out.println("invalid number of seconds");
}
else {
future = this.inSeconds() + sec;
h = (future / SECONDS_PER_HOUR) % HOURS_PER_DAY;
remainder = future % SECONDS_PER_HOUR;
m = remainder / SECONDS_PER_MINUTE;
s = remainder % SECONDS_PER_MINUTE;
}
return String.format("%02d:%02d:%02d",h, m, s);
}
}
The test code that I have written is:
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class TestTime {
private Time test;
#BeforeEach
void setUp() throws Exception {
test = new Time(2, 45, 23);
}
#AfterEach
void tearDown() throws Exception {
test = null;
}
#Test
public void constructorHourNegativeNegative() {
Time t = new Time(-1, 35, 40);
assertEquals(1, t.getHours());
}
#Test
public void constructorHourNegativeAbove24() {
Time t = new Time(25, 45, 30);
assertEquals(1, t.getHours());
}
#Test
public void constructorHourBoundary() {
Time t = new Time(0, 45, 30);
assertEquals(0, t.getHours());
}
#Test
public void constructorHourPositive() {
Time t = new Time(12, 45, 30);
assertEquals(12, t.getHours());
}
#Test
public void constructorMinutesNegativeNegative() {
Time t = new Time(11, -1, 30);
assertEquals(1, t.getMinutes());
}
#Test
public void constructorMinutesNegativeEdge() {
Time t = new Time(11, 60, 30);
assertEquals(1, t.getMinutes());
}
#Test
public void constructorMinutesBoundary() {
Time t = new Time(11, 0, 30);
assertEquals(0, t.getMinutes());
}
#Test
public void constructorMinutePositive() {
Time t = new Time(11, 38, 30);
assertEquals(38, t.getMinutes());
}
#Test
public void constructoSecondsNegativeNegative() {
Time t = new Time(11, 24, -1);
assertEquals(1, t.getSeconds());
}
#Test
public void constructoSecondsNegativeEdge() {
Time t = new Time(11, 24, 60);
assertEquals(1, t.getSeconds());
}
#Test
public void constructoSecondsBoundary() {
Time t = new Time(11, 24, 59);
assertEquals(59, t.getSeconds());
}
#Test
public void constructorSecondsPositive() {
Time t = new Time(11, 24, 30);
assertEquals(30, t.getSeconds());
}
}
I think you are helped best when you read a JUnit Tutorial like this one.
In general, you need to test all methods on correct behavior. You can do this like you did it with the constructor.
For example with your getTimePlus(int sec) method:
You need to create a test case where you have a Time-object to call getTimePlus(int sec) and then compare the return value with the expected and "correct" value using the assertEquals({expectedValue}, {returnValue}) method.
When you did this with all Methods then you probably know which method got a bug but you would still need to investigate what went wrong.
I'm creating a four digit class (like a clock) while calling methods from a two digit class.
The four digit class has two segments. When segment one reaches my set maximum value, the second segment must increase by one.
these are my methods from my first class:
/*
* method to set the value
*/
public void setValue(int anyValue){
if((anyValue < TOO_HIGH) && (anyValue >= 0)){
value = anyValue;}
}
/*
* method to set the value plus one
*/
public void setValuePlusOne(){
int tempValue = value + 1;
if(tempValue < TOO_HIGH){
value = tempValue;}
else{
value = 0;
// value = tempValue % TOO_HIGH;}
}
This is from my second four digit class.
/*
* method to set the increment
*/
public void setIncrement(int increment){
rightSegment.setValuePlusOne();
if(increment == 0)
leftSegment.setValuePlusOne();
}
I think there might be something wrong with my increment == 0. It doesn't compile when I try
if(rightsegment.setValuePlusOne()==0)
any advise would help. Thank you!!
setValuePlusOne(...) does not return anything. Call setValuePlusOne before the if and then use (rightsegment.getValue() == 0) for the if.
Please try the code below. Hope the below code will help you to achieve your implementation.
Instead of setting the TOO_HIGH integer value in the if else block of the below given code, you can set it in the RightSegment and LeftSegment classes respectively which is extending the Clock class.
Thanks
package stackoverflow;
public class Clock {
private int value;
private int TOO_HIGH;
private Clock rightSegment;
private Clock leftSegment;
/*
* method to set the value
*/
public void setValue(int anyValue, String position){
if(position.equals("right")){
TOO_HIGH = 60;
}else if(position.equals("left")){
TOO_HIGH = 13;
}
if((anyValue < TOO_HIGH) && (anyValue >= 0)){
value = anyValue;}
}
/*
* method to set the value plus one
*/
public void setValuePlusOne(){
int tempValue = value + 1;
if(tempValue < TOO_HIGH){
value = tempValue;}
else{
value = 0;
}
// value = tempValue % TOO_HIGH;}
}
/*
* method to set the i`ncrement
*/
public void setIncrement(int increment, Clock right, Clock left){
rightSegment = right;
leftSegment = left;
//rightSegment = new Clock();
//leftSegment = new Clock();
rightSegment.setValuePlusOne();
if(increment == 0){
leftSegment.setValuePlusOne();
}
}
public static void main (String args[]){
Clock clock = new Clock();
clock.rightSegment = new Clock();
clock.leftSegment = new Clock();
clock.rightSegment.setValue(12, "right");
clock.leftSegment.setValue(12, "left");
clock.rightSegment.setIncrement(0, clock.rightSegment, clock.leftSegment);
System.out.println("Time "+clock.leftSegment.value+":"+clock.rightSegment.value);
}
}
I'm doing a Java project where I have to make a GUI that contains multiple timers that countdown from a user input time. When I create only one timer, the program works fine, but when I try to include multiple timers, each timer will countdown faster based on how many timers there are. I think the problem is being caused by the ActionListener being called multiple times each time the swing timer calls for an action, but I do not know how to solve that problem.
Here is my CountDownTimer class that contains the attributes and methods for the timer:
package pack1;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Scanner;
/*******************************************************************************
* Class that contains the methods needed to create a timer that counts down
* #version January 2014, Project 1
********************************************************************************/
public class CountDownTimer {
/** Amount of hours that are being counted down in the timer */
int hours;
/** Amount of minutes that are being counted down in the timer */
int minutes;
/** Amount of seconds that are being counted down in the timer */
int seconds;
/** Boolean that determines if the add method will work. It is changed with the toggleSuspend method */
static boolean toggle = false;
/*****************************************************************************************
* Default constructor that creates a CountDownTimer object with no time contained in it
*****************************************************************************************/
public CountDownTimer() {
hours = 0;
minutes = 0;
seconds = 0;
}
/*********************************************************************************************
* Constructor that uses the input amount of hours, minutes, and seconds to count down from
*
* Does not allow time that is negative or allow seconds or minutes that are over 60
* #param hours amount of hours that will be counted down from
* #param minutes amount of minutes that will be counted down from
* #param seconds amount of seconds that will be counted down from
*********************************************************************************************/
public CountDownTimer(int hours, int minutes, int seconds) {
super();
// doesn't allow negative minutes, seconds, or hours
if (seconds < 0 || minutes < 0 || hours < 0) {
throw new IllegalArgumentException("Time cannot be negative");
}
// doesn't allow seconds that are higher than 60
if (seconds >= 60) {
throw new IllegalArgumentException(
"Cannot have more than 60 seconds");
}
// doesn't allow minutes that are higher than 60
if (minutes >= 60) {
throw new IllegalArgumentException(
"Cannot have more than 60 minutes");
}
this.hours = hours;
this.minutes = minutes;
this.seconds = seconds;
}
/*********************************************************************
* Constructor that takes minutes and seconds, and sets hours to zero
* also doesn't allow minutes or seconds to be negative or above 60
* #param minutes amount of minutes that will be counted down from
* #param seconds amount of seconds that will be counted down from
*************************************************************************/
public CountDownTimer(int minutes, int seconds) {
super();
// doesn't allow seconds minutes or hours to be negative
if (seconds < 0 || minutes < 0 || hours < 0) {
throw new IllegalArgumentException("Time cannot be negative");
}
// doesn't allow seconds to be greater than 60
if (seconds >= 60) {
throw new IllegalArgumentException(
"Cannot have more than 60 seconds");
}
// doesn't allow minutes to be greater than 60
if (minutes >= 60) {
throw new IllegalArgumentException(
"Cannot have more than 60 minutes");
}
this.hours = 0;
this.minutes = minutes;
this.seconds = seconds;
}
/***********************************************************************
* Constructor that only takes seconds and sets hours and minutes to 0
* does not allow the seconds to be above 60 or negative
* #param seconds amount of seconds that will be counted down from
*************************************************************************/
public CountDownTimer(int seconds) {
super();
// doesn't allow seconds minutes or hours to be negative
if (seconds < 0 || minutes < 0 || hours < 0) {
throw new IllegalArgumentException("Time cannot be negative");
}
// doesn't allow seconds to be greater than 60
if (seconds >= 60) {
throw new IllegalArgumentException(
"Cannot have more than 60 seconds");
}
this.hours = 0;
this.minutes = 0;
this.seconds = seconds;
}
/**
* Constructor that clones one CountDownTimer object into a new CountDownTimer object
* #param other The CountDownTimer object that is being cloned
* */
public CountDownTimer(CountDownTimer other) {
this.hours = other.hours;
this.minutes = other.minutes;
this.seconds = other.seconds;
}
/*******************************************************************************************************************************
* Constructor that converts a string in the format of "00:00:00" into seconds minutes and hours so it can be counted down from
* #param startTime String that is converted into seconds minutes and hours
*******************************************************************************************************************************/
public CountDownTimer(String startTime) {
// Separates the seconds minutes and hours into an array
String[] parts = startTime.split(":");
// if the array has only one cell, that means only seconds were input
if (parts.length == 1) {
seconds = Integer.parseInt(parts[0]);
}
// if the array has only 2 cells that means there is only minutes and seconds input
if (parts.length == 2) {
minutes = Integer.parseInt(parts[0]);
seconds = Integer.parseInt(parts[1]);
}
// if the array has 3 cells that means there is seconds minutes and hours input
if (parts.length == 3) {
hours = Integer.parseInt(parts[0]);
minutes = Integer.parseInt(parts[1]);
seconds = Integer.parseInt(parts[2]);
}
// doesn't allow seconds minutes or hours to be negative
if (seconds < 0 || minutes < 0 || hours < 0) {
throw new IllegalArgumentException("Time cannot be negative");
}
// doesn't allow seconds to be greater than or equal to 60
if (seconds >= 60) {
throw new IllegalArgumentException(
"Cannot have more than 60 seconds");
}
// doesn't allow minutes to be greater than or equal to 60
if (minutes >= 60) {
throw new IllegalArgumentException(
"Cannot have more than 60 minutes");
}
}
/**************************************************************************************************
* Method that returns true or false based on whether or not two CountDownTimer objects are equal
* #param other Object that is being compared to another CountDownTimer
**************************************************************************************************/
public boolean equals(Object other) {
// converts the two objects to seconds then compares them
if (this.convertToSeconds() == ((CountDownTimer) other)
.convertToSeconds())
return true;
return false;
}
/********************************************************************************
* Returns a boolean based on whether two CountDownTimers, t1 and t2, are equal
* #param t1 first CountDownTimer being compared
* #param t2 second CountDownTimer being compared
********************************************************************************/
public static boolean equals(CountDownTimer t1, CountDownTimer t2) {
// converts the two objects to seconds and then compares them
if (t1.convertToSeconds() == t2.convertToSeconds())
return true;
return false;
}
/************************************************************************************************
* Compares to CountDownTimer objects and returns an int 1, 0, or -1 based on whether the first
* object is greater than, equal to, or less than the CountDownTimer in the parameter
* #param other CountDownTimer that is being compared
*************************************************************************************************/
public int compareTo(CountDownTimer other) {
if (this.convertToSeconds() > other.convertToSeconds())
return 1;
else if (this.convertToSeconds() < other.convertToSeconds())
return -1;
return 0;
}
/**************************************************************************************************
* Compares to CountDownTimer objects and returns an int 1, 0, or -1 based on whether the first
* object (t1) is greater than, equal to, or less than the second object (t2)
* #param t1 first object being compared
* #param t2 second object being compared
* #return
***************************************************************************************************/
public static int compareTo(CountDownTimer t1, CountDownTimer t2) {
if (t1.convertToSeconds() > t2.convertToSeconds())
return 1;
else if (t1.convertToSeconds() < t2.convertToSeconds())
return -1;
return 0;
}
/***************************************************************
* subtracts the input amount of seconds from a CountDownTimer
* #param seconds amount of seconds the user wants to subtract
***************************************************************/
public void subtract(int seconds) {
// places the amount of seconds into an integer
int tempSeconds = this.convertToSeconds();
// subtracts the input seconds from the seconds that were converted
tempSeconds -= seconds;
// converts the new seconds back into the object
formatSeconds(tempSeconds);
}
/*******************************************************************************************
* Subtracts the amount of time contained in one CountDownTimer from another CountDownTimer
* #param other CountDownTimer that is doing the subtracting
*******************************************************************************************/
public void subtract(CountDownTimer other) {
int otherSeconds = other.convertToSeconds();
this.subtract(otherSeconds);
}
/***********************************************************************
* Adds seconds to the object based on what is put into the parameter
* #param seconds amount of seconds being added to the CountDownTimer
************************************************************************/
public void add(int seconds) {
// keeps the method from adding when the toggle is activated
if (toggle == false) {
int tempSeconds = this.convertToSeconds();
tempSeconds += seconds;
formatSeconds(tempSeconds);
} else
throw new IllegalArgumentException(
"Cannot use add when toggle is enabled");
}
/**
* Adds the seconds from one CountDownTimer to another CountDownTimer
* #param other CountDownTimer that is being added to another CountDowntimer
*/
public void add(CountDownTimer other) {
// doesn't allow the method to add when the toggle is true
if (toggle == false) {
int otherSeconds = other.convertToSeconds();
this.add(otherSeconds);
} else
throw new IllegalArgumentException(
"Cannot use add when toggle is enabled");
}
/*******************************************
* Decreases the CountDownTimer by 1 second
*******************************************/
public void dec() {
int tempSeconds = this.convertToSeconds();
tempSeconds--;
formatSeconds(tempSeconds);
}
/****************************************************
* Increases the CountDownTimer object by 1 second
***************************************************/
public void inc() {
int tempSeconds = this.convertToSeconds();
tempSeconds--;
formatSeconds(tempSeconds);
}
/**
* Returns the object as a string in the format of "00:00:00"
*/
public String toString() {
String time = "" + this.hours + ":";
if (this.minutes < 10) {
time += "0" + this.minutes + ":";
} else {
time += this.minutes + ":";
}
if (this.seconds < 10) {
time += "0" + this.seconds;
} else {
time += this.seconds;
}
return time;
}
/************************************************
* Saves the object with a specified name
* #param fileName name of the file being saved
*************************************************/
public void save(String fileName) {
PrintWriter out = null;
try {
out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
} catch (IOException e) {
e.printStackTrace();
}
out.println(this.hours);
out.println(this.minutes);
out.println(this.seconds);
out.close();
}
/**************************************************
* Loads the object with the specified name
* #param fileName Name of the file being loaded
**************************************************/
public void load(String fileName) {
try {
Scanner fileReader = new Scanner(new File(fileName));
this.hours = fileReader.nextInt();
this.minutes = fileReader.nextInt();
this.seconds = fileReader.nextInt();
fileReader.close();
System.out.println("Hours: " + this.hours);
System.out.println("Minutes: " + this.minutes);
System.out.println("Seconds: " + this.seconds);
} catch (FileNotFoundException error) {
System.out.println("File not found");
} catch (IOException error) {
System.out.println("OH NO THAT WAS NOT SUPPOSED TO HAPPEN");
}
}
/**********************************************************************************************
* Switches the toggle boolean, and doesn't allow the add methods to work when it is activated
************************************************************************************************/
public static void toggleSuspend() {
if (toggle == false)
toggle = true;
if (toggle == true)
toggle = false;
}
/***********************************************************************************
* Formats a certain amount of seconds and puts it into an existing CountDownTimer
* #param seconds seconds being formatted
***********************************************************************************/
private void formatSeconds(int seconds) {
this.hours = seconds / 3600;
seconds %= 3600;
this.minutes = seconds / 60;
this.seconds = seconds % 60;
}
/*****************************************************************************
* Returns the amount of seconds that are contained in a CountDownTime object
*****************************************************************************/
private int convertToSeconds() {
int hSeconds = hours * 3600;
int mSeconds = minutes * 60;
return hSeconds + mSeconds + seconds;
}
}
And here is my GUI panel class, where I'm having my problem. I'm using arrays to make multiple timers.
package pack1;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
public class MyTimerPanel extends JPanel {
/** Array of CountDownTimers that will be used */
private CountDownTimer[] cdt;
/** Array of Timer objects that are used to count down by 1 every second*/
private Timer[] javaTimer;
/** Array of TimerListeners that are called in the Timer objects */
private TimerListener timer[];
/** Array of JPanels that will hold each timer interface */
private JPanel[] panel;
/** Array of JLabels for the display of time, seconds, minutes, and hours */
private JLabel[] timeDisplay, secDisplay, minDisplay, hourDisplay;
/** Array of JButtons that start, stop, and set the timer */
private JButton[] start, stop, set;
/** Array of JTextFields that the user will input the seconds, minutes, and hours*/
private JTextField[] sec, min, hour;
/** Array of booleans that will determine if the timer should be counting down */
private boolean[] go;
/** Amount of milliseconds the timer needs to wait before performing an action */
private final int DELAY = 1000;
/** Amount of CountDownTimers in the interfaces */
private final int AMOUNT = 3;
// test integer
int[] count;
/******************************************************************************************
* Constructor that sets the lengths of the arrays and instantiates each part of the array
******************************************************************************************/
public MyTimerPanel() {
count = new int[AMOUNT];
timer = new TimerListener[AMOUNT];
cdt = new CountDownTimer[AMOUNT];
javaTimer = new Timer[AMOUNT];
panel = new JPanel[AMOUNT];
timeDisplay = new JLabel[AMOUNT];
secDisplay = new JLabel[AMOUNT];
minDisplay = new JLabel[AMOUNT];
hourDisplay = new JLabel[AMOUNT];
sec = new JTextField[AMOUNT];
min = new JTextField[AMOUNT];
hour = new JTextField[AMOUNT];
start = new JButton[AMOUNT];
stop = new JButton[AMOUNT];
set = new JButton[AMOUNT];
go = new boolean[AMOUNT];
// Defines each part of each array and adds the neccessary components to the buttons and panels
for (int i = 0; i < AMOUNT; i++) {
count[i] = 0;
cdt[i] = new CountDownTimer(0, 0, 0);
go[i] = false;
timer[i] = new TimerListener();
javaTimer[i] = new Timer(DELAY, timer[i]);
javaTimer[i].start();
timeDisplay[i] = new JLabel(cdt[i].toString());
secDisplay[i] = new JLabel("Seconds: ");
minDisplay[i] = new JLabel("Minutes: ");
hourDisplay[i] = new JLabel("Hours: ");
sec[i] = new JTextField(5);
min[i] = new JTextField(5);
hour[i] = new JTextField(5);
start[i] = new JButton("Start");
stop[i] = new JButton("Stop");
set[i] = new JButton("Set");
start[i].addActionListener(timer[i]);
stop[i].addActionListener(timer[i]);
set[i].addActionListener(timer[i]);
panel[i] = new JPanel();
panel[i].add(hourDisplay[i]);
panel[i].add(hour[i]);
panel[i].add(minDisplay[i]);
panel[i].add(min[i]);
panel[i].add(secDisplay[i]);
panel[i].add(sec[i]);
panel[i].add(start[i]);
panel[i].add(stop[i]);
panel[i].add(set[i]);
panel[i].add(timeDisplay[i]);
add(panel[i]);
}
setPreferredSize(new Dimension(750, 40 * AMOUNT));
}
/*******************************************************************
* Action Listener that activates when certain buttons are pressed
*******************************************************************/
private class TimerListener implements ActionListener {
/********************************************************************
* ActionListener that is activated whenever someone pushes a button
********************************************************************/
public void actionPerformed(ActionEvent e) {
// CountDownTimer that will be compared to at zero
CountDownTimer zero = new CountDownTimer();
// placeholders for the text from the JTextFields
String[] secText = new String[AMOUNT];
String[] minText = new String[AMOUNT];
String[] hourText = new String[AMOUNT];
// runs through each part of the arrays and checks which button needs to be pressed
for (int i = 0; i < AMOUNT; i++) {
// checks if one of the start buttons is being pressed
if (e.getSource() == start[i]){
go[i] = true;
}
// checks if one of the stop buttons is being pressed
else if (e.getSource() == stop[i])
go[i] = false;
// checks if one of the set buttons is being pressed and sets the minutes hours and seconds input
if (e.getSource() == set[i]) {
secText[i] = sec[i].getText();
minText[i] = min[i].getText();
hourText[i] = hour[i].getText();
// if one of the boxes is blank, it will input zero
if (secText[i].equals("")) {
cdt[i].seconds = 0;
} else {
cdt[i].seconds = Integer.parseInt(secText[i]);
}
if (minText[i].equals("")) {
cdt[i].minutes = 0;
} else {
cdt[i].minutes = Integer.parseInt(minText[i]);
}
if (hourText[i].equals("")) {
cdt[i].hours = 0;
} else {
cdt[i].hours = Integer.parseInt(hourText[i]);
}
// sets timeDisplay to show the time in the countdowntimer
timeDisplay[i].setText(cdt[i].toString());
// stops the timer after setting it
go[i] = false;
}
// the timer should be counting down if the go boolean is true or the countdowntimer isn't zero
if (go[i] == true && !cdt[i].equals(zero)) {
javaTimer[i].start();
cdt[i].dec();
timeDisplay[i].setText(cdt[i].toString());
count[i]++;
}
// the timer shouldn't be counting down if the go boolean is false or the countdowntimer is zero
if (go[i] == false || cdt[i].compareTo(zero) <= 0) {
javaTimer[i].stop();
}
}
// for testing purposes
System.out.println(count[0] + "\t" + count[1] + "\t" + count[2]);
}
}
}
The problem is with your TimerListener.
Basically, what's happening, is each time it is called, it executes this code block...
if (go[i] == true && !cdt[i].equals(zero)) {
javaTimer[i].start();
cdt[i].dec();
timeDisplay[i].setText(cdt[i].toString());
count[i]++;
}
This means, if you have 3 counters running, this code block will be executed 9 times per second. Three times per counter/Timer
You TimerListener is too generalised and is trying to do too many things.
Either, create a "single" Timer that ticks on a regular bases and updates each counter as required (so that each loop is dealing with a single counter) OR modify your TimerListener so that it takes details about the counter it is managing and deals ONLY with that counter.
Updated with possible solution
This example uses a single, centralised Timer which simply ticks away in the background. As required, a special Counter is registered with the Timer, which then notifies it when a tick occurs. The Counter then decrements the CountDownTimer and updates the UI.
This is an imperfect example, as I would prefer that the CountDownTimer had a listener interface capable of notifying registered listeners that object's state has changed in some way. This could be achieved through the wrapper Counter class, but I'm to lazy.
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestTimerPane {
public static void main(String[] args) {
new TestTimerPane();
}
public TestTimerPane() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
CountDownManager countDownManager = new CountDownManager();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
frame.add(new TimerPane(countDownManager), gbc);
frame.add(new TimerPane(countDownManager), gbc);
frame.add(new TimerPane(countDownManager), gbc);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class CountDownManager {
private List<Counter> timers;
public CountDownManager() {
timers = new ArrayList<>(25);
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Iterator<Counter> it = timers.iterator();
while (it.hasNext()) {
it.next().tick(CountDownManager.this);
}
}
});
timer.start();
}
public void add(Counter timer) {
timers.add(timer);
}
public void remove(Counter timer) {
timers.remove(timer);
}
}
public class TimerPane extends JPanel {
private JTextField fldHours;
private JTextField fldMins;
private JTextField fldSecs;
private JButton btnStart;
private JButton btnStop;
private JButton btnSet;
private JLabel countDown;
private CountDownTimer countDownTimer;
private Counter counter;
public TimerPane(final CountDownManager countDownManager) {
fldHours = new JTextField(2);
fldMins = new JTextField(2);
fldSecs = new JTextField(2);
btnSet = new JButton("Set");
btnStop = new JButton("Stop");
btnStart = new JButton("Start");
countDown = new JLabel("??:??:??");
add(fldHours);
add(new JLabel(":"));
add(fldMins);
add(new JLabel(":"));
add(fldSecs);
add(btnSet);
add(btnStart);
add(btnStop);
add(countDown);
btnSet.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
countDownTimer = new CountDownTimer(
toInt(fldHours),
toInt(fldMins),
toInt(fldSecs));
counter = new Counter(countDown, countDownTimer);
countDown.setText(countDownTimer.toString());
}
});
btnStart.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (counter != null) {
countDownManager.add(counter);
}
}
});
btnStop.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (counter != null) {
countDownManager.remove(counter);
}
}
});
}
protected int toInt(JTextField field) {
int value = 0;
try {
value = Integer.parseInt(field.getText());
} catch (NumberFormatException exp) {
}
return value;
}
}
public class Counter {
private JLabel output;
private CountDownTimer timer;
public Counter(JLabel output, CountDownTimer timer) {
this.output = output;
this.timer = timer;
}
public void tick(CountDownManager manager) {
timer.dec();
output.setText(timer.toString());
if (timer.convertToSeconds() <= 0) {
manager.remove(this);
}
}
}
public static class CountDownTimer {
/**
* Amount of hours that are being counted down in the timer
*/
int hours;
/**
* Amount of minutes that are being counted down in the timer
*/
int minutes;
/**
* Amount of seconds that are being counted down in the timer
*/
int seconds;
/**
* Boolean that determines if the add method will work. It is changed with
* the toggleSuspend method
*/
static boolean toggle = false;
/**
* ***************************************************************************************
* Default constructor that creates a CountDownTimer object with no time
* contained in it
* ***************************************************************************************
*/
public CountDownTimer() {
hours = 0;
minutes = 0;
seconds = 0;
}
/**
* *******************************************************************************************
* Constructor that uses the input amount of hours, minutes, and seconds to
* count down from
*
* Does not allow time that is negative or allow seconds or minutes that are
* over 60
*
* #param hours amount of hours that will be counted down from
* #param minutes amount of minutes that will be counted down from
* #param seconds amount of seconds that will be counted down from
* *******************************************************************************************
*/
public CountDownTimer(int hours, int minutes, int seconds) {
super();
// doesn't allow negative minutes, seconds, or hours
if (seconds < 0 || minutes < 0 || hours < 0) {
throw new IllegalArgumentException("Time cannot be negative");
}
// doesn't allow seconds that are higher than 60
if (seconds >= 60) {
throw new IllegalArgumentException(
"Cannot have more than 60 seconds");
}
// doesn't allow minutes that are higher than 60
if (minutes >= 60) {
throw new IllegalArgumentException(
"Cannot have more than 60 minutes");
}
this.hours = hours;
this.minutes = minutes;
this.seconds = seconds;
}
/**
* *******************************************************************
* Constructor that takes minutes and seconds, and sets hours to zero also
* doesn't allow minutes or seconds to be negative or above 60
*
* #param minutes amount of minutes that will be counted down from
* #param seconds amount of seconds that will be counted down from
* ***********************************************************************
*/
public CountDownTimer(int minutes, int seconds) {
super();
// doesn't allow seconds minutes or hours to be negative
if (seconds < 0 || minutes < 0 || hours < 0) {
throw new IllegalArgumentException("Time cannot be negative");
}
// doesn't allow seconds to be greater than 60
if (seconds >= 60) {
throw new IllegalArgumentException(
"Cannot have more than 60 seconds");
}
// doesn't allow minutes to be greater than 60
if (minutes >= 60) {
throw new IllegalArgumentException(
"Cannot have more than 60 minutes");
}
this.hours = 0;
this.minutes = minutes;
this.seconds = seconds;
}
/**
* *********************************************************************
* Constructor that only takes seconds and sets hours and minutes to 0 does
* not allow the seconds to be above 60 or negative
*
* #param seconds amount of seconds that will be counted down from
* ***********************************************************************
*/
public CountDownTimer(int seconds) {
super();
// doesn't allow seconds minutes or hours to be negative
if (seconds < 0 || minutes < 0 || hours < 0) {
throw new IllegalArgumentException("Time cannot be negative");
}
// doesn't allow seconds to be greater than 60
if (seconds >= 60) {
throw new IllegalArgumentException(
"Cannot have more than 60 seconds");
}
this.hours = 0;
this.minutes = 0;
this.seconds = seconds;
}
/**
* Constructor that clones one CountDownTimer object into a new
* CountDownTimer object
*
* #param other The CountDownTimer object that is being cloned
*
*/
public CountDownTimer(CountDownTimer other) {
this.hours = other.hours;
this.minutes = other.minutes;
this.seconds = other.seconds;
}
/**
* *****************************************************************************************************************************
* Constructor that converts a string in the format of "00:00:00" into
* seconds minutes and hours so it can be counted down from
*
* #param startTime String that is converted into seconds minutes and hours
* *****************************************************************************************************************************
*/
public CountDownTimer(String startTime) {
// Separates the seconds minutes and hours into an array
String[] parts = startTime.split(":");
// if the array has only one cell, that means only seconds were input
if (parts.length == 1) {
seconds = Integer.parseInt(parts[0]);
}
// if the array has only 2 cells that means there is only minutes and seconds input
if (parts.length == 2) {
minutes = Integer.parseInt(parts[0]);
seconds = Integer.parseInt(parts[1]);
}
// if the array has 3 cells that means there is seconds minutes and hours input
if (parts.length == 3) {
hours = Integer.parseInt(parts[0]);
minutes = Integer.parseInt(parts[1]);
seconds = Integer.parseInt(parts[2]);
}
// doesn't allow seconds minutes or hours to be negative
if (seconds < 0 || minutes < 0 || hours < 0) {
throw new IllegalArgumentException("Time cannot be negative");
}
// doesn't allow seconds to be greater than or equal to 60
if (seconds >= 60) {
throw new IllegalArgumentException(
"Cannot have more than 60 seconds");
}
// doesn't allow minutes to be greater than or equal to 60
if (minutes >= 60) {
throw new IllegalArgumentException(
"Cannot have more than 60 minutes");
}
}
/**
* ************************************************************************************************
* Method that returns true or false based on whether or not two
* CountDownTimer objects are equal
*
* #param other Object that is being compared to another CountDownTimer
* ************************************************************************************************
*/
public boolean equals(Object other) {
// converts the two objects to seconds then compares them
if (this.convertToSeconds() == ((CountDownTimer) other)
.convertToSeconds()) {
return true;
}
return false;
}
/**
* ******************************************************************************
* Returns a boolean based on whether two CountDownTimers, t1 and t2, are
* equal
*
* #param t1 first CountDownTimer being compared
* #param t2 second CountDownTimer being compared
* ******************************************************************************
*/
public static boolean equals(CountDownTimer t1, CountDownTimer t2) {
// converts the two objects to seconds and then compares them
if (t1.convertToSeconds() == t2.convertToSeconds()) {
return true;
}
return false;
}
/**
* **********************************************************************************************
* Compares to CountDownTimer objects and returns an int 1, 0, or -1 based
* on whether the first object is greater than, equal to, or less than the
* CountDownTimer in the parameter
*
* #param other CountDownTimer that is being compared
* ***********************************************************************************************
*/
public int compareTo(CountDownTimer other) {
if (this.convertToSeconds() > other.convertToSeconds()) {
return 1;
} else if (this.convertToSeconds() < other.convertToSeconds()) {
return -1;
}
return 0;
}
/**
* ************************************************************************************************
* Compares to CountDownTimer objects and returns an int 1, 0, or -1 based
* on whether the first object (t1) is greater than, equal to, or less than
* the second object (t2)
*
* #param t1 first object being compared
* #param t2 second object being compared
* #return
* *************************************************************************************************
*/
public static int compareTo(CountDownTimer t1, CountDownTimer t2) {
if (t1.convertToSeconds() > t2.convertToSeconds()) {
return 1;
} else if (t1.convertToSeconds() < t2.convertToSeconds()) {
return -1;
}
return 0;
}
/**
* *************************************************************
* subtracts the input amount of seconds from a CountDownTimer
*
* #param seconds amount of seconds the user wants to subtract
* *************************************************************
*/
public void subtract(int seconds) {
// places the amount of seconds into an integer
int tempSeconds = this.convertToSeconds();
// subtracts the input seconds from the seconds that were converted
tempSeconds -= seconds;
// converts the new seconds back into the object
formatSeconds(tempSeconds);
}
/**
* *****************************************************************************************
* Subtracts the amount of time contained in one CountDownTimer from another
* CountDownTimer
*
* #param other CountDownTimer that is doing the subtracting
* *****************************************************************************************
*/
public void subtract(CountDownTimer other) {
int otherSeconds = other.convertToSeconds();
this.subtract(otherSeconds);
}
/**
* *********************************************************************
* Adds seconds to the object based on what is put into the parameter
*
* #param seconds amount of seconds being added to the CountDownTimer
* **********************************************************************
*/
public void add(int seconds) {
// keeps the method from adding when the toggle is activated
if (toggle == false) {
int tempSeconds = this.convertToSeconds();
tempSeconds += seconds;
formatSeconds(tempSeconds);
} else {
throw new IllegalArgumentException(
"Cannot use add when toggle is enabled");
}
}
/**
* Adds the seconds from one CountDownTimer to another CountDownTimer
*
* #param other CountDownTimer that is being added to another CountDowntimer
*/
public void add(CountDownTimer other) {
// doesn't allow the method to add when the toggle is true
if (toggle == false) {
int otherSeconds = other.convertToSeconds();
this.add(otherSeconds);
} else {
throw new IllegalArgumentException(
"Cannot use add when toggle is enabled");
}
}
/**
* *****************************************
* Decreases the CountDownTimer by 1 second
* *****************************************
*/
public void dec() {
int tempSeconds = this.convertToSeconds();
tempSeconds--;
formatSeconds(tempSeconds);
}
/**
* **************************************************
* Increases the CountDownTimer object by 1 second
* *************************************************
*/
public void inc() {
int tempSeconds = this.convertToSeconds();
tempSeconds--;
formatSeconds(tempSeconds);
}
/**
* Returns the object as a string in the format of "00:00:00"
*/
public String toString() {
String time = "" + this.hours + ":";
if (this.minutes < 10) {
time += "0" + this.minutes + ":";
} else {
time += this.minutes + ":";
}
if (this.seconds < 10) {
time += "0" + this.seconds;
} else {
time += this.seconds;
}
return time;
}
/**
* **********************************************
* Saves the object with a specified name
*
* #param fileName name of the file being saved
* ***********************************************
*/
public void save(String fileName) {
PrintWriter out = null;
try {
out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
} catch (IOException e) {
e.printStackTrace();
}
out.println(this.hours);
out.println(this.minutes);
out.println(this.seconds);
out.close();
}
/**
* ************************************************
* Loads the object with the specified name
*
* #param fileName Name of the file being loaded
* ************************************************
*/
public void load(String fileName) {
try {
Scanner fileReader = new Scanner(new File(fileName));
this.hours = fileReader.nextInt();
this.minutes = fileReader.nextInt();
this.seconds = fileReader.nextInt();
fileReader.close();
System.out.println("Hours: " + this.hours);
System.out.println("Minutes: " + this.minutes);
System.out.println("Seconds: " + this.seconds);
} catch (FileNotFoundException error) {
System.out.println("File not found");
} catch (IOException error) {
System.out.println("OH NO THAT WAS NOT SUPPOSED TO HAPPEN");
}
}
/**
* ********************************************************************************************
* Switches the toggle boolean, and doesn't allow the add methods to work
* when it is activated
* **********************************************************************************************
*/
public static void toggleSuspend() {
if (toggle == false) {
toggle = true;
}
if (toggle == true) {
toggle = false;
}
}
/**
* *********************************************************************************
* Formats a certain amount of seconds and puts it into an existing
* CountDownTimer
*
* #param seconds seconds being formatted
* *********************************************************************************
*/
private void formatSeconds(int seconds) {
this.hours = seconds / 3600;
seconds %= 3600;
this.minutes = seconds / 60;
this.seconds = seconds % 60;
}
/**
* ***************************************************************************
* Returns the amount of seconds that are contained in a CountDownTime
* object
* ***************************************************************************
*/
private int convertToSeconds() {
int hSeconds = hours * 3600;
int mSeconds = minutes * 60;
return hSeconds + mSeconds + seconds;
}
}
}
Now, if you need a separate Timer per CountDownTimer, then I would probably create some kind of "worker" class that would take a CountDownTimer and that had a self contained Timer. I would also provide some kind of listener that interested parties could register to and would allow them to update the UI as they see fit...
In the CalendarCanvas class, the idea is to press on any day in the Calendar and a form will be presented with the actual date of the day THE USER picked (not just today's date as it's doing now) and underneath the form will be the image pertaining to the form (eg. June 21st (image.jpg)).
I'm having two problems.
Number one, the program only likes one image which is the Men.img. It will display that image in all of the three forms. The other two images will not display at all.
And I don't understand because I first started with one form and the images.jpg image and that image worked. All I did was create 2 new forms and switch the order of images.
I even reduced the sizes (width/height & KB) to match with Men.jpg but it still doesn't show. The string inside the ImageItem constructor doesn't even show.
Lastly how do I create a new instance of the calendar.getSelectedDate().toString() method to show the new user picked date so I can put that above the image when the user picks a different day?
In the keyPressed method, the commented out new instance of Alert will do that for me but I don't want an alert, I want it to be the title of the forms pertaining to what day the user picked.
Below is my code:
Class CalendarMidlet:
import java.io.IOException;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
/**
* #author addylesson
*/
public class CalendarMidlet extends MIDlet {
private Display mydisplay;
private Command f;
private Displayable d;
public CalendarMidlet(){}
public void startApp() {
d = new CalendarCanvas(this);
f = new Command("Exit", Command.EXIT, 0);
d.addCommand(f);
d.setCommandListener(new CommandListener()
{
public void commandAction(Command c, Displayable s)
{
if(c == f)
notifyDestroyed();
}
} );
mydisplay = Display.getDisplay(this);
mydisplay.setCurrent(d);
}
public void startOver() {
final Displayable d = new CalendarCanvas(this);
d.addCommand(f);
d.setCommandListener(new CommandListener()
{
public void commandAction(Command c, Displayable s)
{
if(c == f)
notifyDestroyed();
}
} );
mydisplay.setCurrent(d);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
}
Class CalendarCanvas:
import java.io.IOException;
import java.util.Date;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
public class CalendarCanvas extends Canvas
{
CalendarWidget calendar = null;
CalendarMidlet midlet = null;
private Alert alert;
private List mList;
private Command f,e,n;
private Display mydisplay;
private Form form;
private Form form2;
private Form form3;
private ImageItem imageItem;
private ImageItem imageItem2;
private ImageItem imageItem3;
public CalendarCanvas(final CalendarMidlet m)
{
String day = calendar.getSelectedDate().toString();
this.midlet = m;
calendar = new CalendarWidget(new Date());
calendar.headerFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_LARGE);
calendar.weekdayFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_MEDIUM);
calendar.weekdayBgColor = 0xccccff;
calendar.weekdayColor = 0x0000ff;
calendar.headerColor = 0xffffff;
form = new Form(day);
form2 = new Form("New Page");
form3 = new Form("New Page");
try
{
imageItem = new ImageItem(
"Men In Black 3: ",
Image.createImage("Men.jpg"),
ImageItem.LAYOUT_DEFAULT,
"DuKe");
form.append(imageItem);
} catch(Exception s) {}
try
{
imageItem2 = new ImageItem(
"Jordon vs Knicks: ",
Image.createImage("images.jpg"),
ImageItem.LAYOUT_DEFAULT,
"Not");
form2.append(imageItem2);
} catch(Exception s) {}
try
{
imageItem3 = new ImageItem(
"Horoscope: ",
Image.createImage("Men.jpg"),
ImageItem.LAYOUT_DEFAULT,
"DuKe");
form3.append(imageItem3);
} catch(Exception s) {}
alert = new Alert("Listen", "On this day "
+calendar.getSelectedDate().toString()+ "stuff happened", null, null);
alert.setTimeout(Alert.FOREVER);
n = new Command("Next", Command.SCREEN,0);
f = new Command("Back", Command.BACK, 0);
e = new Command("Exit", Command.EXIT, 1);
form.addCommand(f);
form.addCommand(n);
form2.addCommand(f);
form2.addCommand(n);
form3.addCommand(f);
form3.addCommand(e);
form.setCommandListener(new CommandListener()
{
public void commandAction(Command c, Displayable s)
{
if (c == f)
m.startOver();
if (c == e)
m.notifyDestroyed();
if (c == n)
Display.getDisplay(midlet).setCurrent(form2);
}
} );
form2.setCommandListener(new CommandListener()
{
public void commandAction(Command c, Displayable s)
{
if (c == f)
m.startOver();
if (c == e)
m.notifyDestroyed();
if (c == n)
Display.getDisplay(midlet).setCurrent(form3);
}
} );
form3.setCommandListener(new CommandListener()
{
public void commandAction(Command c, Displayable s)
{
if (c == f)
m.startOver();
if (c == e)
m.notifyDestroyed();
}
} );
calendar.initialize();
}
protected void keyPressed(int key)
{
int keyCode = getGameAction(key);
String day = calendar.getSelectedDate().toString();
if(keyCode == FIRE)
{
/*Display.getDisplay(midlet).setCurrent(
new Alert("Selected date",
day, null,
AlertType.CONFIRMATION)
);*/
Display.getDisplay(midlet).setCurrent(form);
}
else
{
calendar.keyPressed(keyCode);
repaint();
}
}
protected void paint(Graphics g)
{
g.setColor(0xff0000);
g.fillRect(0, 0, getWidth(), getHeight());
calendar.paint(g);
}
}
Class CalendarWidget:
import java.util.Calendar;
import java.util.Date;
import javax.microedition.lcdui.*;
public class CalendarWidget
{
static final String[] MONTH_LABELS = new String[]{
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
};
static final String[] WEEKDAY_LABELS = new String[]{
"M", "T", "W", "T", "F", "S", "S"
};
/* starting week day: 0 for monday, 6 for sunday */
public int startWeekday = 0;
/* elements padding */
public int padding = 2;
/* cells border properties */
public int borderWidth = 4;
public int borderColor = 0x000000;
/* weekday labels properties */
public Font weekdayFont = Font.getDefaultFont();
public int weekdayBgColor = 0xcccccc;
public int weekdayColor = 0xffffff;
/* header (month-year label) properties */
public Font headerFont = Font.getDefaultFont();
public int headerBgColor = 0xff0000;
public int headerColor = 0xff0000;
/* cells properties */
public Font font = Font.getDefaultFont();
public int foreColor = 0xffffff;
public int bgColor = 0xcccccc;
public int selectedBgColor = 0x000000;
public int selectedForeColor = 0xff0000;
/* internal properties */
int width = 0;
int height = 0;
int headerHeight = 0;
int weekHeight = 0;
int cellWidth = 0;
int cellHeight = 0;
/* internal time properties */
long currentTimestamp = 0;
Calendar calendar = null;
int weeks = 0;
public CalendarWidget(Date date)
{
calendar = Calendar.getInstance();
//we'll see these 2 methods later
setDate(date);
initialize();
}
public Date getSelectedDate()
{
return calendar.getTime();
}
public void setDate(Date d)
{
currentTimestamp = d.getTime();
calendar.setTime(d);
//weeks number can change, depending on week starting day and month total days
this.weeks = (int)Math.ceil(((double)getStartWeekday() + getMonthDays()) / 7);
}
public void setDate(long timestamp)
{
setDate(new Date(timestamp));
}
void initialize()
{
//let's initialize calendar size
this.cellWidth = font.stringWidth("MM") + 3 * padding;
this.cellHeight = font.getHeight() + 15 * padding;
this.headerHeight = headerFont.getHeight() + 2 * padding;
this.weekHeight = weekdayFont.getHeight() + 2 * padding;
this.width = 7 * (cellWidth + borderWidth) + borderWidth;
initHeight();
}
void initHeight()
{
this.height =
headerHeight + weekHeight +
this.weeks * (cellHeight + borderWidth) + borderWidth;
}
int getMonthDays()
{
int month = calendar.get(Calendar.MONTH);
switch(month)
{
case 3:
case 5:
case 8:
case 10:
return 30;
case 1:
return calendar.get(Calendar.YEAR) % 4 == 0 && calendar.get(Calendar.YEAR) % 100 != 0 ? 29 : 28;
default:
return 31;
}
}
int getStartWeekday()
{
//let's create a new calendar with same month and year, but with day 1
Calendar c = Calendar.getInstance();
c.set(Calendar.MONTH, calendar.get(Calendar.MONTH));
c.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
c.set(Calendar.DAY_OF_MONTH, 1);
//we must normalize DAY_OF_WEEK returned value
return (c.get(Calendar.DAY_OF_WEEK) + 5) % 7;
}
public void keyPressed(int key)
{
switch(key)
{
case Canvas.UP:
go(-7);
break;
case Canvas.DOWN:
go(7);
break;
case Canvas.RIGHT:
go(1);
break;
case Canvas.LEFT:
go(-1);
break;
}
}
void go(int delta)
{
int prevMonth = calendar.get(Calendar.MONTH);
setDate(currentTimestamp + 86400000 * delta);
//we have to check if month has changed
//if yes, we have to recalculate month height
//since weeks number could be changed
if(calendar.get(Calendar.MONTH) != prevMonth)
{
initHeight();
}
}
public void paint(Graphics g)
{
//painting background
g.setColor(bgColor);
g.fillRect(0, 0, width, height);
//painting header (month-year label)
g.setFont(headerFont);
g.setColor(headerColor);
g.drawString(MONTH_LABELS[calendar.get(Calendar.MONTH)] + " " + calendar.get(Calendar.YEAR), width / 2, padding, Graphics.TOP | Graphics.HCENTER);
//painting week days labels
g.translate(0, headerHeight);
g.setColor(weekdayBgColor);
g.fillRect(0, 0, width, weekHeight);
g.setColor(weekdayColor);
g.setFont(weekdayFont);
for(int i = 0; i < 7; i++)
{
g.drawString(WEEKDAY_LABELS[(i + startWeekday) % 7],
borderWidth + i * (cellWidth + borderWidth) + cellWidth / 2,
padding,
Graphics.TOP | Graphics.HCENTER
);
}
g.translate(0, weekHeight);
g.setColor(borderColor);
for(int i = 0; i <= weeks; i++)
{
g.fillRect(0, i * (cellHeight + borderWidth), width, borderWidth);
}
for(int i = 0; i <= 7; i++)
{
g.fillRect(i * (cellWidth + borderWidth), 0, borderWidth, height - headerHeight - weekHeight);
}
int days = getMonthDays();
int dayIndex = (getStartWeekday() - this.startWeekday + 7) % 7;
g.setColor(foreColor);
int currentDay = calendar.get(Calendar.DAY_OF_MONTH);
for(int i = 0; i < days; i++)
{
int weekday = (dayIndex + i) % 7;
int row = (dayIndex + i) / 7;
int x = borderWidth + weekday * (cellWidth + borderWidth) + cellWidth / 2;
int y = borderWidth + row * (cellHeight + borderWidth) + padding;
if(i + 1 == currentDay)
{
g.setColor(selectedBgColor);
g.fillRect(
borderWidth + weekday * (cellWidth + borderWidth),
borderWidth + row * (cellHeight + borderWidth),
cellWidth, cellHeight);
g.setColor(selectedForeColor);
}
g.drawString("" + (i + 1), x, y, Graphics.TOP | Graphics.HCENTER);
if(i + 1 == currentDay)
{
g.setColor(foreColor);
}
}
g.translate(0, - headerHeight - weekHeight);
}
}
There are no evident mistakes in the code snippet you posted.
To easier find out what could possibly go wrong when your MIDlet becomes complicated like that, add logging statements and run it in emulator watching console output.
You definitely need to add logging within catch blocks. To find out why I am making such a strong statement, search Web for something like Java swallow exceptions.
Other points where I'd recommend logging are within commandAction and when invoking setCurrent. When developing, feel free to add more where you feel the need to (keyPressed looks like a good candidate for that, too).
public class Log {
// utility class to keep logging code in one place
public static void log (String message) {
System.out.println(message);
// when debugging at real device, S.o.p above can be refactored
// - based on ideas like one used here (with Form.append):
// http://stackoverflow.com/questions/10649974
// - Another option would be to write log to RMS
// and use dedicated MIDlet to read it from there
// - If MIDlet has network connection, an option is
// to pass log messages over the network. Etc etc...
}
}
// ... other classes...
// ...
catch (Exception e) {
Log.log("unexpected exception: [" + e + "]");
}
// ...
public void commandAction(Command c, Displayable s) {
Log.log("command: [" + c.getCommandLabel()
+ "] at screen: [" + d.getTitle() + "]");
// ...
}
// ...
Log.log("set current: [" + someDisplayable.getTitle() + "]");
mydisplay.setCurrent(someDisplayable);
// ...
protected void keyPressed(int key) {
Log.log("key pressed: [" + getKeyName(key) + "]");
// ...
}
I have two textfield in my swing component. In one text field i need to have only numbers
(no string,empty spaces,special charaters allowed) and in another textfield i need to have only string(no numbers,empty spaces,special charaters allowed). How can i implement that..???
You can use the Pattern Class (Regular Expressions) to validate the input. A short tutorial is available here.
I am pretty sure that the basic tutorial covers all this...
"^//d+$" //The text must have at least one digit, no other characters are allowed
"^[a-zA-Z]+$" //The text must have at least one letter, no other characters are allowed
You have two choices, you can validate the text in the fields either 1) on entry or 2) when the user performs an action such as clicks a confirmation button.
For 2) npinti's answer should steer you in the right direction, just get the value of the field and validate it with a regular expression.
For 1) you might want to write a KeyListener that intercepts key presses and only allows the correct type of character for the field.
You can extend javax.swing.text.PlainDocument class, and call setDocument method textfield. Here is one of the example ;
package textfield;
import java.awt.Toolkit;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
public class LimitedValuePositiveIntegerDocument extends PlainDocument {
int maxValue;
int maxLength;
Toolkit toolkit;
/**
* Constructor for the class.
* #param max maximum value of the number
*/
public LimitedValuePositiveIntegerDocument(int max){
maxValue = max;
maxLength = (""+max).length();
toolkit = Toolkit.getDefaultToolkit();
}
/**
* Inserts the input string to the current string after validation.
* #param offs offset of the place where the input string is supposed to be inserted.
* #param str input string to be inserted
* #param a attribute set
*/
#Override
public void insertString(int offs, String str, AttributeSet a)
throws BadLocationException {
if(str == null)return;
String currentText = getText(0,getLength());
String resultText = new String();
int i;
boolean errorFound = false;
boolean deleteFirstZero = false;
int accepted=0;
for (i = 0; (i<str.length())&&(!errorFound); i++) {
if (Character.isDigit(str.charAt(i))) { /* if it is digit */
if (offs==currentText.length()) { /* calculates the resultant string*/
resultText = currentText+str.substring(0,i+1);
} else if (offs==0) {
resultText = str.substring(0,i+1)+currentText;
} else {
resultText = currentText.substring(0, offs)+str.substring(0,i+1)+currentText.substring(offs,currentText.length());
}
if (Integer.parseInt(resultText) > maxValue) {
errorFound = true;
toolkit.beep();
} else {
if ( resultText.length() == maxLength+1) {
deleteFirstZero = true;
}
accepted++;
}
} else {
errorFound = true;
toolkit.beep();
}
}
if ( accepted>0 ) { /* insert string */
super.insertString(offs, str.substring(0,accepted), a);
if (deleteFirstZero) {
super.remove(0,1);
}
}
}
/**
* Removes a part of the current string.
* #param offs offset of the place to be removed.
* #param len length to be removed
*/
#Override
public void remove(int offs, int len) throws BadLocationException{
super.remove(offs, len);
}
/**
* Returns max value of the number.
* #return max value
*/
public int getMaxValue() {
return maxValue;
}
/**
* Sets max value of the number.
* #param max maximum value of the number
*/
public void setMaxValue(int max) {
this.maxValue = max;
}
} // end of class
EDIT :
and its usage;
LimitedValuePositiveIntegerDocument doc = new LimitedValuePositiveIntegerDocument(999);
JTextField numberField = new JtextField();
numberField.setDocument(doc);
You can only enter positive numbers less than 1000, and it check while you are pressing the key..