Auto-scroll JTextArea with ScrollPane?

autoscroll, java, jscrollpane, jtextarea, swing

Solution

Never do this:

actionLog.setPreferredSize(new Dimension(500, 300));

Since by doing this you artificially restrict the size of the JTextArea causing the effect that is currently vexing you. Note also that it's generally a good idea to avoid setting preferred sizes on anything.

Instead set the column and row counts of the JTextARea. This can be done via setter methods or via a simple constructor call: `JTextArea myTextArea = new JTextArea(rows, columns);`

As an aside: I wonder if a JList will work better for you.

MCVE Example:

import javax.swing.*;
import javax.swing.text.DefaultCaret;

public class Main2 {

   private static void createAndShowGUI() {
      JPanel mainPanel = new ControlPanel();

      JFrame frame = new JFrame("Main2");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

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

class ControlPanel extends JPanel {
   private static final int LOG_ROWS = 15;
   private static final int LOG_COLS = 40;
   private JButton resetButton = new JButton("Reset");
   private JPanel logPanel = new JPanel();
   private JLabel actionLogsLabel = new JLabel("Action Log");
   private JLabel pointsLogsLabel = new JLabel("Points Log");
   private JTextArea actionLog = new JTextArea();
   private JTextArea pointsLog = new JTextArea();
   private JScrollPane actionScroll;
   private JScrollPane pointsScroll;

   public ControlPanel() {
      init();
      this.add(resetButton);
      this.add(logPanel);
   }

   private void init() {
      this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
      this.setAlignmentX(LEFT_ALIGNMENT);
      this.logPanel.setLayout(new BoxLayout(logPanel, BoxLayout.Y_AXIS));
      this.logPanel.setAlignmentX(LEFT_ALIGNMENT);
      // !! actionLog.setPreferredSize(new Dimension(500, 300));
      // !! actionLog.setMaximumSize(actionLog.getPreferredSize());
      actionLog.setRows(LOG_ROWS); // !!
      actionLog.setColumns(LOG_COLS); // !!

      actionLog.setEditable(false);
      actionLog.setWrapStyleWord(true);
      DefaultCaret caret = (DefaultCaret) actionLog.getCaret();
      caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
      // !! pointsLog.setPreferredSize(new Dimension(500, 300));
      // !! pointsLog.setMaximumSize(pointsLog.getPreferredSize());
      pointsLog.setRows(LOG_ROWS); // !!
      pointsLog.setColumns(LOG_COLS); // !!

      pointsLog.setEditable(false);
      pointsLog.setWrapStyleWord(true);
      pointsScroll = new JScrollPane(pointsLog,
            JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
            JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
      actionScroll = new JScrollPane(actionLog,
            JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
            JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
      logPanel.add(actionLogsLabel);
      logPanel.add(actionScroll);
      for (int i = 0; i < 500; i++) {
         actionLog.setText(actionLog.getText() + "Line: " + i + "\n");
      }
      logPanel.add(pointsLogsLabel);
      logPanel.add(pointsScroll);
   }
}

Edit Example with nested layouts and JLists:

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

import javax.swing.*;

public class Main2B {

   private static void createAndShowGUI() {
      ControlPanel2B controlPanel = new ControlPanel2B();
      controlPanel.setBorder(BorderFactory.createEtchedBorder());

      JPanel mainPanel = new JPanel(new GridBagLayout());
      GridBagConstraints gbc = new GridBagConstraints();
      mainPanel.add(controlPanel, gbc);

      JFrame frame = new JFrame("Main2");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

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

@SuppressWarnings("serial")
class ControlPanel2B extends JPanel {
   private static final int LOG_ROWS = 15;
   private static final int LIST_WIDTH = 500;
   private JButton resetButton = new JButton("Reset");
   private JPanel logPanel = new JPanel();
   private JLabel actionLogsLabel = new JLabel("Action Log");
   private JLabel pointsLogsLabel = new JLabel("Points Log");

   private DefaultListModel<String> actionLogListModel = new DefaultListModel<>();
   private JList<String> actionLogList = new JList<String>(actionLogListModel);
   private DefaultListModel<String> pointsLogListModel = new DefaultListModel<>();
   private JList<String> pointsLogList = new JList<String>(pointsLogListModel);
   private JScrollPane actionScroll;
   private JScrollPane pointsScroll;

   public ControlPanel2B() {
      init();
      this.add(resetButton);
      this.add(logPanel);
   }

   private void init() {
      actionLogList.setVisibleRowCount(LOG_ROWS);
      pointsLogList.setVisibleRowCount(LOG_ROWS);
      actionLogList.setFixedCellWidth(LIST_WIDTH);

      this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
      this.setAlignmentX(LEFT_ALIGNMENT);
      this.logPanel.setLayout(new BoxLayout(logPanel, BoxLayout.Y_AXIS));
      this.logPanel.setAlignmentX(LEFT_ALIGNMENT);
      pointsScroll = new JScrollPane(pointsLogList,
            JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
            JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
      actionScroll = new JScrollPane(actionLogList,
            JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
            JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
      logPanel.add(actionLogsLabel);
      logPanel.add(actionScroll);
      for (int i = 0; i < 500; i++) {
         actionLogListModel.addElement("Line: " + i);
      }
      logPanel.add(pointsLogsLabel);
      logPanel.add(pointsScroll);
   }
}

Problem

I'm trying to build a dynamic log window (basically a auto-scrolling jtext-area). The problem I'm having is that although I'm printing 500 lines in the text area, it displays as below: Below you have my code: ``` import java.awt.Dimension; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.text.DefaultCaret; public class Main { private static JFrame mainFrame; public static void main(String args[]) { mainFrame = new JFrame(); mainFrame.setSize(500, 500); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ControlPanel cp = new ControlPanel(); mainFrame.add(cp); mainFrame.setVisible(true); } } class ControlPanel extends JPanel { private JButton resetButton = new JButton("Reset"); private JPanel logPanel = new JPanel(); private JLabel actionLogsLabel = new JLabel("Action Log"); private JLabel pointsLogsLabel = new JLabel("Points Log"); private JTextArea actionLog = new JTextArea(); private JTextArea pointsLog = new JTextArea(); private JScrollPane actionScroll; private JScrollPane pointsScroll; public ControlPanel() { init(); this.add(resetButton); this.add(logPanel); } private void init() { this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); this.setAlignmentX(LEFT_ALIGNMENT); this.logPanel.setLayout(new BoxLayout(logPanel, BoxLayout.Y_AXIS)); this.logPanel.setAlignmentX(LEFT_ALIGNMENT); actionLog.setPreferredSize(new Dimension(500, 300)); actionLog.setMaximumSize(actionLog.getPreferredSize()); actionLog.setEditable(false); actionLog.setWrapStyleWord(true); DefaultCaret caret = (DefaultCaret) actionLog.getCaret(); caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); pointsLog.setPreferredSize(new Dimension(500, 300)); pointsLog.setMaximumSize(pointsLog.getPreferredSize()); pointsLog.setEditable(false); pointsLog.setWrapStyleWord(true); pointsScroll = new JScrollPane(pointsLog, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); actionScroll = new JScrollPane(actionLog, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); logPanel.add(actionLogsLabel); logPanel.add(actionScroll); for(int i = 0; i < 500; i++) { actionLog.setText(actionLog.getText() + "Line: " + i + "\n"); } logPanel.add(pointsLogsLabel); logPanel.add(pointsScroll); } } ``` Hopefully someone with a bit more Swing experience can take the time to point me the right way with this.

Original source