0

The problem I'm facing here is that I'm not sure how to have a JButton action result in the calling of a method many classes above and removed from itself.

To give some idea of scope and structure here, I am working on a ski resort management app. It features classes like SkiRun and Lift, both of which are used within a SkiResort class. The SkiResort holds the functionality to perform actions on these. Originally, I used a ResortApp class to perform the (console) interfacing, taking in user inputs and making modifications to the SkiResort accordingly.

I am now adding a GUI via the Swing library, and have a complicated structure of (obviously) a JFrame, which contains many JPanels, which each contain more JPanels, and some of those contain JButtons. These are all managed through a class named MountainManager that sets up the JFrame and components, and should perform the interfacing with the SkiResort. The real issue I face here is that I'm not sure how to have it so that when a JButton is pressed, for example, that call can be passed back up to the MountainManager it's contained in to be able to perform whatever action it's supposed to perform.

In the included diagram, the red line is the calling I'm trying to achieve. So far, all of the JFrames and Panels and such are called new, and have no functionality.

diagram, red line is what's desired

Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111
  • What exactly is the JButton passing back up to the MountainManager? You could have a `ButtonXPressed()` method that the JButton calls on press. – Josi Whitlock Mar 19 '21 at 23:19
  • to clarify, i need to have the button press result in a method being called in MountainManager, not passing any data – Alex Dart Mar 20 '21 at 00:07
  • You have to pass the instance of MountainManager down through every JFrame and JPanel class. Usually, in Java Swing, you create the model. the view, and as many controller classes as you have JButtons so you don't run into this problem of passing control back and forth between classes. – Gilbert Le Blanc Mar 20 '21 at 04:35

1 Answers1

1

As basic simple solution is to path a listener to the target Jbutton:

import java.awt.event.ActionListener;
import javax.swing.*;   

public class MountainManager {

    private int counter = 0;

    public MountainManager() {
        ActionListener actionListener = e -> {
            System.out.println("Button presssed "+ ++counter +" times");
        };
        new View(actionListener);
    }

    public static void main(String[] args) {
        new MountainManager();
    }
}

class View{

    private final ActionListener actionListener;

    public View(ActionListener actionListener) {

        this.actionListener = actionListener;
        JFrame f = new JFrame("Ski Resort");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLocationByPlatform(true);
        f.add(new MainPanel());
        f.pack();
        f.setVisible(true);
    }

    class MainPanel extends JPanel{

        MainPanel() {
            JButton testButton = new JButton("Click");
            testButton.addActionListener(actionListener);
            add(testButton);
        }
    }
} 

A better design can be achieved by using MVC pattern. Introduce a Model class, shared between View and MountainManage (the controller) and listen to changes in Model:

import javax.swing.*;
import javax.swing.event.ChangeListener;

public class MountainManager {

    public MountainManager() {

        Model model = new Model();
        model.setListener(e -> System.out.println("Button presssed "+ model.getClickCount() +" times"));
        new View(model);
    }

    public static void main(String[] args) {
        new MountainManager();
    }
}

class View{

    private final Model model;

    public View(Model model) {

        this.model = model;
        JFrame f = new JFrame("Ski Resort");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLocationByPlatform(true);
        f.add(new MainPanel());
        f.pack();
        f.setVisible(true);
    }

    class MainPanel extends JPanel{

        MainPanel() {
            JButton testButton = new JButton("Click");
            testButton.addActionListener(e->model.incrementClickCount());
            add(testButton);
        }
    }
}

class Model {

    private int clickCount=0;
    //todo: change to List<ChangeListener> to support multi listeners
    private ChangeListener listener;

    int getClickCount(){
        return clickCount;
    }

    void incrementClickCount(){
         clickCount++;
         notifyListener();
    }

    void notifyListener(){
        if(listener != null){
            listener.stateChanged(null);
        }
    }

    void setListener(ChangeListener listener) {
        this.listener = listener;
    }
}

(Test it online here)

c0der
  • 18,467
  • 6
  • 33
  • 65