Can't call a the third page via the 2nd Page

2019-01-29 11:34发布

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 "frame".

One uses a navigation pane that sits on the left side of the page and the main content is held on the right.

I'm currently only able to navigate to the 2nd page. The class called to draw page 2 on the JFrame can't seem to call Page three.

My code is as follows:

// The Home Page

package Try;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class HomePage {
    JPanel panelHomeWrapper = new JPanel();
    JPanel panelNavigation = new JPanel();
    JPanel panelDisplay = new JPanel();

    JButton buttonNavigation = new JButton("Button Home = Menu Items");
    JButton buttonBody = new JButton("Button body Page Home = Home body Items");

    public static void main (String[] args) {
        HomePage home = new HomePage();
        home.homePanel();
    }

    public void homePanel () {
        JFrame frame = new JFrame("Home");

        JButton button = new JButton("Button");

        ActionListener actionListener = new ActionListener() {
            public void actionPerformed(ActionEvent actionEvent) {
                System.out.println("Panel 2 was called.");

                Page2 panel2 = new Page2();

                panelNavigation.remove(buttonNavigation);
                panelDisplay.remove(buttonBody);

                panelNavigation.add(panel2.buttonNavigation);
                panelDisplay.add(panel2.buttonBody);

                panelNavigation.validate();
                panelNavigation.repaint();

                panelDisplay.validate();
                panelDisplay.repaint();
            }
        };

        button.addActionListener(actionListener);


        buttonNavigation.addActionListener(actionListener);

        panelNavigation.add(buttonNavigation);
        panelDisplay.add(buttonBody);

        panelHomeWrapper.add(panelNavigation);
        panelHomeWrapper.add(panelDisplay);

        frame.add(panelHomeWrapper);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(455, 355);
        frame.setVisible(true);
    }
} // End of Home Page

Page 2

// Page 2

package Try;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;

public class Page2 {
    JButton buttonNavigation = new JButton("Button 2 = Menu Items");
    JButton buttonBody = new JButton("Button body Page 2 = Page 2 body Items");

    ActionListener actionListenerCallAnotherPage = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent actionEvent) {
            System.out.println("Button Body 3 was called.");

            HomePage home = new HomePage();
            Page3 panel3 = new Page3();

            home.panelDisplay.remove(buttonBody);
            home.panelDisplay.add(panel3.buttonBody3);

            home.panelDisplay.validate();
            home.panelDisplay.repaint();

        }
    };

    public void addAction () {
        buttonNavigation.addActionListener(actionListenerCallAnotherPage);
    }
}

Page 3

// Page 3

package Try;

import javax.swing.JButton;

public class Page3 {
    JButton buttonBody3 = new JButton("Page 3");
} // End of Page 3

Please help me figure out how to make the 2nd class(page 2) call the 3rd Class(page 3). Thank you all in advance.

1条回答
迷人小祖宗
2楼-- · 2019-01-29 12:10

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...

  • Some idea of all the available views and the order they should be displayed in
  • The current "active" view
  • Some way to change the views, previous/next for example...
  • Some way to provide notifications about the change in state...
  • You also want to decouple the model from the actual components. While "strictly" speaking it's not wrong, I just don't like having my components begin passed around to places that they might get modified without my knowledge if I can help it. It's also not the responsibility of the model to update the views, that's the controllers responsibility...This also means that the actual component used to be displayed on the screen is irrelevant to the model...

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...

查看更多
登录 后发表回答