如何手动更新和滚动 JScrollPane

How to manually update & scroll JScrollPane

我在让我的 JScrollPane 在动作侦听器中自动更新和滚动时遇到了一些麻烦。有一个字母动画出现在 Thread.sleep() 的类型中,但是一旦滚动条需要滚动(它在动作侦听器之后滚动)就不起作用。我想知道是否有人可以在评论中帮助我 //Need jsp (JScrollPane) to imstantly update

谢谢,非常感谢您的帮助

public class MainGUI {

public String appName = "Chat Assistant v1.3.3";
public MainGUI mainGUI;
public JPanel mainPanel;
public JScrollPane jsp;
public JFrame newFrame = new JFrame(appName);
public JButton sendMessage;
public JTextField messageBox = new JTextField(30);
public JTextArea chatBox;
String username = "Evan";
public Random rand = new Random();
public Calendar cal= Calendar.getInstance();

//public MainEngine me = new MainEngine();

public String temp = "";
public String tempL = "";

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (Exception e) {
                e.printStackTrace();
            }
            MainGUI mainGUI = new MainGUI();
            mainGUI.display();
        }
    });
}

public void display() {
    mainPanel = new JPanel();
    mainPanel.setLayout(new BorderLayout());

    JPanel southPanel = new JPanel();
    southPanel.setBackground(Color.BLUE);
    southPanel.setLayout(new GridBagLayout());

    messageBox.requestFocusInWindow();

    sendMessage = new JButton("Send Message");
    sendMessage.addActionListener(new sendMessageButtonListener());
    chatBox = new JTextArea();
    chatBox.setEditable(false);
    chatBox.setFont(new Font("Arial", Font.PLAIN, 18));
    chatBox.setLineWrap(true);
    jsp = new JScrollPane(chatBox);
    jsp.setBorder(new LineBorder(Color.white, 7));

    mainPanel.add(jsp, BorderLayout.CENTER);

    GridBagConstraints left = new GridBagConstraints();
    left.anchor = GridBagConstraints.LINE_START;
    left.fill = GridBagConstraints.HORIZONTAL;
    left.weightx = 512.0D;
    left.weighty = 1.0D;

    GridBagConstraints right = new GridBagConstraints();
    right.insets = new Insets(0, 10, 0, 0);
    right.anchor = GridBagConstraints.LINE_END;
    right.fill = GridBagConstraints.NONE;
    right.weightx = 1.0D;
    right.weighty = 1.0D;

    southPanel.add(messageBox, left);
    southPanel.add(sendMessage, right);

    mainPanel.add(BorderLayout.SOUTH, southPanel);

    newFrame.add(mainPanel);
    newFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    newFrame.setSize(720, 480);
    newFrame.setVisible(true);
    newFrame.setResizable(false);
    newFrame.setLocationRelativeTo(null);
    messageBox.requestFocusInWindow();
    messageBox.addKeyListener(new KeyListener());

    startup();
}

public void startup() {
    int h = cal.get(Calendar.HOUR_OF_DAY);
    int n = rand.nextInt(2) + 1;
    String message = "";
    chatBox.append("AIBot:  ");
    if (n == 1)
        message = "Welcome back sir!";
    else if ( n == 2) {
        if ((h > 4) && (h < 11)) 
            message = "Good Morning sir, I hope you have a great day.";
        else if ((h >= 11) && (h < 17))
            message = "Good Afternoon sir";
        else if ((h >= 17) && (h < 25))
            message = "Good Evening sir, how was your day?";
        else
            message = "It's quite late, you should get some rest sir";
    }
    try {
        Runtime.getRuntime().exec( new String[] { "say" , "" + message }) ;
    } catch (IOException e) {
        e.printStackTrace();
    }
    messageBox.paintImmediately(messageBox.getBounds());
    sendMessage.paintImmediately(sendMessage.getBounds());
    messageBox.requestFocusInWindow();
    for (int i = 0; i < message.length(); i++) { //Appends 1 letter at a time, "animation", voice is already executed
        try {Thread.sleep(35);} catch (InterruptedException e) {e.printStackTrace();}
        chatBox.append(message.substring(i, i+1));
        chatBox.setCaretPosition(chatBox.getDocument().getLength());
        chatBox.paintImmediately(chatBox.getBounds ());
    }
    chatBox.append("\n\n");
    messageBox.setText("");
}

public class KeyListener extends KeyAdapter {
    @Override
        public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
            sendMessage.doClick();
        }
    }
}

public class sendMessageButtonListener implements ActionListener {
    public void actionPerformed(ActionEvent event) {
        if (messageBox.getText().length() < 1) {
            // do nothing
        } else if (messageBox.getText().equals(".clear")) {
            chatBox.setText("Cleared all messages\n"); 
            messageBox.setText("");
        } else {
            chatBox.append("" + username + ":  ");
            chatBox.append(messageBox.getText() + "\n\n");
            temp = messageBox.getText();
            tempL = temp.toLowerCase();
            messageBox.setText("");
            chatBox.setCaretPosition(chatBox.getDocument().getLength());
            chatBox.paintImmediately(chatBox.getBounds ());
        }
        messageBox.requestFocusInWindow();
        chatBox.append("AIBot:  ");
        //String message = me.disperse(tempL) + " ";
        String message = "TEST................";
        if (message.contains("username")) {
            String[] t = message.split("username");
            message = t[0] + username + t[1];
        }
        chatBox.setCaretPosition(chatBox.getDocument().getLength());
        chatBox.paintImmediately(chatBox.getBounds());
        //Need jsp (JScrollPane) to instantly update
        message += "";
        try {
            Runtime.getRuntime().exec( new String[] { "say" , "" + message }) ;
        } catch (IOException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < message.length(); i++) { //Appends 1 letter at a time, "animation", voice is already executed
            try {Thread.sleep(35);} catch (InterruptedException e) {e.printStackTrace();}
            chatBox.append(message.substring(i, i+1));
            chatBox.paintImmediately(chatBox.getBounds());
            chatBox.setCaretPosition(chatBox.getDocument().getLength());
        }
        chatBox.append("\n\n");

        chatBox.setCaretPosition(chatBox.getDocument().getLength());
    }
}
}

不要使用 Thread.sleep(...)。这会导致事件调度线程 (EDT) 休眠,这意味着 GUI 在所有代码执行完毕之前无法重新绘制自身。阅读 Concurrency in Swing 上的 Swing 教程部分了解更多信息。

相反,您可以使用 SwingTimer 来安排动画。

或者,如果您不想使用定时器,您可以使用 SwingWorker,如并发教程中所述。

还有:

  1. 不要在文本字段上使用 KeyListener 来处理 Enter 键。您只需将 ActionListener 添加到文本字段即可。按下回车键时会自动调用监听器。

  2. 不要立即使用绘画。一旦你摆脱了 Thread.sleep(),GUI 将正常重绘自己。