Java 线程不能与 JTextArea 一起正常工作

标签 java string multithreading jtextarea

在 Java 中,我目前正在创建一个登录/注册程序(希望将来扩展它)但是遇到了问题。

我试图让文本以动画类型的作家风格出现在屏幕上。

当我运行该类的 main 方法时,文本显示得很好,当我在 keylistener 中运行它时,它失败并等待文本出现,然后更新框架以便我可以看到它。

源代码如下:

登录类

public class Login implements KeyListener {

int BACK_WIDTH = java.awt.Toolkit.getDefaultToolkit().getScreenSize().width;
int BACK_HEIGHT = java.awt.Toolkit.getDefaultToolkit().getScreenSize().height;

JFrame back_frame = new JFrame();

LoginTerminal login = new LoginTerminal();

public Login() {
    back_frame.setSize(BACK_WIDTH, BACK_HEIGHT);
    back_frame.setLocation(0, 0);
    back_frame.getContentPane().setBackground(Color.BLACK);
    back_frame.setUndecorated(true);
    //back_frame.setVisible(true);

    back_frame.addKeyListener(this);
    login.addKeyListener(this);
    login.setLocationRelativeTo(null);
    login.setVisible(true);
    login.field.addKeyListener(this);

    login.slowPrint("Please login to continue...\n"
               + "Type 'help' for more information.\n");
}

public void keyPressed(KeyEvent e) {
    int i = e.getKeyCode();

    if(i == KeyEvent.VK_ESCAPE) {
        System.exit(0);
    }

    if(i == KeyEvent.VK_ENTER) {
        login.slowPrint("\nCommands\n"
                 + "-----------\n"
                 + "register\n"
                 + "login\n");
    }
}

public void keyReleased(KeyEvent e) {}

public void keyTyped(KeyEvent e) {}

}

LoginTerminal.class

public class LoginTerminal implements KeyListener {

CustomFrame frame = new CustomFrame(Types.TERMINAL);

JTextArea log = new JTextArea();
public JTextField field = new JTextField();

public void setVisible(boolean bool) {
    frame.setVisible(bool);
}

public void addKeyListener(KeyListener listener) {
    frame.addKeyListener(listener);
}

public void slowPrint(String str) {
    for(int i = 0; i < str.length(); i++) {
        log.append("" + str.charAt(i));

        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (i == str.length() - 1) {
            log.append("\n");
        }
    }
}

public void setLocation(int x, int y) {
    frame.setLocation(x, y);
}

public void setLocationRelativeTo(Component c) {
    frame.setLocationRelativeTo(c);
}

public LoginTerminal() {
    try {

        InputStream is = LoginTerminal.class.getResourceAsStream("/fonts/dungeon.TTF");
        Font font = Font.createFont(Font.TRUETYPE_FONT, is);
        font = font.deriveFont(Font.PLAIN, 10);

        frame.add(field);
        frame.add(log);

        log.setBackground(Color.BLACK);
        log.setForeground(Color.WHITE);
        log.setWrapStyleWord(true);
        log.setLineWrap(true);
        log.setBounds(4, 20 + 4, frame.getWidth() - 8, frame.getHeight() - 50);
        log.setFont(font);
        log.setEditable(false);
        log.setCaretColor(Color.BLACK);

        field.setBackground(Color.BLACK);
        field.setForeground(Color.WHITE);
        field.setBounds(2, frame.getHeight() - 23, frame.getWidth() - 5, 20);
        field.setFont(font);
        field.setCaretColor(Color.BLACK);
        field.addKeyListener(this);
        field.setText("  >  ");

    } catch (FontFormatException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public void dumpToLog() {
    log.append(field.getText() + "\n");
    field.setText("  >  ");
}

public void keyPressed(KeyEvent e) {
    int i = e.getKeyCode();

    if(i == KeyEvent.VK_ENTER && field.isFocusOwner()) {
        if(field.getText().equals("  >  HELP") || field.getText().equals("  >  help")) {
            dumpToLog();


        } else {
            dumpToLog();
        }
    }


    if(!field.getText().startsWith("  >  ")) {
        field.setText("  >  ");
    }
}

public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}

}

主类

public class Main {

    public static void main(String[] args) {
        new Login();
    }

}

我的问题是:

public Login() {
    login.slowPrint("Please login to continue...\n"
               + "Type 'help' for more information.\n");
}

当我运行它时,它按预期工作。

下面是同一个类(Login.class)

public void keyPressed(KeyEvent e) {
    int i = e.getKeyCode();

    if(i == KeyEvent.VK_ENTER) {
        login.slowPrint("\nCommands\n"
                 + "-----------\n"
                 + "register\n"
                 + "login\n");
    }
}

它卡住并等待完成。

我认为可能是 Thread.sleep(50);在 LoginTerminal.class 中,如标题所述,因为这是触发打字动画的事实。

希望我在这里说清楚了。 感谢大家的帮助!

编辑

这就是导致计时器错误的原因...

public void timerPrint(String text) {
Timer timer = new Timer(50, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        if (index < text.length() - 1 && index >= 0) {
            String newChar = Character.toString(text.charAt(index));
            textArea.append(newChar);
            index++;
        } else {
            textArea.setText(null);
            index = 0;
           // You could stop the timer here...
        }
   }
});
timer.start();
}

构造函数 Timer(int, new ActionListener(){}) 未定义

编辑编辑,全类:

package com.finn.frametypes;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.io.InputStream;

import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.Timer;

import com.finn.gui.CustomFrame;
import com.finn.gui.Types;

public class LoginTerminal implements KeyListener {

CustomFrame frame = new CustomFrame(Types.TERMINAL);

JTextArea log = new JTextArea();
public JTextField field = new JTextField();

public void setVisible(boolean bool) {
    frame.setVisible(bool);
}

public void addKeyListener(KeyListener listener) {
    frame.addKeyListener(listener);
}
public void timerPrint(String text) {
Timer timer = new Timer(50, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        if (index < text.length() - 1 && index >= 0) {
            String newChar = Character.toString(text.charAt(index));
            textArea.append(newChar);
            index++;
        } else {
            textArea.setText(null);
            index = 0;
           // You could stop the timer here...
        }
   }
});
timer.start();
}

public void slowPrint(String str) {
    for(int i = 0; i < str.length(); i++) {
        log.append("" + str.charAt(i));

        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (i == str.length() - 1) {
            log.append("\n");
        }
    }
}

public void setLocation(int x, int y) {
    frame.setLocation(x, y);
}

public void setLocationRelativeTo(Component c) {
    frame.setLocationRelativeTo(c);
}

public LoginTerminal() {
    try {

        InputStream is = LoginTerminal.class.getResourceAsStream("/fonts/dungeon.TTF");
        Font font = Font.createFont(Font.TRUETYPE_FONT, is);
        font = font.deriveFont(Font.PLAIN, 10);

        frame.add(field);
        frame.add(log);

        log.setBackground(Color.BLACK);
        log.setForeground(Color.WHITE);
        log.setWrapStyleWord(true);
        log.setLineWrap(true);
        log.setBounds(4, 20 + 4, frame.getWidth() - 8, frame.getHeight() - 50);
        log.setFont(font);
        log.setEditable(false);
        log.setCaretColor(Color.BLACK);

        field.setBackground(Color.BLACK);
        field.setForeground(Color.WHITE);
        field.setBounds(2, frame.getHeight() - 23, frame.getWidth() - 5, 20);
        field.setFont(font);
        field.setCaretColor(Color.BLACK);
        field.addKeyListener(this);
        field.setText("  >  ");

    } catch (FontFormatException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public void dumpToLog() {
    log.append(field.getText() + "\n");
    field.setText("  >  ");
}

public void keyPressed(KeyEvent e) {
    int i = e.getKeyCode();

    if(i == KeyEvent.VK_ENTER && field.isFocusOwner()) {
        if(field.getText().equals("  >  HELP") || field.getText().equals("  >  help")) {
            dumpToLog();


        } else {
            dumpToLog();
        }
    }


    if(!field.getText().startsWith("  >  ")) {
        field.setText("  >  ");
    }
}

public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}

}

回答

如果以后有人读到这篇文章并想在 Java 中使用慢打印,请使用以下内容:

int index;

public void timerPrint(final String text) {
    Timer timer = new Timer(50, new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            if (index < text.length() - 1 && index >= 0) {
                String newChar = Character.toString(text.charAt(index));
                textarea.append(newChar);
                index++;
            } else {
                index = 0;
                ((Timer)e.getSource()).stop();
            }
       }
    });
    timer.start();
}

最佳答案

Swing 是一个单线程框架,也就是说,任何阻塞事件调度线程的东西都会阻止 UI 更新或让您的程序响应新线程

参见 Concurrency in Swing了解更多详情。

您永远不应在 EDT 中执行任何阻塞 (Thread.sleep) 或长时间运行的进程。

Swing 也不是线程安全的,这意味着您永远不应该从 EDT 上下文之外更新 UI。

这让你陷入了一个难题......

幸运的是,有很多选择。对于您的情况,最简单的解决方案可能是使用 Swing Timer,它可用于安排定期回调到 EDT,您可以在其中执行更新

参见 How to use Swing Timers了解更多详情

例如……

Scrolling Text

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ScrollingText100 {

    public static void main(String[] args) {
        new ScrollingText100();
    }

    public ScrollingText100() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private String text;
        private int index;
        private JTextArea textArea;

        public TestPane() {            
            setLayout(new BorderLayout());
            textArea = new JTextArea(2, 40);
            add(textArea);

            text = "Please login to continue...\n" + "Type 'help' for more information.\n";
            Timer timer = new Timer(50, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (index < text.length() - 1 && index >= 0) {
                        String newChar = Character.toString(text.charAt(index));
                        textArea.append(newChar);
                        index++;
                    } else {
                        textArea.setText(null);
                        index = 0;
                        // You could stop the timer here...
                    }
                }
            });
            timer.start();
        }

    }

}

已更新

如果我正确理解您的要求,像这样...

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JScrollPane;

import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class LoginTerminal {

    private JTextArea log = new JTextArea(20, 40);
    private JTextField field = new JTextField();

    private int index;
    private StringBuilder textToDisplay;
    private Timer timer;

    public static void main(String[] args) {
        new LoginTerminal();
    }

    public LoginTerminal() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                textToDisplay = new StringBuilder(128);
                timer = new Timer(50, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (textToDisplay.length() > 0) {
                            String newChar = Character.toString(textToDisplay.charAt(0));
                            textToDisplay.delete(0, 1);
                            log.append(newChar);
                        } else {
                            ((Timer) e.getSource()).stop();
                        }
                    }
                });

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                frame.add(field, BorderLayout.NORTH);
                frame.add(new JScrollPane(log));

                log.setBackground(Color.BLACK);
                log.setForeground(Color.WHITE);
                log.setWrapStyleWord(true);
                log.setLineWrap(true);
                log.setEditable(false);
                log.setCaretColor(Color.BLACK);

                field.setBackground(Color.BLACK);
                field.setForeground(Color.WHITE);
                field.setCaretColor(Color.BLACK);
                field.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if ("  >  HELP".equalsIgnoreCase(field.getText())) {
                            dumpToLog();
                        } else {
                            dumpToLog();
                        }
                    }
                });
                field.setText("  >  ");

                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public void timerPrint(String text) {

        textToDisplay.append(text).append("\n");
        if (!timer.isRunning()) {
            timer.start();
        }
    }

    public void slowPrint(String str) {
        for (int i = 0; i < str.length(); i++) {
            log.append("" + str.charAt(i));

            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (i == str.length() - 1) {
                log.append("\n");
            }
        }
    }

    public void dumpToLog() {
        timerPrint(field.getText());
    }

}

可能是您真正要找的东西。

注意,KeyListener 不是 JTextComponent 的好选择,在这种情况下,ActionListener 是更好的选择。

关于Java 线程不能与 JTextArea 一起正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26754146/

相关文章:

java - 如何访问java类中WEB-INF文件夹下的文件

c - 显示字符串中每个字符的二进制

android - RxAndroid ViewObservable NetworkOnMainThreadException

java - 通过 final方法参数安全发布?

java - Spring通过枚举来选择bean配置属性

java - XML 中的 TextContent 和属性值之间的区别

java - 如何使用空的soapAction定义调用Web服务?

c++ - 为什么 std::string 不是 std::vector 的特化?

string - 如何在PowerShell中使用运算符 '-replace'将文本字符串替换为特殊字符并成功替换

java - 是否可以有一个程序运行另一个程序(.java 文件)中的代码并自动调试每一行