【问题标题】:How to return values from an ActionListener which is not in inner class如何从不在内部类中的 ActionListener 返回值
【发布时间】:2018-12-25 23:11:23
【问题描述】:

我是 Java 新手,也是 StackOverflow 的新手,如果我做错了什么,请见谅。我马上改正!

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

实现ActionListener的类不是内部类。

我自愿省略了导入。

这里是一个例子:

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;
    }
}

如何在File_A.javapath 变量中返回File_B.javapath 变量?

我尝试编写一个返回它的方法,但该方法没有出现在所有方法的列表中,因此无法调用。我还尝试使用Gui 扩展Pick_Something 并使path 受保护,但我有一个StackOverflowError

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

【问题讨论】:

  • 您不能从 void 方法返回值(正如您所发现的那样),这不是事件驱动编程的工作方式。您要么需要某种回调机制,要么使用模态对话框,例如 JOptionPane 或模态 JDialog。请查看我在this similar question 中的回答。
  • 另外,如果 PickSomething 引用了可视化的 Gui 对象,它可能会向该对象“推送”一个值,但这会导致紧密耦合,因此不是最佳设计。
  • 请阅读 Java 命名约定并避免使用“_”下划线字符。
  • 您想将动作侦听器定义为不是内部类的原因是什么?
  • 我已经发布了两种替代方法的答案。它们在某种程度上有助于回答您的问题吗?

标签: java swing actionlistener inner-classes


【解决方案1】:

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

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

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

// 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);
    }
}   

【讨论】:

  • 您好,感谢您的回答!效果很好!
【解决方案2】:

我的问题是:我怎样才能将一个在实现 ActionListener 的类中的变量返回到另一个类的变量中?

你不能。

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

解决方案?将“模型”传递给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

这通常被称为“委托”(ActionListener 也是它的一个例子),其中将实际责任“委托”给其他对象。这也是“可观察性”的一个例子,在一个非常简单的意义上,PickSomething 可以通过PathPicker 的实例观察到其状态的变化(选择了一条路径)。

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

关于路径的注释...

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

许多 API 还引用 File 来执行它们的操作。您应该尽可能避免将File 转换为String,因为从长远来看,您将失去此功能并使您的生活更加困难

【讨论】:

  • 您好,感谢您的回答并教我有关接口和文件的知识!我现在正在使用 File 对象
【解决方案3】:

这是我尝试过的,希望对您有所帮助。在此示例中,侦听器类将所选路径返回到主窗口。

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);
    }
}



替代方式:

这是从动作侦听器类返回值的另一种方法,当它不是内部类时。到主要的 GUI 类。这使用 java.util.ObserverObservable 对象。主要的 GUI 类是观察者,动作侦听器类中的选定路径是可观察的。当可观察对象(路径)被更新时,观察者(主 GUI 类)会收到路径值的通知。
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);
        }
    }
}

【讨论】:

  • 您好,感谢您的回答!我尝试了您的替代解决方案,仅供您参考,因为 Java 9 已弃用,所以 Observable 和 Observer
  • 感谢您的信息;我不知道 Java 9 中不推荐使用 Observer/Observable。我认为可以以类似的方式使用java.beans.PropertyChangeListener
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多