I am trying to create a digital clock an am currently trying to fix my incrementMinutesBy method.
I need this method to increase both the minutes and if necessary the hours. I need the final this.minutes to be the sum of the previous this.minutes and the parameter, modulo the number of minutes in an hour.
I also need the final this.hours to be the sum of the previous this.minutes and the parameter, divided by the number of minutes in an hour. My method so far begins on line 94.
public class DigitalClock
{
private int currentHour;
private int currentMinutes;
public int getHour()
{
return currentHour;
}
public void setHour(int currentHour)
{
this.currentHour = currentHour;
}
public int getMinutes()
{
return currentMinutes;
}
public void setMinutes(int currentMinutes)
{
this.currentMinutes = currentMinutes;
}
public static final int HOUR_MAX = 23; // Refactored hourly max
public static final int HOUR_MIN = 0; // Refactored hourly min
public static final int MINUTES_MAX = 59; // Refactored minute max
public static final int MINUTES_MIN = 0; // Refactored minute min
public static final int TOTAL_NUMBERS_HOURS = 24;
public static final int TOTAL_NUMBERS_MINUTES = 60;
/**
* Creates a new digital clock with the time set at the given
* hours and minutes.
*
* #precondition 0 <= hour <= 23 AND 0 <= minutes <= 59
* #postcondition getHour()==hour AND getMinutes()==minutes
*
* #param hour the hour to set for the time
* #param minutes the minutes to set for the time
*/
public DigitalClock (int hour, int minutes) // 2-parameter constructor (parameters: int hour and int minutes)
{
// enforcing hourly preconditions using appropriate ranges
if (hour >= HOUR_MIN && hour <= HOUR_MAX){
currentHour = hour;
}
else {
currentHour = 0;
//throw an exception on invalid hour's input
throw new IllegalArgumentException("Invalid input for Hours");
}
// enforcing minute preconditions using appropriate ranges
if (minutes >= MINUTES_MIN && minutes <= MINUTES_MAX) {
currentMinutes = minutes;
}
else {
currentMinutes = 0;
//throw an exception on invalid minute's input
throw new IllegalArgumentException("Invalid input for Minutes");
}
}
/**
* Advances the entire clock by the given number of hours.
* If the hours advances past 23 they wrap around.
*
* #precondition hours >= 0
* #postcondition the clock has moved forward
* by the appropriate number of hours
*
* #param hours the number of hours to add
*/
public void incrementHoursBy(int hour)
{
int h = getHour()+hour; {
// if statement enforcing precondition
if(h>HOUR_MAX)
h%=TOTAL_NUMBERS_HOURS;
// increasing this.hours by the parameter
setHour(h);}
// if statement which prevents negative hours from being inputted
if (h<HOUR_MIN) {
throw new IllegalArgumentException("Invalid input. No negative hours");
}
}
public void incrementMinutesBy(int minutes) {
int m = getMinutes()+minutes;
m++;
if (m > MINUTES_MAX) {
m%=TOTAL_NUMBERS_MINUTES;
setMinutes(m);
}
if (m<MINUTES_MIN) {
throw new IllegalArgumentException("Invalid input. No negative minutes");
}
else
{
System.out.println("Minutes are: "+m);
}
}
public static void main(String args[])
{
DigitalClock obj=new DigitalClock(12, 15);
obj.incrementHoursBy(HOUR_MAX);
obj.incrementMinutesBy(MINUTES_MAX);
DigitalClockFormatter();
}
public static void DigitalClockFormatter() {
// method for class DigitalClockFormatter
}
}
To simplify the implementation, it's beter to save totalMinutes internaly, but not the separate hours and minutes.
public final class DigitalClock {
private static final int MINUTES_PER_HOUR = (int)TimeUnit.HOURS.toMinutes(1);
private static final int MINUTES_PER_DAY = (int)TimeUnit.DAYS.toMinutes(1);
private int totalMinutes;
public DigitalClock(int hour, int minutes) {
setHour(hour);
setMinutes(minutes);
}
public int getHour() {
return totalMinutes / MINUTES_PER_HOUR;
}
public int getMinutes() {
return totalMinutes % MINUTES_PER_HOUR;
}
public void setHour(int hour) {
if (hour < 0 || hour > 23)
throw new IllegalArgumentException("'hour' should be within [0;23]");
totalMinutes = (int)TimeUnit.HOURS.toMinutes(hour) + getMinutes();
}
public void setMinutes(int minutes) {
if (minutes < 0 || minutes > 59)
throw new IllegalArgumentException("'minutes' should be within [0;59]");
totalMinutes = (int)TimeUnit.HOURS.toMinutes(getHour()) + minutes;
}
public void incrementHoursBy(int hour) {
totalMinutes = withinDay((int)TimeUnit.HOURS.toMinutes(hour) + totalMinutes);
}
public void incrementMinutesBy(int minutes) {
totalMinutes = withinDay(totalMinutes + minutes);
}
private static int withinDay(int totalMinutes) {
totalMinutes %= MINUTES_PER_DAY;
if (totalMinutes < 0)
totalMinutes = MINUTES_PER_DAY + totalMinutes;
return totalMinutes;
}
#Override
public String toString() {
return String.format("%02d:%02d", getHour(), getMinutes());
}
}
I believe that your calculations can also be achieved by subtraction. Consider the following example.
If currentHour is 23 and you add two hours, then the result should be 1 (one).
23 + 2 = 25
25 % 24 = 1
25 - 24 = 1
Similarly for currentMinutes. However, for currentMinutes you also need to increment currentHour if the result of the increment (to currentMinutes) is greater than MINUTES_MAX. And when you increment currentHour you also need to check whether the incremented currentHour value is greater than HOUR_MAX. In other words, if currentHour is 23 and currentMinutes is 55 and you incrementMinutesBy(10), then:
55 + 10 = 65
65 - 60 = 5
Therefore currentMinutes is set to 5 and currentHour needs to be incremented. Therefore:
23 + 1 = 24
24 - 24 = 0
So currentHour needs to be set to 0 (zero).
Also, as explained in the book Java by Comparison, methods need to fail fast so the first thing you should do in both method incrementHoursBy and method incrementMInutesBy is to check the value of the method argument – and not the result of the calculation. After all, you wrote in the code comments that the pre-condition is that the method argument not be negative – even though I think it would be logical to be able to set the digital clock backwards. I mean that's what you do when daylight saving ends and the clocks get turned back one hour. But then again, with your [digital] clock, you could achieve the same result by invoking incrementHoursBy(HOUR_MAX)
Consider the following code:
public class DigitalClock {
private int currentHour;
private int currentMinutes;
public int getHour() {
return currentHour;
}
public void setHour(int currentHour) {
this.currentHour = currentHour;
}
public int getMinutes() {
return currentMinutes;
}
public void setMinutes(int currentMinutes) {
this.currentMinutes = currentMinutes;
}
public static final int HOUR_MAX = 23; // Refactored hourly max
public static final int HOUR_MIN = 0; // Refactored hourly min
public static final int MINUTES_MAX = 59; // Refactored minute max
public static final int MINUTES_MIN = 0; // Refactored minute min
public static final int TOTAL_NUMBERS_HOURS = 24;
public static final int TOTAL_NUMBERS_MINUTES = 60;
/**
* Creates a new digital clock with the time set at the given hours and minutes.
*
* #precondition 0 <= hour <= 23 AND 0 <= minutes <= 59
* #postcondition getHour()==hour AND getMinutes()==minutes
*
* #param hour the hour to set for the time
* #param minutes the minutes to set for the time
*/
public DigitalClock(int hour, int minutes) {
// enforcing hourly preconditions using appropriate ranges
if (hour >= HOUR_MIN && hour <= HOUR_MAX) {
currentHour = hour;
}
else {
currentHour = 0;
// throw an exception on invalid hour's input
throw new IllegalArgumentException("Invalid input for Hours");
}
// enforcing minute preconditions using appropriate ranges
if (minutes >= MINUTES_MIN && minutes <= MINUTES_MAX) {
currentMinutes = minutes;
}
else {
currentMinutes = 0;
// throw an exception on invalid minute's input
throw new IllegalArgumentException("Invalid input for Minutes");
}
}
/**
* Advances the entire clock by the given number of hours. If the hours advances
* past 23 they wrap around.
*
* #precondition hours >= 0
* #postcondition the clock has moved forward by the appropriate number of hours
*
* #param hours the number of hours to add
*/
public void incrementHoursBy(int hour) {
// if statement which prevents negative hours from being inputted
if (hour < HOUR_MIN || hour > HOUR_MAX) {
throw new IllegalArgumentException("Invalid input. Between 0 & 23");
}
int h = currentHour + hour;
// if statement enforcing precondition
if (h > HOUR_MAX) {
h -= TOTAL_NUMBERS_HOURS;
}
// increasing this.hours by the parameter
setHour(h);
}
public void incrementMinutesBy(int minutes) {
if (minutes < MINUTES_MIN || minutes > MINUTES_MAX) {
throw new IllegalArgumentException("Invalid input. Between 0 & 59");
}
int m = getMinutes() + minutes;
if (m > MINUTES_MAX) {
incrementHoursBy(1);
m -= TOTAL_NUMBERS_MINUTES;
setMinutes(m);
}
}
public static void main(String args[]) {
DigitalClock obj = new DigitalClock(22, 55);
obj.incrementHoursBy(1);
obj.incrementMinutesBy(10);
DigitalClockFormatter(obj);
}
public static void DigitalClockFormatter(DigitalClock dc) {
System.out.printf("%02d:%02d", dc.getHour(), dc.getMinutes());
}
}
Running above code displays:
00:05
Alternatively, you could use the date-time API.
import java.time.LocalTime;
public class DigiClok {
private LocalTime clock;
public DigiClok(int hour, int minute) {
clock = LocalTime.of(hour, minute); // throws java.time.DateTimeException if invalid arguments
}
public int getHour() {
return clock.getHour();
}
public void setHour(int currentHour) {
// class 'java.time.LocalTime' is immutable.
int minute = clock.getMinute();
clock = LocalTime.of(currentHour, minute);
}
public int getMinutes() {
return clock.getMinute();
}
public void setMinutes(int currentMinutes) {
int hour = clock.getHour();
clock = LocalTime.of(hour, currentMinutes);
}
public boolean equals(Object obj) {
boolean equal = this == obj;
if (!equal) {
if (obj instanceof DigiClok) {
DigiClok other = (DigiClok) obj;
equal = clock.equals(other.clock);
}
}
return equal;
}
public int hashCode() {
return clock.hashCode();
}
public String toString() {
return clock.toString();
}
public void incrementHoursBy(int hour) {
clock = clock.plusHours(hour); // also handles negative 'hour'
}
public void incrementMinutesBy(int minutes) {
clock = clock.plusMinutes(minutes); // also handles negative 'minute'
}
public static void main(String[] args) {
DigiClok obj = new DigiClok(22, 55);
obj.incrementHoursBy(1);
obj.incrementMinutesBy(10);
System.out.println(obj);
}
}
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 have project in java.
Create CTime class with the following specifications
Attributes: Hour, minute and second
Hour >=0 and <= 23, minute >=0 and <=59 , second >=0 and <= 59
Methods
Constructor that updates the CTime attributes
set and get methods for each attribute
tick method that add 1 second to the CTime object and returns nothing
toString method that creates time string with the following format HH:mm:ss e.g. 22:15:01
and I did this
public class CTime {
int hour;
int min;
int sec;
public CTime(int h, int m, int s) {
hour = h;
min = m;
sec = s;
}
public void setHour(int h) {
hour = h;
}
public int getHour() {
return hour;
}
public void setMin(int m) {
min = m;
}
public int getMin() {
return min;
}
public void setSec(int s) {
sec = s;
}
public int getSec() {
return sec;
}
public void tick() {
sec++;
if (sec >= 59) {
sec = 0;
min++;
if (min >= 59) {
min = 0;
}
hour++;
}
}
public String toString() {
String s = hour + ":" + min + ":" + sec;
return s;
}
}
How to make my hour and min and sec with 2 digit, e.g. 05:02:09?
And my code is it correct or not?
If you insist on doing this kind of stuff with Strings the way you are, you could do something like the following:
public String toString() {
String s = (hour > 9 ? hour : "0" + hour) + ":" + (min > 9 ? min : "0" + min) + ":" + (sec > 9 ? sec : "0" + sec);
return s;
}
Just use String.format()
public String toString() {
String s = String.format("%02d:%02d:%02d", hour, min, sec);
return s;
}
One more link here
And your code, yes it is correct. To make it a live clock, all you have to do is call tick() method every second. Take a look at this SO post to learn how to do that.
Under the comment version 4, i am trying to create a method named equals that will test the hours, minutes, and seconds. The formal parameter is used again in the return statement. I know i should have it in the ______.hours format, hours being the instance variable used to test and produce the true or false, but i don't know what should go before the period as the formal parameter. Any suggestions/explanations would be appreciated greatly.
public class Clock
{
private static final byte DEFAULT_HOUR = 0,
DEFAULT_MIN = 0,
DEFAULT_SEC = 0,
MAX_HOURS = 24,
MAX_MINUTES = 60,
MAX_SECONDS = 60;
// ------------------
// Instance variables
// ------------------
private byte seconds,
minutes,
hours;
public Clock (byte hours , byte minutes , byte seconds )
{
setTime(hours, minutes, seconds);
}
public Clock ( )
{
setTime(DEFAULT_HOUR, DEFAULT_MIN, DEFAULT_SEC);
}
public void setTime ( byte hours, byte minutes, byte seconds )
{
this.hours = hours;
this.minutes = minutes;
this.seconds = seconds;
// hours
if (DEFAULT_HOUR >= 0 && DEFAULT_HOUR <= 29)
{
}
else
{
hours = DEFAULT_HOUR;
}
// minutes
if (DEFAULT_MIN >= 0 && DEFAULT_MIN <= 59)
{
}
else
{
minutes = DEFAULT_MIN;
}
// seconds
if (DEFAULT_SEC >= 0 && DEFAULT_SEC <= 59)
{
}
else
{
seconds = DEFAULT_SEC;
}
}
//--------------------------
// Version 3 mutator methods
//--------------------------
public void incrementSeconds()
{
seconds += 1;
if (seconds >= 59)
{
seconds = DEFAULT_SEC;
incrementMinutes();
}
}
public void incrementMinutes()
{
minutes += 1;
if (minutes >= 59)
{
minutes = DEFAULT_MIN;
incrementHours();
}
}
public void incrementHours()
{
hours += 1;
if (hours >= 23)
{
hours = DEFAULT_HOUR;
}
}
//----------
// Version 4
//----------
public boolean equals(Clock your_clock)
{
return boolean your_clock.hours;
}
//----------
// Version 2
//----------
public String toString()
{
final byte MIN_2DIGITS = 10;
String str = "";
// my input
if (hours < MIN_2DIGITS)
{
str += "0" + hours + ":" ;
}
else
str += hours + ":";
if (minutes < MIN_2DIGITS)
{
str += "0" + minutes + ":" ;
}
else
str += minutes + ":";
if (seconds < MIN_2DIGITS)
{
str += "0" + seconds;
}
else
str += seconds;
//end of my input
return str;
}
} // End of class definition
If you're trying to find equality between the parameter Clock and the caller Clock, I would do the following
public boolean equals(Clock another_clock) {
// Check if 'this' is equal to 'another_clock'
// 1. If you're checking if the pointer is the same
return another_clock.equals(this);
// 2. If you're checking if time is the same (You probably need to create getter/setter methods or change privacy for these fields)
return another_clock.hours == this.hours &&
another_clock.minutes == this.minutes &&
another_clock.seconds == this.seconds;
}
Or something along those lines.
I'm working on the mooc.fi assignments and I'm really stuck at the clock assignment. I don't want a solution, but advice on how to reach my solution as I'm still learning and really need to figure out how to work through this. If you're unfamiliar with mooc, the current section is on how to work with objects within objects.
It consists of three classes, the main creates the clock and the boundedcounter is what makes the clock tick. I get it to print onto the screen, but when a custom starting input is entered it prints that first, then resets the value back to zero. Can someone point me in the right direction please? Sorry for the basic question, still trying to learn this java language!
Main
public class Main {
public static void main(String[] args) {
Clock clock = new Clock(23, 59, 50);
int i = 0;
while( i < 20) {
System.out.println( clock );
clock.tick();
i++;
}
}
}
Clock
public class Clock {
private BoundedCounter hours;
private BoundedCounter minutes;
private BoundedCounter seconds;
public Clock(int hoursAtBeginning, int minutesAtBeginning, int secondsAtBeginning) {
// the counters that represent hours, minutes and seconds are created and set to have the correct initial values
this.hours = new BoundedCounter(hoursAtBeginning);
this.hours.setValue(hoursAtBeginning);
this.minutes = new BoundedCounter(minutesAtBeginning);
this.minutes.setValue(minutesAtBeginning);
this.seconds = new BoundedCounter(secondsAtBeginning);
this.seconds.setValue(secondsAtBeginning);
}
public void tick() { //increases the time
this.seconds.next();
if (this.seconds.getValue() == 0) {
this.minutes.next();
if (this.minutes.getValue() == 0) {
this.hours.next();
}
}
}
public String toString() {
// returns the string representation
return hours + ":" + minutes + ":" + seconds;
}
}
BoundedCounter
public class BoundedCounter {
private int value;
private int upperLimit;
public BoundedCounter(int upperLimit) {
// write code here
this.value = 0;
this.upperLimit = upperLimit;
}
public void next() {
// write code here
if (value < upperLimit) {
value++;
} else {
value = 0;
}
}
public int getValue() {
return value;
}
public void setValue(int value) {
if (value >= 0 && value <= upperLimit) {
this.value = value;
}
}
#Override
public String toString() {
if (value < 10) {
return "0" + value;
}
return "" + value;
}
}
You're passing the current time to BoundedCounter.
this.hours = new BoundedCounter(hoursAtBeginning);
this.minutes = new BoundedCounter(minutesAtBeginning);
this.seconds = new BoundedCounter(secondsAtBeginning);
But BoundedCounter is expecting the upper bound, not the current time.
public BoundedCounter(int upperLimit) {
Since whatever time you pass it is considered the upper limit, the next tick rolls over to 0:0:0.
BoundedCounter.next() sets value to 0 if value is not less than upperLimit. Since the Clock constructor always initializes all BoundedCounter value values to be equal to upperLimit, the first call to Clock.tick() will cause all BoundedCounter objects to set their value values to 0.
Besides the corrections:
this.hours = new BounedCounter(int upperLimit);
this.minutes = new BounedCounter(int upperLimit);
this.seconds = new BounedCounter(int upperLimit);
the method public void tick() has to be composed as shown below, otherwise when the value of the minutes is 00 while the value of the seconds is 01, 02, ...59, the value of the hours will be increased with every second, and the result will be 01:00:01, 2:00:02, 3:00:03, etc.
public void tick() {
this.seconds.next();
if (this.seconds.getValue() == 0) {
this.minutes.next();
}
if (this.minutes.getValue() == 0 && this.seconds.getValue() == 0) {
this.hours.next();
}
}
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...