Search code examples
javaswingjscrollpanejtextarea

JScrollPane is not added to JTextArea


I saw a few questions like this question, but I couldn't get this solved. I cannot get a JScrollPane visible on JTextArea. Can anyone please point out where I have done my mistake? Thanks.

package experiement;

import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class Experiment extends JFrame{

   public Experiment(){
   JTextArea tarea=new JTextArea();
   tarea.setBounds(100,100,200,200);
   JScrollPane pan= new JScrollPane();
   pan.setPreferredSize(new Dimension(100,100));
   pan=new      JScrollPane(tarea,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

   add(pan);
   add(tarea);

    setLayout(null);
    setSize(600,600);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setVisible(true);
}

public static void main(String[]aegs){
    Experiment e=new Experiment();
}
}

Solution

  • Problems with your code:

    • You constrain the size of your JTextArea by setting its bounds. Whenever you do this, either with setBounds, setSize, or setPreferredSize, you make the JTextArea's size rigid and so it won't expand if text is added to it that is larger than this size. Doing this will usually result in the containing JScrollPane from showing scrollbars when needed since the JTextArea won't expand when needed.
    • You're using null layouts. While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
    • You're adding the JTextArea to two containers, the GUI and the JScrollPane, and this is not allowed in Swing GUI's.

    Instead:

    • Constrain the viewed size of the JTextArea by setting its rows and columns properties, something easiest to achieve by passing these into the JTextArea's two int constructor.
    • Use nested JPanels, each with its own layout to achieve complex, yet flexible and attractive GUI's.
    • Add your JTextArea to the JScrollPane's viewport only, and then the JScrollPane to the GUI.

    For example say you wanted a JTextArea inside of a JScrollPane within the center of your GUI, with buttons on top and a JTextField and a submit button below, say a typical chat window type application, you could make the overall layout a BorderLayout, add a GridLayout-using JPanel with buttons to the top, a BoxLayout using JPanel with the JTextField and submit button to the bottom, and the JScrollPane holding the JTextArea to the center. It could look like this:

    enter image description here

    and the code could look like this:

    import java.awt.BorderLayout;
    import java.awt.GridLayout;
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class Experiment2 extends JPanel {
        private static final int ROWS = 20;
        private static final int COLUMNS = 50;
        private static final int GAP = 3;
        // create the JTextArea, setting its rows and columns properties
        private JTextArea tarea = new JTextArea(ROWS, COLUMNS);
        private JTextField textField = new JTextField(COLUMNS);
    
        public Experiment2() {
            // create the JScrollPane and pass in the JTextArea
            JScrollPane scrollPane = new JScrollPane(tarea);
    
            // let's create another JPanel to hold some buttons
            JPanel buttonPanel = new JPanel(new GridLayout(1, 0, GAP, 0));
            buttonPanel.add(new JButton("Save"));
            buttonPanel.add(new JButton("Load"));
            buttonPanel.add(new JButton("Whatever"));
            buttonPanel.add(new JButton("Exit"));
    
            // create JPanel for the bottom with JTextField and a button
            JPanel bottomPanel = new JPanel();
            bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.LINE_AXIS));
            bottomPanel.add(textField);
            bottomPanel.add(Box.createHorizontalStrut(GAP));
            bottomPanel.add(new JButton("Submit"));
    
            setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
            // use BorderLayout to add all together
            setLayout(new BorderLayout(GAP, GAP));
            add(scrollPane, BorderLayout.CENTER);  // add scroll pane to the center
            add(buttonPanel, BorderLayout.PAGE_START);  // and the button panel to the top
            add(bottomPanel, BorderLayout.PAGE_END);
        }
    
        private static void createAndShowGui() {
            Experiment2 mainPanel = new Experiment2();
    
            JFrame frame = new JFrame("Experiment 2");
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.getContentPane().add(mainPanel);
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> createAndShowGui());
        }
    }
    


    EDIT

    Rather than guess what works and what doesn't, let's experiment and create a GUI that holds two JTextAreas, one where the columns and rows property is set and held by the colRowTextArea variable, and another where the preferred size of the JTextArea is set, and call its variable the prefSizeTextArea.

    We'll create a method, setUpTextArea(...) where we place the JTextArea into a JScrollPane, place this into a JPanel, and have a button that adds lots of text into the JTextArea, and see what happens with the behavior of the JTextArea when text is added.

    Here's the code and push the buttons and see for yourself which one scrolls:

    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.GridLayout;
    import java.awt.event.ActionEvent;
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class TwoTextAreas extends JPanel {
        // our nonsense String
        public static final String LoremIpsum = "Lorem ipsum dolor sit amet, "
                + "consectetur adipiscing elit, sed do eiusmod tempor incididunt "
                + "ut labore et dolore magna aliqua. Ut enim ad minim veniam, "
                + "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
                + "commodo consequat. Duis aute irure dolor in reprehenderit in "
                + "voluptate velit esse cillum dolore eu fugiat nulla pariatur. "
                + "Excepteur sint occaecat cupidatat non proident, sunt in culpa "
                + "qui officia deserunt mollit anim id est laborum.";
        private static final int ROWS = 30;
        private static final int COLS = 40;
        private static final Dimension TA_PREF_SIZE = new Dimension(440, 480);
        private JTextArea colRowTextArea = new JTextArea(ROWS, COLS);
        private JTextArea prefSizeTextArea = new JTextArea();
    
        public TwoTextAreas() {
            setLayout(new GridLayout(1, 0));
            prefSizeTextArea.setPreferredSize(TA_PREF_SIZE);
    
            add(setUpTextArea(colRowTextArea, "Set Columns & Rows"));
            add(setUpTextArea(prefSizeTextArea, "Set Preferred Size"));
    
        }
    
        private JPanel setUpTextArea(JTextArea textArea, String title) {
            // allow word wrapping
            textArea.setLineWrap(true);
            textArea.setWrapStyleWord(true);
    
            JScrollPane scrollPane = new JScrollPane(textArea);
    
            JPanel buttonPanel = new JPanel();
            buttonPanel.add(new JButton(new AppendTextAction(textArea)));
    
            JPanel holderPanel = new JPanel(new BorderLayout());
            holderPanel.setBorder(BorderFactory.createTitledBorder(title));
            holderPanel.add(scrollPane);
            holderPanel.add(buttonPanel, BorderLayout.PAGE_END);
            return holderPanel;
        }
    
        private class AppendTextAction extends AbstractAction {
            private JTextArea textArea;
            private StringBuilder sb = new StringBuilder();
    
            public AppendTextAction(JTextArea textArea) {
                super("Append Text to TextArea");
                this.textArea = textArea;
    
                // create nonsense String
                for (int i = 0; i < 100; i++) {
                    sb.append(LoremIpsum);
                    sb.append("\n");
                }
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                textArea.append(sb.toString());
            }
        }
    
        private static void createAndShowGui() {
            JFrame frame = new JFrame("Two TextAreas");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(new TwoTextAreas());
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> createAndShowGui());
        }
    }