I\'d like to create a software that has three pages: The \"Home Page\" (That\'s drawn on a JFrame \"frame\"), \"Page 2\" and \"Page 3\".
Pages 2 and 3 are drawn on \
The problem seems to be the fact that the method addAction
in your Page2
class is never called. It seems responsible for registering the actionListenerCallAnotherPage
to the buttonNavigation
JButton
....
Now, having said that...this seems like a lot of hard work for little gain.
Instead of trying to hard chain the navigation in this manner, you should use some kind of model that manages the navigation.
Basically, you would pass a reference of this model to each page in turn and each page would, when it's ready, ask the model to move to the next page. The model would fire some kind of change event to indicate to the main view that the current page has changed and the view would update itself accordingly.
You could do this in conjuntion with a CardLayout, meaning that you wouldn't even need to give a reference of the component to the model, but instead use the "page name" which is associated with the CardLayout
, further decoupling your program...
A model based approach
The basic idea is to separate your views from each other, even if they need to share data, I'd still use some kind of data model, but lets stay focused on the navigation model.
What you need...
Something like...
public interface ViewModel {
/**
* Returns the name of the current "active" view name
* @return
*/
public String getCurrentView();
/**
* Instructs the model to move to the next view if one is
* available...
*/
public void nextView();
/**
* Instructs the model to move to the previous view if one is
* available...
*/
public void previousView();
/**
* Returns the number of views in this model
*/
public int size();
/**
* Returns the name of the view at the specified index...
* @param index
* @return
*/
public String getViewAt(int index);
/**
* Adds a ChangeListeners to the model, which will be notified when
* the current view changes
*/
public void addChangeListener(ChangeListener listener);
/**
* Remove a ChangeListeners from the model
*/
public void removeChangeListener(ChangeListener listener);
}
...For example
Now, I like to have an abstract implementation which does all the boring, repetitive work...
public abstract class AbstractViewModel implements ViewModel {
private EventListenerList listenerList;
private List<String> views;
public void addView(String name) {
getViews().add(name);
}
public void removeView(String name) {
getViews().remove(name);
}
@Override
public int size() {
return views.size();
}
@Override
public String getCurrentView() {
return getViewAt(getCurrentViewIndex());
}
protected abstract int getCurrentViewIndex();
protected List<String> getViews() {
if (views == null) {
views = new ArrayList<>(25);
}
return views;
}
@Override
public String getViewAt(int index) {
return index >= 0 && index < size() ? getViews().get(index) : null;
}
@Override
public void addChangeListener(ChangeListener listener) {
getListenerList().add(ChangeListener.class, listener);
}
@Override
public void removeChangeListener(ChangeListener listener) {
getListenerList().remove(ChangeListener.class, listener);
}
protected EventListenerList getListenerList() {
if (listenerList == null) {
listenerList = new EventListenerList();
}
return listenerList;
}
protected void fireStateChanged() {
ChangeListener[] listeners = getListenerList().getListeners(ChangeListener.class);
if (listeners.length > 0) {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : listeners) {
listener.stateChanged(evt);
}
}
}
}
This allows me to devise different implementations to meet my needs...
For example, here, I've provided a "linear" view model, this model will move all the way to the last view and stop or all the way to first view and stop. It wouldn't take much to create a circular view model which when it reach either end of the mode, would flip around to the other end...
public class LinearViewModel extends AbstractViewModel {
private int currentIndex;
@Override
protected int getCurrentViewIndex() {
return currentIndex;
}
@Override
public void nextView() {
if (currentIndex + 1 < size()) {
currentIndex++;
fireStateChanged();
}
}
@Override
public void previousView() {
if (currentIndex - 1 >= 0) {
currentIndex--;
fireStateChanged();
}
}
}
To get started, you would need some way to map the view names to there representative components. You would also need some way to monitor for changes to the model...
private Map<String, Component> mapViews;
private Component currentView;
private LinearViewModel model;
public ViewModelTest() {
// Maps the view name to the component...
mapViews = new HashMap<>(25);
model = new LinearViewModel();
mapViews.put("Page01", new Page01());
mapViews.put("Page02", new Page02());
mapViews.put("Page03", new Page03());
mapViews.put("Page04", new Page04());
// Add the view names to the model...
model.addView("Page01");
model.addView("Page02");
model.addView("Page03");
model.addView("Page04");
// Initialise out view with the first page...
currentView = mapViews.get("Page01");
add(currentView);
// Monitor for changes...
model.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
remove(currentView);
currentView = mapViews.get(model.getCurrentView());
add(currentView);
revalidate();
}
});
}
Now, you're probably wondering, how does my view move to the next/previous view...?
It doesn't. Instead, I would create a navigation bar, which would exist on the main screen, which would be used to interact with the model itself...leaving each page/view decoupled from the whole process...