【问题标题】:Method listening方法监听
【发布时间】:2012-08-30 15:33:22
【问题描述】:

我是 Swing 的新手,所以这可能很愚蠢。

无论如何,我已经创建了一个扩展 JFrame 的类,并且在面板内包含一个 JTextField、一个 JTextArea 和一个 JButton。

这应该是一个控制台实现,因此当我按下 Enter 或旁边的按钮时,TextField 会将输入打印到 TextArea。

我已经为此设置了监听器,并且一切正常。我现在的问题是如何制作一个希望我在字段上按 Enter 的方法?例如我有一个需要 3 行输入的方法。第一行调用方法,第二行期望我输入一些东西,第三行期望更多输入。完成所有输入后,我将一些内容打印到 TextArea。

那么实际上该方法必须等待侦听器触发还是什么?有人可以解释一下这个东西是如何工作的,或者给我一个解决方法吗?

请记住,我想要一些可重用的方法,因为我可能会使用多行输入实现很多方法。提前致谢!

更新:这是我扩展 JFrame 的类 - 代码主要是 netbean 生成的,我将确保迟早处理导入语句。我还没有实现一个方法,因为我不知道怎么做,但是希望我添加一小段代码检查输入是否正确(在 ConsoleInputAcionPerformed 内部)并调用该方法(我们称之为 methodX ) 这将需要两条输入线的其余部分。这个类是从我的 main() 中的另一个类调用的。

public class MainWindow extends javax.swing.JFrame {
private javax.swing.JButton EnterButton;
private javax.swing.JPanel ConsolePanel;
private javax.swing.JScrollPane ConsoleScroll;
private javax.swing.JTextArea ConsoleOutput;
private javax.swing.JTextField ConsoleInput;

public MainWindow() {
    initComponents();
}

private void initComponents() {

    ConsolePanel = new javax.swing.JPanel();
    ConsoleScroll = new javax.swing.JScrollPane();
    ConsoleOutput = new javax.swing.JTextArea();
    ConsoleInput = new javax.swing.JTextField();
    EnterButton = new javax.swing.JButton();

    setTitle("Graphical Super Console v.1.0");
    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
    setPreferredSize(new java.awt.Dimension(800, 600));

    ConsoleOutput.setColumns(20);
    ConsoleOutput.setRows(5);
    ConsoleOutput.setLineWrap(true);
    ConsoleOutput.setEditable(false);
    ConsoleOutput.setFont(new java.awt.Font("Consolas", 1, 14));

    ConsoleScroll.setViewportView(ConsoleOutput);
    ConsoleScroll.setVerticalScrollBarPolicy(javax.swing.JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

    ConsoleInput.setText("");
    ConsoleInput.requestFocusInWindow();
    ConsoleInput.setFont(new java.awt.Font("Consolas", 1, 14));
    ConsoleInput.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            ConsoleInputActionPerformed(evt);
        }
    }); 

    EnterButton.setText(">>");
    EnterButton.setFont(new java.awt.Font("Consolas", 1, 14));
    EnterButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            ConsoleInputActionPerformed(evt);
        }
    });

    javax.swing.GroupLayout ConsolePanelLayout = new javax.swing.GroupLayout(ConsolePanel);
    ConsolePanel.setLayout(ConsolePanelLayout);
    ConsolePanelLayout.setHorizontalGroup(
        ConsolePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(ConsolePanelLayout.createSequentialGroup()
            .addContainerGap()
            .addGroup(ConsolePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(ConsoleScroll)
                .addGroup(ConsolePanelLayout.createSequentialGroup()
                    .addComponent(ConsoleInput, javax.swing.GroupLayout.DEFAULT_SIZE, 679, Short.MAX_VALUE)
                    .addGap(18, 18, 18)
                    .addComponent(EnterButton)))
            .addContainerGap())
    );
    ConsolePanelLayout.setVerticalGroup(
        ConsolePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(ConsolePanelLayout.createSequentialGroup()
            .addContainerGap()
            .addComponent(ConsoleScroll, javax.swing.GroupLayout.DEFAULT_SIZE, 536, Short.MAX_VALUE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addGroup(ConsolePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                .addComponent(EnterButton)
                .addComponent(ConsoleInput, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
            .addContainerGap())
    );

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addComponent(ConsolePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
    );
    layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addComponent(ConsolePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
    );

    pack();
    setVisible(true);
    ConsoleInput.requestFocus();
}
private void ConsoleInputActionPerformed(java.awt.event.ActionEvent evt) {
    printf(">"+ConsoleInput.getText()+"\n");
    ConsoleInput.setText("");
}

public javax.swing.JTextArea getConsoleOutput(){
    return ConsoleOutput;
}

public javax.swing.JTextField getConsoleInput(){
    return ConsoleInput;
}

public void printf(Object... obj){
    for(int i=0; i<obj.length; i++){
        ConsoleOutput.append(String.valueOf(obj[i]));
    }
}

}

【问题讨论】:

标签: java swing methods actionlistener


【解决方案1】:

观察者和可观察者:

这个想法基本上是你让某个类观察另一个类,当发生某些事情时,被观察的类 Observable 将通知该类 Observes,即观察者,并告诉它发生了一些变化。 Observable 有方法 setChanged() 和 notifyObservers() 来完成。并且 Observer 使用实现的 update() 方法监听该调用。

我将所有内容都放在一个类中,以便您可以复制/粘贴并运行它。当你按下一个键时,你会看到它是如何工作的。

//the textfield is wrapped in a class so that it can extends Observable
public class MyTextField extends Observable {

    private JTextField jTextField = new JTextField();

    //this method notifies the observers you will add   
    public void notify(Object o) {
        this.setChanged();
        this.notifyObservers(o);
    }

    public JTextField getJTextField() {
        return jTextField;
    }

}

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

// The main class that observes the swing component you tell it to
public class Controller implements Observer {

    private final JFrame jFrame = new JFrame();

    private final MyTextField myTextField = new MyTextField();

    public Controller() {

        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jFrame.pack();
        jFrame.setVisible(true);
        jFrame.add(myTextField.getJTextField());

        //here we add the Observer (Controller) to myTextField (Observable)
        myTextField.addObserver(this);

        //and the keylistener
        myTextField.getJTextField().addKeyListener(new KeyListener() {

            @Override
            public void keyTyped(KeyEvent e) {
                System.out.println("keyTyped " + e.getKeyCode());
                //now we notify our observers for real
                myTextField.notify(e.getKeyCode());
            }

            @Override
            public void keyReleased(KeyEvent e) {
                System.out.println("keyReleased " + e.getKeyCode());
                myTextField.notify(e.getKeyCode());
            }

            @Override
            public void keyPressed(KeyEvent e) {
                System.out.println("keyPressed " + e.getKeyCode());
                myTextField.notify(e.getKeyCode());
            }
        });
    }

    // this is where the event is received by the Observer 
    // from the observable.
    @Override
    public void update(Observable observable, Object object) {
        System.out.println("Notified by " + observable
                + " with object " + object);

    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Controller();
            }
        });
    }
}

我希望这是可以理解的,并且可以解决您的问题:)

【讨论】:

  • 这是一个很好的例子,我可以理解,我会尝试实现(不过,现在没有时间)。不过,我有一点困惑。需要观察整个 JFrame 还是只观察 JTextField?因为如果我必须只观察文本字段,这可能需要我一段时间来修改!还是非常感谢!
  • 我如何根据不同的输入实现多个观察者?
  • 除非你有一些你想在 JFrame 中观察的东西,比如 onclose/onminimize/... 事件或类似的东西,否则不需要它。您可以制作不同的实例 MyTextField 并使用一个或不同的观察者制作它们(这是可观察的)。当您 notifyObservers() 时,它将通知您添加到 MyTextField 实例中的所有观察者。
  • 所以我现在添加了多个观察者,它们出于某种原因实现不同的功能。是否有可能在某些情况下禁用某些观察者并调用其他观察者,或者我需要杀死我拥有的观察者并重新制作它们?有什么方法可以让一般的观察者蒙蔽双眼吗?顺便说一句,非常感谢,这个解决方案做到了!
  • 我认为你不能那样做。正如您所说,您可以删除观察者,执行通知然后读取它,但这需要您可以直接访问观察者,但情况可能并非如此。我认为在这种情况下最好的解决方案是在观察者的更新方法中实现一些逻辑,并在它满足您为其设置的要求时执行它包含的代码。类似 if (object instanceof boolean) ...
【解决方案2】:

我唯一能想到的就是你需要通过一个可以确定当前状态的方法来运行来自字段的输​​入......即

public void handleFieldInput(JTextField field) {
    String text = field;
    switch (state) {
        case 0:
            // First line...maybe store the result in a List or array
            state++;
            break;
        case 1:
            // Second line...
            state++;
            break;
        case 2:
            // Third line...
            // Add contents to the text area
            state = 0;
            break;
    }
}

【讨论】:

  • 为什么,为什么不简单地将 ActionListener 添加到 JTextField 中?
  • @brano88 因为据我所知,OP 已经在这样做了。据我所知,在将结果添加到文本区域之前,他需要多次处理来自单个字段的输入。
  • @HovercraftFullOfEels 我认为这是对帖子的误读(来自 cmets 的其他答案)
【解决方案3】:

这是您的解决方案:

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

import javax.swing.*;

public class Frame extends JFrame{
    JTextField t = new JTextField(20);
    JPanel p = new JPanel();

    public Frame(){
        p.add(t);
        t.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                t.setText("Hello world");
            }
        });
        add(p);
    }

    public static void main(String[] args){
        SwingUtilities.invokeLater(new Runnable(){
            public void run(){
                Frame f = new Frame();
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.pack();
                f.setVisible(true);
            }   
        });
    }
}

【讨论】:

  • 这是一个动作监听器,当你按下回车时他想要。你有 KeyListener 。
  • 我已经在我的代码中实现了监听器,我需要的是一种方法来等待监听器一次又一次地触发以获取我输入和计算的内容!
  • @jonathan.cruz 当用户在该字段上按 Enter 键时触发动作侦听器
  • 实际上我现在在想:p 有一个更好的解决方案。你可以使用 Observer 和 Observable。基本上,当 Observable 做出决定时,Observable 会通知 Observer。
  • @jonathan.cruz 如果他是初学者,那就太糟糕了。
【解决方案4】:

在等待用户输入时使方法挂起的最简单方法可能是创建一个带有文本框的 JDialog。在用户关闭 JDialog 之前,您的代码将不会运行超过您显示该对话框的点。然而,这似乎不是您正在寻找的解决方案。

您可能想要在这里让代码挂起的是使用等待和通知。如需更多信息,请参阅How to use wait and notify in Java?

【讨论】:

  • 谢谢,这实际上是一个好的开始! (一旦我获得声誉,我就会投票赞成,这是我在这个问题上的第一个领先优势:D)
  • 很高兴听到这个消息。我最近碰巧写了一个名为 ParameterInputDialog 的对象,它接受一个 var args 类型的数量,然后为每个传递的类型启动一个带有 JTextField 和 JLabel 的 JDialog。然后,用户必须在每个 JTextField 中输入一个有效值并在继续之前点击确定。再次不确定这是否适合你,但我想我会告诉你以防万一
  • 通知和等待用于并发,即当一个类需要等待另一个类完成其执行时。在这种情况下它没有用。
  • 我相信在这种情况下它实际上是有用的,因为他需要程序正在运行的线程等待。它等待的是一些用户输入。无论代码请求什么,这个输入都可以放在另一个线程中,从主线程启动,然后告诉主线程等待()。用户输入请求线程将通知主线程,一旦收到此输入,它就可以继续运行。
【解决方案5】:

您可能不得不以与您希望的方式略有不同的方式执行此操作。基本上你保持你已经收到的所有行的状态,只有当你已经有三行时,然后调用需要三行的方法。一般的想法是这样的:

List<String> buffer = new ArrayList<String>();

public void actionPerformed(ActionEvent e) {
    buffer.add(getText())
    if (!expectingMoreInput()) {
        processInput(buffer);
        buffer.clear();
    }
}

因此,对于您的具体情况,expectingMoreInput() 只会返回buffer.size() &lt; 3,而processInput 实际上会调用需要三行的方法。

另一种方法是使用多个线程和一个在它们之间传递线路的对象。请注意这一点 - 线程交互可能会变得复杂。比如:

SynchronousQueue<String> queue = new SynchronousQueue<String>();

public void actionPerfomred(ActionEvent e) {
    queue.add(getLine());
}

public void threeLineMethod() {
    String s1, s2, s3;
    try {
        s1 = queue.take();
        s2 = queue.take();
        s3 = queue.take();
    } catch (InterruptedException ex) {

    }
    // process the three lines
}

请注意,take 将阻止 put,这正是您想要的。另一方面是put 会阻塞take,所以如果你没有一个线程不断调用take,事件线程会阻塞并锁定接口。

【讨论】:

  • 想到这一点,这可能是我迄今为止唯一的解决方法。问题是我希望用消息来实现各种方法,在他们的代码中要求正确的输入和处理异常,所以问题是我需要写很多次,这可能需要更长的时间。基本上我希望模仿需要输入的正常控制台输入想法。这仍然是目前唯一可行的方法!谢谢! :)
  • 我编辑添加了另一个您可能会考虑的选项。将线程添加到应用程序时总是存在一些危险,但听起来它可能会简化您的代码。
  • 我喜欢我看到的可以使用线程完成的东西。我知道其中的危险,并且会非常小心,但在模仿内置 OS 控制台系统方面可能没有比这更好的了。
  • 所以您的意思是,如果退出此方法,我将需要一些其他方法来不断“获取”来自侦听器的输入,这样我的应用程序就不会停止和锁定,对吗? (因为我将实现一个名为 validate 或识别的方法,或者将在每次按下 Enter 键时输入的内容)。
  • 正确。你可以做一些像while (true) { queue.take(); // do whatever, handle input } 这样简单的事情,然后在那个循环中,你可以根据需要调用可能调用take() 的其他方法。例如,根据第一行,确定是否需要更多输入,如果需要,请再调用几次take()。当该方法退出时,它将返回到 while 循环并且不会锁定应用程序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-09
  • 1970-01-01
  • 2022-01-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多