简体   繁体   中英

Java ActionListener in another class - accessing objects from main class

i am writing a simple BMI calculator program. The application includes ActionListener, which handles button click, check if textfields are filled in and executes calculations.

For now, the ActionListener method is as a subclass of a main class. And it looks like this:

BMICalc.java

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class BMICalc extends JFrame {

    private JMenuBar menuBar1;
    private JMenu jMenu1;
    private JMenuItem jMenuItem1, jMenuItem2;
    private JButton jButton1;
    private JPanel mainPanel, jPanel1;
    private JLabel jLabel1, jLabel2;
    private JTextField jTextField1, jTextField2;


    private BMICalc() {

        super("BMI Calculator");

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(new Dimension(250, 300));
        setLocationRelativeTo(null);
        setLayout(new BorderLayout(10, 10));


        mainPanel = new JPanel(new BorderLayout(10, 10));
        mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
        add(mainPanel);

        jPanel1 = new JPanel(new GridLayout(6,2));
        mainPanel.add(jPanel1, BorderLayout.CENTER);

        menuBar1 = new JMenuBar();

            jMenu1 = new JMenu("Help");
            menuBar1.add(jMenu1);

                jMenuItem1 = new JMenuItem("The purpose");
                jMenu1.add(jMenuItem1);

                jMenuItem2 = new JMenuItem("About");
                jMenu1.add(jMenuItem2);

        setJMenuBar(menuBar1);

        jLabel1 = new JLabel("Enter weight in [kg]:");
        jPanel1.add(jLabel1);

        jTextField1 = new JTextField("");
        jPanel1.add(jTextField1);

        jLabel2 = new JLabel("Enter height in [cm]:");
        jPanel1.add(jLabel2);

        jTextField2 = new JTextField("");
        jPanel1.add(jTextField2);

        jButton1 = new JButton("Calculate");
        mainPanel.add(jButton1, BorderLayout.SOUTH);

        Handler handler = new Handler();
        jButton1.addActionListener(handler);
        jMenuItem1.addActionListener(handler);
        jMenuItem2.addActionListener(handler);
    }


    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                BMICalc bmicalc = new BMICalc();
                bmicalc.setVisible(true);
            }
        });
    }

    private class Handler implements ActionListener {

        public void actionPerformed(ActionEvent event) {

            if (event.getSource() == jButton1) {

                if (jTextField1.getText().equals("") || jTextField2.getText().equals("")) {

                    JOptionPane.showMessageDialog(null, "All fields must be filled in!", "Error", JOptionPane.INFORMATION_MESSAGE);
                }
                else {
                    Calculations calcs = new Calculations();
                    calcs.calculateBMI(jTextField1.getText(), jTextField2.getText());
                    JOptionPane.showMessageDialog(null, "Your BMI: " +calcs.returnBMI());
                }
            }
            else if (event.getSource() == jMenuItem1) {

                JOptionPane.showMessageDialog(null, "The program calculates BMI based on information entered by user." , "The purpose of this program", JOptionPane.INFORMATION_MESSAGE);
            }
            else if (event.getSource() == jMenuItem2) {

                JOptionPane.showMessageDialog(null, "BMI Calc v. 1.0 " , "About", JOptionPane.INFORMATION_MESSAGE);
            }


        }

    }

}

Calculations.java

public class Calculations {

        private double BMI;
        private int weight, height;

        public void calculateBMI(String sWeight, String sHeight) {

            weight = Integer.parseInt(sWeight);
            height = Integer.parseInt(sHeight);
            BMI = weight/(height*height*0.0001);
        }

        public String returnBMI() {

            return String.format("%.2f", BMI);

        }
}

It works just fine, but I would like to make the code 'clenaer' and make the Handler a class, not a subclass, in another file. I've created a Handler.java and moved the whole Handler subclass, but the class doesn't see the jTextFields and jButton, as they are private (and as far as I'm concerned, they should be).

How can I separate ActionListener class, access these jObjects in it and still be fair with privacy stuff?

Thank you very much for answers.

You can pass the objects you need to the Handler class using the constructor :

public class Handler {

     private JButton button;
     private JTextField textField;

     public Handler(JButton button, JTextField textField) {
          this.button = button;
          this.textField = textField;
     }

}

And when you instantiate the class you just pass in the two variables you want:

Handler handler = new Handler(jButton1, jTextField1);

Explanation:

your Handler class is inner class of of BMICalc . When a nested class is not static (see also difference between static and non-static nested classes) it means that objects of those class exist within an object of the parent class. That's why your Handler class see private fields.

This is no problem for us when the class is static. You just have to pass in those variables to the Handler somehow (constructor or setter fields) and then you can reuse your class for other button-text field combinations.


Edit: Yet another way:

If your handler is to be used here, and only here, and nowhere else in the code, you could instantiate anonymous Handler and assign it to the field (no need to reuse somewhere else). So, in example:

jMenuItem1.addActionListener(new Handler() {
    @Override
    public void actionPerformed(ActionEvent event) {
        JOptionPane.showMessageDialog(null, "The program calculates BMI based on information entered by user." , "The purpose of this program", JOptionPane.INFORMATION_MESSAGE);
    }
});

jMenuItem2.addActionListener(new Handler() {
    @Override
    public void actionPerformed(ActionEvent event) {
        JOptionPane.showMessageDialog(null, "BMI Calc v. 1.0 " , "About", JOptionPane.INFORMATION_MESSAGE);
    }
});

Now you don't have to create one huge Handler with a lot of fields and ifs...

Note that the class Handler that you show is not a subclass of Main . To be a subclass means it inherits. What you have is an inner class .

You need to pass the references to the handler so it can refer to them. For example:

public class Handler implements ActionListener {
    private final JTextField jTextField1;
    private final JButton jButton1;
    public Handler(final JTextField textField, final JButton button)
        {
        this.jTextField1 = textField;
        this.jButton1 = button;
        }
}

And create it like this:

Handler handler = new Handler(jTextField1, jButton1);

If you want to protect those JTextField and JMenuItem from the others classes while having the handler in another classe, then you need to add some methods to the BMICalc class:

public boolean isButton1(ActionEvent event) {
    return event.getSource() == jButton1;
}

public boolean isJMenuItem1(ActionEvent event) {
    return event.getSource() == jMenuItem1;
}

public boolean isJMenuItem2(ActionEvent event) {
    return event.getSource() == jMenuItem2;
}


public String getJButton1Text() {
    return this.jButton1.getText();
}

public String getJTextField1Text() {
    return jTextField1.getText();
}

public String getJTextField2Text() {
    return jTextField2.getText();
}

Then you need to have the following Handler class:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JOptionPane;

public class Handler implements ActionListener {

private final BMICalc calc;

public Handler(BMICalc calc) {
    this.calc = calc;
}

public void actionPerformed(ActionEvent event) {

    if (calc.isButton1(event)) {

        if (calc.getJTextField1Text().equals("") || calc.getJTextField2Text().equals("")) {

            JOptionPane.showMessageDialog(null, "All fields must be filled in!", "Error", JOptionPane.INFORMATION_MESSAGE);
        }
        else {
            Calculations calcs = new Calculations();
            calcs.calculateBMI(calc.getJTextField1Text(), calc.getJTextField2Text());
            JOptionPane.showMessageDialog(null, "Your BMI: " +calcs.returnBMI());
        }
    }
    else if (calc.isJMenuItem1(event)) {

        JOptionPane.showMessageDialog(null, "The program calculates BMI based on information entered by user." , "The purpose of this program", JOptionPane.INFORMATION_MESSAGE);
    }
    else if (calc.isJMenuItem2(event)) {

        JOptionPane.showMessageDialog(null, "BMI Calc v. 1.0 " , "About", JOptionPane.INFORMATION_MESSAGE);
    }


}

}

And change on line in the BMICalc :

Handler handler = new Handler(this);

But as the handler is supposed to handle only button and input of this view (and BMICalc class), it would make more sense (for me) to keep this Handler class private and inside the BMICalc class). Hope this helps !

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