I'm having a bit of trouble getting my JScrollPane to auto-update and scroll within an action listener. There is an an animation of letters appearing one at a type with Thread.sleep(), however doesn't work once the scrollbar needs to scroll (it scrolls after the action listener). I was wondering if anyone could help me out at the comment //Need jsp (JScrollPane) to instantly update
Thanks, would really appreciate some help
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());
}
}
}
Don't use Thread.sleep(...)
. This is causing the Event Dispatch Thread (EDT) to sleep which means the GUI can't repaint itself until all the code has finished executing. Read the section from the Swing tutorial on Concurrency in Swing for more information.
Instead you can use a SwingTimer to schedule the animation.
Or if you don't want to use the Timer you could use a SwingWorker
as discussed in the tutorial on concurrency.
Also:
Don't use a KeyListener on the text field to handle the Enter key. You can just add an ActionListener to the text field. The listener will automatically be invoked when the Enter key is pressed.
Don't use paintImmediately. Once you get rid of the Thread.sleep(), the GUI will repaint itself normally.