6

我正在尝试用 Java 制作一个应用程序。为了使 Swing 正常工作,我这样做了:

public static void main(String[] array){ 

String outerInput;
SwingUtilities.invokeLater(new Runnable(){
    @Override
    public void run() {
        // I want this string input.
        String input = JOptionPane.showInputDialog(
            null,"Stop ?", JOptionPane.QUESTION_MESSAGE);  
});
// How can I get this input value in String outerInput?
}

如何在主体中获取此输入字符串?

4

7 回答 7

6

您可以AtomicReference<String>用于以线程安全的方式在线程之间传递值。

正如 Hemal 所指出的,您需要在两个线程之间进行一些同步,以确保它已经被执行。例如,您可以使用 CountDownLatch 或使用 SwingUtilities.invokeAndWait (确保不要从 Swing 线程调用它!)

更新:这是使用AtomicReferenceCountDownLatch的完整示例

public class Main {
    public static void main(String[] args) throws InterruptedException {
        final AtomicReference<String> result = new AtomicReference<String>();
        final CountDownLatch latch = new CountDownLatch(1);

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                String input = JOptionPane.showInputDialog(null, "Stop?", "Stop?", JOptionPane.QUESTION_MESSAGE);
                result.set(input);

                // Signal main thread that we're done and result is set.
                // Note that this doesn't block. We never call blocking methods
                // from Swing Thread!
                latch.countDown();
            }
        });

        // Here we need to wait until result is set. For demonstration purposes,
        // we use latch in this code. Using SwingUtilities.invokeAndWait() would
        // be slightly better in this case.

        latch.await();

        System.out.println(result.get());
    }
}

另请阅读有关 GUI(和 Swing)应用程序的一般设计的答案。

于 2011-09-14T15:41:03.490 回答
5

如何在主体中获取此输入字符串?

你不会的。您的“主要”将调用一个 Swing 对话框然后对结果执行某些操作的想法与图形用户界面的整个想法背道而驰。

在 GUI 中,您设计程序来处理一系列用户启动的事件。这些事件可能是完全异步的,例如典型文字处理器的击键、选择和菜单选择。或者它们可能是脚本化的,例如“向导”的问答格式。

假设您想做类似后者的事情,您将使用以下序列实现它:

  1. 用户启动一些动作,可能选择一个菜单选项。这变成了对 an 的调用ActionListener,它决定它需要来自用户的更多输入。
  2. 在事件分派线程上执行的ActionListener,被允许对 UI 做任何它想做的事情,比如显示一个对话框。该对话框可以是模态的或非模态的;在一种情况下,原始侦听器可以使用输出,在另一种情况下,您需要编写一个新的侦听器来执行后续操作。
  3. 获得足够信息后,您可以选择调用后台操作。您通常会有一个线程池来服务这些请求。您不会尝试在“主”线程上执行请求;事实上,对于所有意图,主线程都不再运行。
  4. 当您的操作完成运行时,它会使用SwingUtilities.invokeLater(). 虽然您可以使用invokeAndWait()在后台操作中间将结果发送到 Swing,但这并不是一个好主意。相反,创建一系列操作,最好是用户可以轻松取消的操作。

在后台线程上启动操作的“标准”方式是通过SwingWorker。有替代方案;例如,您可以使用 aBlockingQueue将操作发送到单个长时间运行的后台线程,并用于invokeLater()返回结果。

无论如何,您不想打破一条规则永远不要在事件调度线程上执行阻塞操作。如果你这样做,那么你的应用程序就坏了

于 2011-09-14T19:33:58.240 回答
3

现在你有两个线程在运行:主线程和 EDT(事件调度线程)。我假设您知道它SwingUtilities.invokeLater(runnable)正在 EDT 上运行任务。

要在线程之间共享数据,您只需要两个线程范围内的一些变量。最简单的方法是声明一个volatile数据成员或AtomicReference在包含 main 方法的类中。

为了确保您在返回值后读取该值JOptionPane,您可以在这里做的最简单的事情是将 invokeLater 调用更改为invokeAndWait调用。这将导致您的主线程停止执行,直到您放入 EDT 的内容完成。

前任:

public class MyClass {
  private static volatile String mySharedData;
  public static void main(String[] args) throws InterruptedException {
    SwingUtilities.invokeAndWait(new Runnable() {
      public void run() {
        mySharedData = JOptionPane.showInputDialog(null, "Stop ?", JOptionPane.QUESTION_MESSAGE);
      }
    });
// main thread is blocked, waiting for the runnable to complete.

   System.out.println(mySharedData);
  }
}

如果您的主线程正在执行一些在选项窗格存在时不应停止的任务,那么您可以在主线程中定期检查(即在运行任务的循环的外部)是否mySharedData有被设置。如果您的任务没有循环,而是执行一些 I/O 或等待,您可以使用Thread.interrupt并检查mySharedDataInterruptedExecption 处理程序。

于 2011-09-14T15:42:28.463 回答
1

我建议为此使用观察者/可观察模式,也许使用 PropertyChangeListener。然后,如果关键变量状态发生变化,您的 Swing 应用程序将能够通知任何和所有侦听器。

例如:

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

public class ListenToSwing  {
   public static final String STATE = "state";
   private static final int STATE_MAX = 10;
   private static final int STATE_MIN = -10;
   private JPanel mainPanel = new JPanel();
   private int state = 0;
   private JSlider slider = new JSlider(STATE_MIN, STATE_MAX, 0);

   public ListenToSwing() {
      mainPanel.add(slider);
      slider.setPaintLabels(true);
      slider.setPaintTicks(true);
      slider.setMajorTickSpacing(5);
      slider.setMinorTickSpacing(1);

      slider.addChangeListener(new ChangeListener() {

         @Override
         public void stateChanged(ChangeEvent e) {
            setState(slider.getValue());
         }
      });
   } 

   public void addPropertyChangeListener(PropertyChangeListener listener) {
      mainPanel.addPropertyChangeListener(listener);
   }

   public Component getMainPanel() {
      return mainPanel;
   }

   public void setState(int state) {
      if (state > STATE_MAX || state < STATE_MIN) {
         throw new IllegalArgumentException("state: " + state);
      }
      int oldState = this.state;
      this.state = state;

      mainPanel.firePropertyChange(STATE, oldState, this.state);
   }

   public int getState() {
      return state;
   }

   public static void main(String[] args) {
      final ListenToSwing listenToSwing = new ListenToSwing();

      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            JFrame frame = new JFrame("ListenToSwing");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(listenToSwing.getMainPanel());
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
         }
      });

      listenToSwing.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getPropertyName().equals(ListenToSwing.STATE)) {
               System.out.println("New state: " + listenToSwing.getState());
            }
         }
      });
   }

}
于 2011-09-14T16:56:34.027 回答
1

您可以使用 AtomicReference 和 invokeAndWait。

public static void main(String[] array){ 

AtomicReference<String> outerInput = new AtomicReference<String>();
SwingUtilities.invokeAndWait(new Runnable(){
    @Override
    public void run() {
        String input = JOptionPane.showInputDialog(
            null,"Stop ?", JOptionPane.QUESTION_MESSAGE);
        outerInput.set(input); 
});

outerInput.get(); //Here input is returned.
}
于 2014-09-16T13:47:38.143 回答
0

您可以通过声明String[]可运行对象设置值的 a 来轻松地将其公开给外部类。但请注意,您将需要一些同步机制来知道它是否已由Runnable.

于 2011-09-14T15:31:15.567 回答
0

以下代码将执行您想要的操作。我做了类似的事情,只是我启动了一个JFileChooser而不是一个输入对话框。我发现它比将一堆路径硬编码到我的应用程序中或接受命令行参数更方便,至少出于测试目的。我想补充一点,可以修改prompt()方法以返回FutureTask实例以增加灵活性。

public class Question {

    public static void main(String[] args) {
        Question question = new Question();
        String message = "Stop?";
        System.out.println(message);
        // blocks until input dialog returns
        String answer = question.ask(message);
        System.out.println(answer);
    }

    public Question() {
    }

    public String ask(String message) {
        try {
            return new Prompt(message).prompt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return null;
    }

    private class Prompt implements Callable<String> {

        private final String message;

        public Prompt(String message) {
            this.message = message;
        }

        /**
         * This will be called from the Event Dispatch Thread a.k.a. the Swing
         * Thread.
         */
        @Override
        public String call() throws Exception {
            return JOptionPane.showInputDialog(message);
        }

        public String prompt() throws InterruptedException, ExecutionException {
            FutureTask<String> task = new FutureTask<>(this);
            SwingUtilities.invokeLater(task);
            return task.get();
        }

    }

}
于 2014-03-30T20:52:51.057 回答