如何 return 来自不在内部 class 中的 ActionListener 的值

How to return values from an ActionListener which is not in inner class

我是 Java 和 Whosebug 的新人,所以,如果我做错了什么,请原谅。我马上改正!

我的问题是:我如何return一个class中的变量将ActionListener实现到另一个class的变量?

实现ActionListener的class不是内部class。

我自愿省略进口。

举个例子:

File_A.java

public class Gui extends JFrame {
    private JButton myButton;
    private String path;
    some other properties...

    public Gui () {
        myButton = new JButton("Some Text");
        myButton.AddActionListener(new Pick_Something());
    }
}

File_B.java

public class Pick_Something implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        JFileChooser selectElement = new JFileChooser();
        String path;
        int status = selectElement.showOpenDialog(null);

        if (status == JFileChooser.APPROVE_OPTION)
            path = selectElement.getSelectedFile().getAbsolutePath();
        else
            path = null;
    }
}

我怎么能把returnFile_B.javapath变量放到File_A.javapath变量里呢?

我试图编写一个 return 方法,但是该方法没有出现在所有方法的列表中,因此无法调用。我还尝试用 Gui 扩展 Pick_Something 并使 path 受到保护,但我有一个 WhosebugError.

有人看到我做错了什么或知道该怎么做吗?

My question is: How could I return a variable which is in a class that implements an ActionListener into another class's variable?

你不能。

快速查看 JavaDocs for ActionListener 会告诉您该方法没有 return 值。我很确定,即使它可以,它也毫无价值,因为您的代码唯一知道该方法已被触发的时间是它实际被调用的时候。

解决办法?将 "model" 传递给 ActionListener 实现...

首先定义一个简单的 interface 或合同...

public PathPicker {
    public void setPath(File path);
}

然后更新 PickSomething 以接受此 interface 的实例...

public class PickSomething implements ActionListener {
    private PathPicker picker;

    public PickSomething(PathPicker picker) {
        this.picker = picker;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        JFileChooser selectElement = new JFileChooser();
        int status = selectElement.showOpenDialog(null);

        if (status == JFileChooser.APPROVE_OPTION) {
            picker.setPath(selectElement.getSelectedFile());
        } else {
            picker.setPath(null);
        }
    }
}

现在,您需要做的就是实现 PathPicker 接口,在创建时将其引用传递给 PickSomething 并等待它调用 setPath.

这就是俗称的"delegation"(ActionListener也是它的一个例子),这里的实际责任要"delegated"到其他一些对象。它也是 "observability" 的一个例子,在一个非常简单的意义上,PickSomething 可以通过 PathPicker 的实例观察其状态的变化(选择了路径)。

它还解耦了代码,因为 PathPicker 不关心路径是如何设置的,只关心它何时被通知。

关于路径的注释...

File是文件系统文件或路径的抽象表示。它有很多非常酷的功能,可以使使用文件系统变得更容易和更简单。

许多 API 也引用 File 来执行它们的操作。您应该尽可能避免将 File 转换为 String,因为您将剥夺自己的此功能并使您的生活在长期 运行

中变得更加困难

我建议使用回调,Java-8 的 java.util.function 为您提供的东西,事实上 Consumer<String> 在这里可以完美地工作。在原始 class 中创建您的 Consumer,并让 ActionListener class 调用其 .accept(...) 方法,以低耦合将信息直接从侦听器 class 传递到 GUI。例如,如果您的 Gui 有一个名为 filePathTxtField 的 JTextField,您希望它填充用户选择的文件路径,一个由 ActionListener 获得,那么消费者可能看起来像这样:

Consumer<String> consumer = (String text) -> {
    filePathTxtField.setText(text);
};

这将在 Gui class 中创建,然后通过构造函数参数传递到 ActionListener class:

// in the Gui class's constructor
button.addActionListener(new PickSomething(consumer));  

// the PickSomething class and its constructor
class PickSomething implements ActionListener {
    private Consumer<String> consumer;

    public PickSomething(Consumer<String> consumer) {
        this.consumer = consumer;
    }

那么 actionPerformed 方法可能如下所示:

@Override
public void actionPerformed(ActionEvent e) {
    JFileChooser selectElement = new JFileChooser();
    String path;

    // get the path String and set it
    int status = selectElement.showOpenDialog(null);

    if (status == JFileChooser.APPROVE_OPTION) {
        path = selectElement.getSelectedFile().getAbsolutePath();
    } else {
        path = null;
    }

    // pass the path String into the Gui by calling the call-back method, passing it in
    consumer.accept(path);
}

整个事情看起来像:

import java.util.function.Consumer;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
public class Gui extends JPanel {
    private JTextField filePathTxtField = new JTextField(45);
    private int foo = 0;

    public Gui() {
        filePathTxtField.setFocusable(false);
        add(filePathTxtField);

        JButton button = new JButton("Get File Path");
        Consumer<String> consumer = (String text) -> {
            filePathTxtField.setText(text);
        };
        button.addActionListener(new PickSomething(consumer));
        add(button);
    }

    private static void createAndShowGui() {
        Gui mainPanel = new Gui();

        JFrame frame = new JFrame("Gui");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.function.Consumer;
import javax.swing.JFileChooser;

public class PickSomething implements ActionListener {
    private Consumer<String> consumer;

    public PickSomething(Consumer<String> consumer) {
        this.consumer = consumer;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        JFileChooser selectElement = new JFileChooser();
        String path;
        int status = selectElement.showOpenDialog(null);

        if (status == JFileChooser.APPROVE_OPTION) {
            path = selectElement.getSelectedFile().getAbsolutePath();
        } else {
            path = null;
        }
        consumer.accept(path);
    }
}   

这是我尝试过的方法,希望对您有所帮助。在此示例中,侦听器 class return 将所选路径指向主 window。

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

public class ActionListenerTester {

    private String path;
    private JLabel status;

    public static void main(String [] args) {
        new ActionListenerTester().gui();
    }
    private void gui() {
        JFrame frame = new JFrame();
        frame.setTitle("An External Listener");
        JLabel title = new JLabel("Get My Paths:");
        JButton button = new JButton("Get Path");
        button.addActionListener(new MyActionListener(this));
        status = new JLabel("Click the button to get path...");
        Container pane = frame.getContentPane();
        pane.setLayout(new GridLayout(3, 1));
        pane.add(title);
        pane.add(button);
        pane.add(status);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setSize(500, 300);        
        frame.setVisible(true);
    }

    public void setPath(String path) {
        this.path = path;
        status.setText(path);
    }
}

class MyActionListener implements ActionListener {

    private ActionListenerTester gui;

    public MyActionListener(ActionListenerTester gui) {
        this.gui = gui;
    }

    public void actionPerformed(ActionEvent e) {
        JFileChooser selectElement = new JFileChooser();
        String path = "";
        int status = selectElement.showOpenDialog(null);
        if (status == JFileChooser.APPROVE_OPTION) {
            path = selectElement.getSelectedFile().getAbsolutePath();
        }

        path = path.isEmpty() ? "No path selected!" : path;
        gui.setPath(path);
    }
}



替代方式:

这是 return 来自动作侦听器 class 的值的另一种方式,当它不是内部 class 时。到主 GUI class。这使用 java.util.ObserverObservable 对象。主 GUI class 是观察者,在动作侦听器 class 中选择的路径是可观察的。当可观察对象(路径)更新时,观察者(主 GUI class)会收到路径值通知。

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Observer;
import java.util.Observable;

public class ActionListenerTester2
        implements Observer {

    private String path;
    private JLabel status;

    public static void main(String [] args) {
        new ActionListenerTester2().gui();
    }
    private void gui() {
        JFrame frame = new JFrame();
        frame.setTitle("An External Listener 2");
        JLabel title = new JLabel("Get My Paths:");
        JButton button = new JButton("Get Path");
        button.addActionListener(new MyActionListener(this));
        status = new JLabel("Click the button to get path...");
        Container pane = frame.getContentPane();
        pane.setLayout(new GridLayout(3, 1));
        pane.add(title);
        pane.add(button);
        pane.add(status);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setSize(500, 300);        
        frame.setVisible(true);
    }

    /*
     * Observer interface's overridden method.
     * This method runs when the Observable object notifies
     * its observer objects (in this case, ActionListenerTester2)
     * about the update to the observable.
     */
    @Override
    public void update(Observable o, Object arg) {
        path = (String) arg;
        status.setText(path);
    }
}

class MyActionListener implements ActionListener {

    private PathObservable observable;

    public MyActionListener(ActionListenerTester2 gui) {
        observable = new PathObservable();
        observable.addObserver(gui);
    }

    public void actionPerformed(ActionEvent e) {

        JFileChooser selectElement = new JFileChooser();
        String path = "";
        int status = selectElement.showOpenDialog(null);
        if (status == JFileChooser.APPROVE_OPTION) {
            path = selectElement.getSelectedFile().getAbsolutePath();
        }

        System.out.println("Path: " + path);
        path = path.isEmpty() ? "No path selected!" : path;
        observable.changeData(path);
    }

    /*
     * When the Observable object changes, the notifyObservers()
     * method informs all the Observer objects - in this example
     * the main gui class: ActionListenerTester2.
     */
    class PathObservable extends Observable {

        PathObservable() {   
            super();
        }

        void changeData(Object data) {
            // the two methods of Observable class
            setChanged(); 
            notifyObservers(data);
        }
    }
}