There's probably a better question/answer for this but what I've been finding hasn't worked out and I have had trouble phrasing the question for google query. Basically I have a JFrame with several panels and components that pull their data from xml files. I use the JFrame's instance variable private Date focusDate = new Date(); to store which day's info I'd like each panel to display, so far so good.
My problem comes now that I'm trying to set the various actions of the navigation components to update after I change 'focusDate'. I have a toolbar in a JPanel NavButtons navPanel = new NavButtons(focusDate); which I setup as an Inner Class and the console reports focusDate being changed but I can't get the JFrame to validate(), repaint(), etc... when I call my setFocus(Date d) method.
I can include more of my code if that would be helpful but here's the method in question:
public void setFocus(Date d) throws IOException {
focusDate = d;
dispose();
// validate(); //Tried revalidate too, but DisplayView extends JFrame
// repaint();
// revalidate();
// pack();
// DisplayView view = new DisplayView(focusDate);
setVisible(true); }
and here's how I'm setting the ActionListener in the constructor:
public NavButtons(Date d) {
newDate = LocalDate.parse(new SimpleDateFormat("yyyy-MM-dd").format(d));
weekBack.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
newDate = newDate.plusDays(-7);
try { setFocus(Date.from(newDate.atStartOfDay(ZoneId.systemDefault()).toInstant()));
} catch (IOException e) {
e.printStackTrace(); }
//validate();
//repaint();
}
});
I'm not very familiar with swing, so I'm sure this is some minor detail I'm just not getting but if someone can explain how to re-trigger the argument passing and update the child components of the frame to an amateur that would be most appreciated.
Update Here's the entire JFrame
package interfaceComponents;
import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.*;
import java.io.IOException;
import java.text.*;
import java.util.*;
import java.time.*;
public class DisplayView extends JFrame {
//instance variables
private Date focusDate = new Date();
//constructor
public DisplayView(Date d) throws IOException {
DisplayMenus menus = new DisplayMenus();
setJMenuBar(menus);
JPanel body = new JPanel();
body.setLayout(new BoxLayout(body, BoxLayout.Y_AXIS));
body.add(new DayView(focusDate));
LocalDate focusNextDay = LocalDate.now();
body.add(new DayView(Date.from(focusNextDay.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant())));
add(new JScrollPane(body), BorderLayout.CENTER);
JPanel footer = new JPanel();
NavButtons navPanel = new NavButtons(focusDate);
JLabel focusPoint = new JLabel(new SimpleDateFormat("E, dd MMM yyyy").format(focusDate).toString());
focusPoint.setForeground(Color.RED);
footer.setLayout(new BorderLayout());
footer.add(focusPoint, BorderLayout.CENTER);
footer.add(navPanel, BorderLayout.EAST);
footer.setBackground(Color.BLACK);
add(footer, BorderLayout.SOUTH);
pack(); }
public DisplayView() throws IOException { this(new Date()); }
public void setFocus(Date d) throws IOException {
focusDate = d;
SwingUtilities.updateComponentTreeUI(this);
// dispose();
// invalidate();
// validate(); //Tried revalidate too, but DisplayView extends JFrame
repaint();
// revalidate();
// pack();
// DisplayView view = new DisplayView(focusDate);
// setVisible(true);
}
public Date getFocus() { return focusDate; }
class NavButtons extends JPanel {
private JToolBar toolBar = new JToolBar("Navigation");
private JButton weekBack = new JButton("<<");
private JButton dayBack = new JButton("<");
private JButton returnToday = new JButton("Today");
private JButton nextDay = new JButton(">");
private JButton nextWeek = new JButton(">>");
private JButton calendar = new JButton("L");
private LocalDate newDate = LocalDate.now();
public NavButtons(Date d) {
newDate = LocalDate.parse(new SimpleDateFormat("yyyy-MM-dd").format(d));
weekBack.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
newDate = newDate.plusDays(-7);
try { setFocus(Date.from(newDate.atStartOfDay(ZoneId.systemDefault()).toInstant()));
} catch (IOException e) {
e.printStackTrace(); }
// invalidate();
// validate();
// repaint();
}
});
dayBack.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) { newDate = newDate.plusDays(-1); }
});
returnToday.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) { newDate = LocalDate.now(); }
});
nextDay.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) { newDate = newDate.plusDays(1); }
});
nextWeek.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) { newDate = newDate.plusDays(7); }
});
toolBar.add(weekBack);
toolBar.add(dayBack);
toolBar.add(returnToday);
toolBar.add(nextDay);
toolBar.add(nextWeek);
toolBar.add(new GalileoMode());
toolBar.add(calendar);
add(toolBar); }
}
}
As @MadProgrammer pointed out, I should have the date stored in a separate object (shown here):
package interfaceComponents;
import java.util.*;
public class FocusDate {
//instance variables
Date focus = new Date();
//constructors
//intentionally blank: public FocusDate() {}
//methods:
public void setFocus(Date d) {
focus = d; }
public Date getFocus() {
return focus; }
}
and I edited the Frame as such:
public class DisplayView extends JFrame {
//instance variables
FocusDate focus = new FocusDate();
// private Date focusDate = new Date();
//constructor
public DisplayView(Date d) throws IOException {
// focusDate = d;
Date focusDate = focus.getFocus();
...
public void setFocus(Date d) throws IOException {
// focusDate = d;
focus.setFocus(d);
Still not doing something right though...
So, the basic idea is to wrap the "current" date value in an observable pattern and pass this to each interested party. This allows the navigation to make changes to the date value, but not need to know about any other part of the program, decoupling it and allow for a more flexible outcome.
So, I started with two basic contracts...
One is non-mutable (for those parts of the programs that don't need to be able to change the date) and one is mutable, for those parts of the program that do (like the navigation)
Then I created a "default" implementation of the model...
I'm been lazy and using the
ObserverandObservableAPI fromjava.util, you can create your own based on your needs, but this just suits the example.What this means is, I can create an instance of
DefaultDateModeland pass it to those parts of the program that want aDateModeland those that want aMutableDateModelwithout needing to create separate instances, neat.Because we're working to interface contract, those parts of the program that only want a
DateModelwill only ever be able to access the methods defined within the interface (and if the cast it, then they are doing the wrong thing)...As an example...
Oh, I'd kind of avoid moving between
java.util.Dateandjava.time.LocalDateif you can, for your purposes, I'd stick withLocalDate, but that's me ;)