简体   繁体   English

试图从System.out.println迁移到Swing的Java学徒

[英]A java apprentice trying to migrate from System.out.println to swing

I'm finally trying to migrate my old console programs to Swing, to make distribution to my friends easier. 我终于尝试将旧的控制台程序迁移到Swing,以简化向我的朋友的分发。 To this end, I'm trying to write a class ConsoleFrame that I can extend instead of JFrame, that will allow me to interface my old code with Swing as effortlessly as possible. 为此,我试图编写一个我可以扩展而不是JFrame的类ConsoleFrame,这将允许我尽可能轻松地将旧代码与Swing接口。 out(String) appears to be working, but inln() has me stumped. out(String)似乎可以工作,但是inln()让我感到难过。

//Imports not included
public class ConsoleFrame extends JFrame
{
    protected JTextField in;
    protected JTextArea out;

    public ConsoleFrame(){
        this("Console Frame", 80, 10);
    }
    public ConsoleFrame(int cols){
        this("Console Frame", cols, 10);
    }
    public ConsoleFrame(int cols, int rows){
        this("Console Frame", cols, rows);
    }
    public ConsoleFrame(String title){
         this(title, 80, 10);
    }
    public ConsoleFrame(String title, int cols, int rows){
        in = new JTextField();
        in.setEditable(true);
        in.setColumns(cols);

        out = new JTextArea();
        out.setEditable(false);
        out.setColumns(cols);
        out.setRows(rows);
        out.setWrapStyleWord(true);

        setTitle(title);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        add(in, BorderLayout.PAGE_END);
        add(out, BorderLayout.PAGE_START);
        pack();
    }

    protected void out(String o) {
        out.append(o);
    }
    protected void outln(String o) {
            out(o + BIO.$ln);    //BIO.$ln == System.getProperty("line.separator")
    }

    /*
     * This is supposed to halt execution until the user presses enter, then return the text entered in the JTextField named in.
     */
    protected String inln() {
        in.setEnabled(true);
        KeyListener enter = new KeyListener() {
            @Override
            public void keyTyped(KeyEvent paramKeyEvent) {
                if(paramKeyEvent.getKeyCode() == KeyEvent.VK_ENTER) {
                     if(in.hasFocus()) {
                         in.setEnabled(false);
                     }
                }
            }
            @Override public void keyPressed(KeyEvent paramKeyEvent) {}
            @Override public void keyReleased(KeyEvent paramKeyEvent) {}    
        };
        in.addKeyListener(enter);
        while(true){    //This loop is intended to interrupt flow until in.isEnabled()==false, which will only happen when the enter key is typed.
            if(in.isEnabled()==false){
                String result = in.getText();
                in.setText("");
                in.setCaretPosition(0);
                this.removeKeyListener(enter);
                in.setEnabled(true);
                return result;
            }
        }
    }
}

Tester program: 测试程序:

public class Tester extends ConsoleFrame
{
    public static void main(String[] args) {
        new Tester();
    }
    public Tester() {
        super("Test", 60, 30);
        out(inln());
    }
}

Don't know if it will fix your problem, but you should NOT be using a KeyListner. 不知道它是否可以解决您的问题,但是您不应该使用KeyListner。

JTextField was designed to use an ActionListener to handle the Enter key. JTextField设计为使用ActionListener来处理Enter键。

A bit of debugging shows the key code is 0x0 always. 一点调试显示密钥代码始终为0x0。 Replacing keyTyped with the following code seems to work; 用以下代码替换keyTyped似乎可行;

public void keyTyped(KeyEvent paramKeyEvent) {
    if (paramKeyEvent.getKeyChar() == '\n') {
        if(in.hasFocus()) {
            in.setEnabled(false);
        }
    }
}

EDIT: I'm not saying this is a solution I'm overly happy with, but it seems to do the trick. 编辑:我并不是说这是我很不满意的解决方案,但这似乎可以解决问题。

You are adding the keyListener to the frame, and not the in textfield. 您是将keyListener添加到框架,而不是in文本字段。 Therefore, the event will always be captured by the textfield and never propagated up to the Frame. 因此,事件将始终由文本字段捕获,并且永远不会传播到Frame。 This is why you are not receiving the event. 这就是为什么您没有收到事件。

That inln code looks racy to me. 那个inln代码对我来说看起来很inln To make it more robust: 为了使其更强大:

  • Change your code so that the actual program runs in a thread different from the event dispatch thread (I am not sure if this is already true). 更改您的代码,以使实际程序在与事件分发线程不同的线程中运行(我不确定这是否已经成立)。 SwingUtilities.isEventDispatchThread should return false when called from your test program. 从测试程序中调用SwingUtilities.isEventDispatchThread时应返回false。

  • Use SwingUtilities.invokeLater in your output methods so that the actual output will happen on the event dispatch thread asynchronously. 在输出方法中使用SwingUtilities.invokeLater ,以便实际输出将异步发生在事件分发线程上。

  • in inln() , use synchronization and SwingUtilities.invokeAndWait to initialize your key listener stuff (or use an ActionListener as already stated). inln() ,使用inln()SwingUtilities.invokeAndWait初始化您的关键侦听器(或使用已经声明的ActionListener )。 Then use Object.wait() and Object.notify() instead of your busy-waiting loop while waiting for input (laptop users' batteries will thank you for that, and the Task Manager which will no longer show 100% CPU usage). 然后在等待输入时使用Object.wait()Object.notify()而不是忙于等待的循环(笔记本电脑用户的电池将对此表示感谢,而任务管理器将不再显示100%的CPU使用率)。


Sorry, Here is the (tested and working) code for you: 抱歉,这是您的(经过测试且有效的)代码:

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

public class ConsoleFrame extends JFrame
{
    private Object lock = new Object();
    private String enteredText = null;

    protected JTextField in;
    protected JTextArea out;

    public ConsoleFrame(){
        this("Console Frame", 80, 10);
    }
    public ConsoleFrame(int cols){
        this("Console Frame", cols, 10);
    }
    public ConsoleFrame(int cols, int rows){
        this("Console Frame", cols, rows);
    }
    public ConsoleFrame(String title){
         this(title, 80, 10);
    }
    public ConsoleFrame(String title, int cols, int rows){
        in = new JTextField();
        in.setEditable(true);
        in.setColumns(cols);

        ActionListener enter = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                in.setEnabled(false);
                synchronized(lock) {
                    enteredText = in.getText();
                    lock.notifyAll();
                }
                in.setText("");
            }
        };
        in.addActionListener(enter);

        out = new JTextArea();
        out.setEditable(false);
        out.setColumns(cols);
        out.setRows(rows);
        out.setWrapStyleWord(true);

        setTitle(title);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        add(in, BorderLayout.PAGE_END);
        add(out, BorderLayout.PAGE_START);
        pack();
    }

    protected void out(final String o) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                out.append(o);
            }
        });
    }

    protected void outln(String o) {
            out(o + System.getProperty("line.separator"));
    }

    /*
     * This is supposed to halt execution until the user presses enter, then return the text entered in the JTextField named in.
     */
    protected String inln() {
        SwingUtilities.invokeLater(new Runnable() { 
            @Override
            public void run() {
                in.setEnabled(true);
            }
        });
        synchronized(lock) {
            enteredText = null;
            while (enteredText == null) {
                try {
                    lock.wait();
                } catch (InterruptedException ex) {}
            }
            return enteredText;
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM