简体   繁体   中英

Why doesn't ActionListener work in the controller?

This post is continue of this post. Basically in my software is based on CardLayout panels. And now I want to change panels from outside of cardLayout . in the provided link, I got helped to do this and it worked. Now I added a controller to my software, that is between an inner panel ( CardLayout ) and an outside panel. In the outside panel( leftBar ) I have a button that if its actionListener is located inside that class it works correctly and open the chose inner panel of CardLayout . But if I take the ActionListener into the controller, it just doesnt work. No error comes up.

Here is my sample code: BASE:

public class Base {
        JFrame frame = new JFrame("Panel");
        BorderLayout bl = new BorderLayout();

    public Base(){
        MainPanel mainPanel = new MainPanel();
        LeftBar leftBar = new LeftBar(mainPanel);
        frame.setLayout(bl);
        frame.setSize(800, 600);
        frame.add(leftBar, BorderLayout.WEST);
        frame.add(mainPanel, BorderLayout.CENTER);

        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
    public static void main(String[] args) throws IOException {
        new Base();
    }
}

MainPanel (CardLAyout base)

public class MainPanel extends JPanel {
    private CardLayout cl = new CardLayout();
    private JPanel panelHolder = new JPanel(cl);

    public MainPanel(){
        FirstPage firstPage = new FirstPage(this);
        SecondPage secondPage = new SecondPage(this);
        LeftBar leftBar = new LeftBar(this);

        Controller controller = new Controller(secondPage, leftBar, this);
        setLayout(new GridLayout(0,1));

        panelHolder.add(firstPage, "firstPage");
        panelHolder.add(secondPage, "secondPage");

        cl.show(panelHolder, "firstPage");
        add(panelHolder);

    }
    public void showPanel(String panelIdentifier){
        cl.show(panelHolder, panelIdentifier);
    }
}

LeftBar

public class LeftBar extends JPanel{
    private JButton button;
    private MainPanel mainPanel;

    public LeftBar(MainPanel mainPanel){
        this.mainPanel = mainPanel;

        setPreferredSize(new Dimension(200, 40));
        setLayout(new BorderLayout());
        setBackground(Color.black);

        button = new JButton("Show Second Page");


       add(button, BorderLayout.NORTH);
    }
    public void addPageListener(ActionListener listenForButton){
        button.addActionListener(listenForButton);
    }
}

Second Page:

public class SecondPage extends JPanel{
    MainPanel mainPanel;
    JButton button;
    public SecondPage(MainPanel mainPanel){
       this.mainPanel = mainPanel;
        setBackground(Color.white);
       add(new JLabel("This is second page"));
    }
}

First Page:

public class FirstPage extends JPanel {
    MainPanel mainPanel;
    JButton button;

    public FirstPage(MainPanel mainPanel) {
       this.mainPanel = mainPanel;
       setBackground(Color.GRAY);

       button = new JButton("Show page");
       button.addActionListener(new ActionListener(){
           @Override
           public void actionPerformed(ActionEvent ae) {
               mainPanel.showPanel("secondPage");
           }

       });

       add(button);
    }
}

And controller:

public class Controller {
    private SecondPage secondPage;
    private LeftBar leftBar;
    private MainPanel mainPanel;
    public Controller(SecondPage secondPage, LeftBar leftBar, MainPanel mainPanel){
        this.secondPage=secondPage;
        this.leftBar=leftBar;
        this.mainPanel=mainPanel;

        this.leftBar.addPageListener(new ButtonListener());
    }

    class ButtonListener implements ActionListener{
        @Override
        public void actionPerformed(ActionEvent ae) {
            System.out.println("Works");
            mainPanel.showPanel("secondPage");
        }
    }
}

As you can see in LeftBar I have a method for the button that works as an actionListener . and I call that method in controller and give it a class. But it doesnt work. However the ACtionListener works if it is located in LeftBar class (inline).

Any idea how to fix it?

You're creating two instances of LeftBar , one in the Base constructor, which gets added to the screen...

public Base() {
    MainPanel mainPanel = new MainPanel();
    LeftBar leftBar = new LeftBar(mainPanel);
    frame.setLayout(bl);
    frame.setSize(800, 600);
    frame.add(leftBar, BorderLayout.WEST);
    frame.add(mainPanel, BorderLayout.CENTER);

And one in the MainPanel constructor which gets passed to the controller...

public class MainPanel extends JPanel {

    private CardLayout cl = new CardLayout();
    private JPanel panelHolder = new JPanel(cl);

    public MainPanel() {
        FirstPage firstPage = new FirstPage(this);
        SecondPage secondPage = new SecondPage(this);
        LeftBar leftBar = new LeftBar(this);

This means the controller is attach a ActionListener to a button which is never visible on the screen

The first thing I would do is start decoupling your code, rather then passing instances of your class, you need to establish a series of contracts to which you controller and views can agree to, for example, your MainPanel should provide a way to switch views, your LeftBar should provide notification when the user wants to switch views

For example...

public interface Pageable {

    public void showView(String name);

}

public interface Navigatable {

    public void addActionListener(ActionListener listener);

}

Then your main view would implement the Pageable interface

public class MainPanel extends JPanel implements Pageable {
    //...
    @Override
    public void showView(String name) {
        cl.show(panelHolder, name);
    }

And your LeftBar would implement the Navigatable interface

public class LeftBar extends JPanel implements Navigatable {
    //...
    @Override
    public void addActionListener(ActionListener listener) {
        button.addActionListener(listener);
    }

This simply defines the contractual capabilities of any implementation, your controller should not care about anything else (nor should you allow it to do things it wasn't meant to, like remove all the components from your views, naughty controller)

Your controller then just becomes responsible for managing the contracts between the interfaces...

public class Controller {

    private final Navigatable navigatable;
    private final Pageable pageable;

    public Controller(Navigatable navigatable, Pageable pageable) {
        this.navigatable = navigatable;
        this.pageable = pageable;

        navigatable.addActionListener(new ButtonListener());
    }

    class ButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent ae) {
            pageable.showView("secondPage");
        }
    }
}

For example...

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Base {

    JFrame frame = new JFrame("Panel");
    BorderLayout bl = new BorderLayout();

    public Base() {
        MainPanel mainPanel = new MainPanel();
        LeftBar leftBar = new LeftBar(mainPanel);
        frame.setLayout(bl);
        frame.setSize(800, 600);
        frame.add(leftBar, BorderLayout.WEST);
        frame.add(mainPanel, BorderLayout.CENTER);

        Controller controller = new Controller(leftBar, mainPanel);

        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public static void main(String[] args) throws IOException {
        new Base();
    }

    public interface Pageable {

        public void showView(String name);

    }

    public interface Navigatable {

        public void addActionListener(ActionListener listener);

    }

    public class MainPanel extends JPanel implements Pageable {

        private CardLayout cl = new CardLayout();
        private JPanel panelHolder = new JPanel(cl);

        public MainPanel() {
            FirstPage firstPage = new FirstPage(this);
            SecondPage secondPage = new SecondPage(this);
            setLayout(new GridLayout(0, 1));

            panelHolder.add(firstPage, "firstPage");
            panelHolder.add(secondPage, "secondPage");

            cl.show(panelHolder, "firstPage");
            add(panelHolder);

        }

        public void showPanel(String panelIdentifier) {
            cl.show(panelHolder, panelIdentifier);
        }

        @Override
        public void showView(String name) {
            cl.show(panelHolder, name);
        }
    }

    public class LeftBar extends JPanel implements Navigatable {

        private JButton button;
        private MainPanel mainPanel;

        public LeftBar(MainPanel mainPanel) {
            this.mainPanel = mainPanel;

            setPreferredSize(new Dimension(200, 40));
            setLayout(new BorderLayout());
            setBackground(Color.black);

            button = new JButton("Show Second Page");

            add(button, BorderLayout.NORTH);
        }

        @Override
        public void addActionListener(ActionListener listener) {
            button.addActionListener(listener);
        }
    }

    public class SecondPage extends JPanel {

        MainPanel mainPanel;
        JButton button;

        public SecondPage(MainPanel mainPanel) {
            this.mainPanel = mainPanel;
            setBackground(Color.white);
            add(new JLabel("This is second page"));
        }
    }

    public class FirstPage extends JPanel {

        MainPanel mainPanel;
        JButton button;

        public FirstPage(MainPanel mainPanel) {
            this.mainPanel = mainPanel;
            setBackground(Color.GRAY);

            button = new JButton("Show page");
            button.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent ae) {
                    mainPanel.showPanel("secondPage");
                }

            });

            add(button);
        }
    }

    public class Controller {

        private final Navigatable navigatable;
        private final Pageable pageable;

        public Controller(Navigatable navigatable, Pageable pageable) {
            this.navigatable = navigatable;
            this.pageable = pageable;

            navigatable.addActionListener(new ButtonListener());
        }

        class ButtonListener implements ActionListener {

            @Override
            public void actionPerformed(ActionEvent ae) {
                pageable.showView("secondPage");
            }
        }
    }
}

Now personally, this is just a bit of hacking around your code, personally, I would prefer to have a controller which interacted with a single view/contract, it makes it SO much simpler...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM