I have a little program that inputs a date from a JDateChooser component and calculates the number of days from now until the date entered. It uses a MVC pattern, was coded in Netbeans IDE, and calculates the correct number of days but does not display it in "labelDays" which is a JLabel. When I type in labelDays.setText("29") it works and when I get the value of labelDays.getText() it retrieves the right number of days into the future, and strDays is correct, but the label does not display the updated value. Here is the sample code:
model:
public class CountDownModel {
public LocalDate getCurrentDate() {
return LocalDate.now();
}
public long getDays(LocalDate futureDate) {
long daysBetween = DAYS.between(LocalDate.now(), futureDate);
if(daysBetween <= 0) {
return 0;
}
return daysBetween;
}
view:
public class CountDownView extends javax.swing.JFrame {
...
private CountDownController controller = new CountDownController();
public CountDownView() {
initComponents();
Date input = new Date();
Instant instant = input.toInstant();
Date output = Date.from(instant);
future_date.setDate(output);
}
private void button_calculateMouseClicked(java.awt.event.MouseEvent evt) {
Date futureDate;
futureDate = future_date.getDate();
String strDate = DateFormat.getDateInstance().format(futureDate);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d-MMM-yyyy");
LocalDate localDate = LocalDate.parse(strDate, formatter);
controller.setDays(localDate);
}
...
public void setDays(long days) {
String strDays = String.valueOf(days);
System.out.print("strDays:");
System.out.println(strDays);
String oldValue = labelDays.getText();
labelDays.setText(strDays);
labelDays.paintImmediately(labelDays.getVisibleRect());
String newValue = labelDays.getText();
System.out.print("oldValue:");
System.out.println(oldValue);
System.out.print("newValue:");
System.out.println(newValue);
System.out.println("================");
}
}
controller:
public class CountDownController {
public void startApplication() {
CountDownView view = new CountDownView();
view.setDays(0);
view.setVisible(true);
}
public void setDays(LocalDate futureDate) {
CountDownModel model = new CountDownModel();
CountDownView view = new CountDownView();
long longDays = model.getDays(futureDate);
if(longDays <= 0) {
longDays = 0;
}
view.setDays(longDays);
}
}
main:
public class DateCountDown {
public static void main(String[] args) {
// TODO code application logic here
CountDownController controller = new CountDownController();
controller.startApplication();
}
}
Output:
run:
strDays:0
oldValue:200
newValue:0
================
strDays:28
oldValue:200
newValue:28
================
Thanks. What do I need to do to make it work? PS: I wonder if my error is due to the way I setup my MVC.
Philip
I find it a little bit strange how MVC is set up here.
First of all, I think there is no reason to recreate CountDownView
every time you're doing setDays
. That may be the reason why a label does not show its new text - a new instance of CountDownView
can be simply unvisible: an old instance of CountDownView
is visible, while a new is not. So, here controller can have an instance of CountDownView
as an object-level field. I can say the same about CountDownModel
.
Futhermore, a view creates its own controller, that is not efficient, because leads to crosslinks and memory leaks. I think a constructor of CountDownView
can accept an instance of CountDownController
as an agrument, and store it as an object-level weak reference.
Moreover, it's a common practice to start all Swing jobs in a new instance of Runnable
, for example:
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
CountDownController controller = new CountDownController();
controller.startApplication();
}
});
You can modify your code in the following way (hope it helps):
model:
public class CountDownModel {
public LocalDate getCurrentDate() {
return LocalDate.now();
}
public long getDays(LocalDate futureDate) {
long daysBetween = DAYS.between(LocalDate.now(), futureDate);
if(daysBetween <= 0) {
return 0;
}
return daysBetween;
}
}
view:
public class CountDownView extends javax.swing.JFrame {
...
private WeakReference<CountDownController> controller;
public CountDownView(CountDownController controller) {
this.controller = new WeakReference<>(controller);
initComponents();
Date input = new Date();
Instant instant = input.toInstant();
Date output = Date.from(instant);
future_date.setDate(output);
}
private void button_calculateMouseClicked(java.awt.event.MouseEvent evt) {
Date futureDate;
futureDate = future_date.getDate();
String strDate = DateFormat.getDateInstance().format(futureDate);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d-MMM-yyyy");
LocalDate localDate = LocalDate.parse(strDate, formatter);
controller.get().setDays(localDate);
}
...
public void setDays(long days) {
String strDays = String.valueOf(days);
System.out.print("strDays:");
System.out.println(strDays);
String oldValue = labelDays.getText();
labelDays.setText(strDays);
labelDays.paintImmediately(labelDays.getVisibleRect());
String newValue = labelDays.getText();
System.out.print("oldValue:");
System.out.println(oldValue);
System.out.print("newValue:");
System.out.println(newValue);
System.out.println("================");
}
}
controller:
public class CountDownController {
private CountDownView view;
private CountDownModel model;
public void startApplication() {
view = new CountDownView(this);
model = new CountDownModel();
view.setDays(0);
view.setVisible(true);
}
public void setDays(LocalDate futureDate) {
long longDays = model.getDays(futureDate);
if(longDays <= 0) {
longDays = 0;
}
view.setDays(longDays);
}
}
main:
public class DateCountDown {
public static void main(String[] args) {
// TODO code application logic here
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
CountDownController controller = new CountDownController();
controller.startApplication();
}
});
}
}