Search code examples
javaswingchatjscrollpane

JTextArea with JScrollPAne within Frame with null Layout


I'm trying to add this JTextArea with a JScrollPane (with vertical but not horizontal scrollbar) to my frame but the result is just a grey area with a scrollbar on the right... I'm probably doing something really dumb but i've done that same exact thing to a JPanel and it worked

public class Chats {

     public static int height = 600;
     public static int length = 400;

     public static void init(String me, String you){

         JFrame frame = new JFrame ("Chat");
         frame.setSize(larguraframe, alturaframe);
         frame.setLocation(620, 100);
         frame.setResizable(false);
         frame.setLayout(null);
         frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);

         JTextArea chat = new JTextArea();
         chat.setColumns(10);
         chat.setLineWrap(true);
         chat.setWrapStyleWord(true);
         JScrollPane scrollpane = new JScrollPane(chat, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER;  
         scrollpane.setBounds(length/8 - 27, height/9 + 27, 350, 380);
         chat.setPreferredSize(new Dimension(lenght-15, 7*height/8-27));

         frame.add(chat);
         frame.add(scrollpane);
         frame.setVisible(true);
     }

 }

I don't mind changing my frame's Layout but i really want one that allows me to put stuff exactly where i want it. Thanks


EDIT

Okay it now shows on my frame but the TextArea is still not resizable. When i write something in it using a JTextfield and a JButton with a Listener that appends the JTextfield's text to the JTextArea and then sets the text in the JTextField to "" it only accepts up to a certain ammount of lines. After that it just looks the same.


Solution

  • I know that you've already "accepted" an answer, but I feel that I'd be remiss if I didn't give an answer that gave important points, ones that in the long run would allow you to create a better and more robust (i.e., a more easily debuggable, modifiable, and enhanceable) application.

    Again,

    1. Never set a JTextArea's preferredSize, as this will create a JTextArea whose size is inflexibly set that will not add additional lines when needed. Instead set the JTextArea's row and column properties.
    2. 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.
    3. Better to use a JPanel, or more often multiple nested JPanels, each using its own layout manager that allow you to more simply create agile and powerful complex yet beautiful GUI's.
    4. When using layout managers, you'll want to pack() your JFrame after adding all components so that all layout managers will do their jobs and layout components appropriately.

    I've an example program below I show,

    • a title JLabel with large centered text
    • a JTextArea, called chatViewArea, of specified row and column size held within a JScrollPane and that is for viewing the chat. It is non-focusable so that the user cannot directly interact with it.
    • Another JTextArea, called textEntryArea, for entering text. You could use a simple JTextField for this and give it an ActionListener so that it responds to the enter key, however if you want a multi-lined text component that acts similar, you'll need to change the key bindings for the JTextArea's enter key. I've done that here so that the enter key "submits" the text held within the textEntryArea JTextArea, and the control-enter key combination acts the same as the enter key used to -- creating a new line.
    • The main JPanel uses simply a BorderLayout to allow placement of the title at the top, the chat view JTextArea in the center and the text entry JTextArea at the bottom. Note that if you needed to see more components, such as a JList showing other chatters, this can be done by nesting JPanels and using more layouts if need be.

    For example:

    import java.awt.BorderLayout;
    import java.awt.Font;
    import java.awt.event.ActionEvent;
    import java.awt.event.KeyEvent;    
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class Chat2 extends JPanel {
        private static final int ROWS = 25; // rows in the chat view JTextArea
        private static final int COLS = 40; // columns in the chat view JTextArea
                                            // and text entry area
        private static final int ENTRY_ROWS = 4; // rows in the text entry JTextArea
        private static final int BL_HGAP = 10; // horizontal gap for our
                                               // BorderLayout
        private static final int BL_VGAP = 5; // vertical gap for our BorderLayout
        private static final int EB_GAP = 15; // gap for empty border that goes
                                              // around entire app
        private static final String TITLE_TEXT = "My Chat Application";
        private static final float TITLE_POINTS = 32f; // size of the title jlabel
                                                       // text
        private JTextArea chatViewArea = new JTextArea(ROWS, COLS);
        private JTextArea textEntryArea = new JTextArea(ENTRY_ROWS, COLS);
    
        public Chat2() {
            // label to display the title in bold large text
            JLabel titleLabel = new JLabel(TITLE_TEXT, SwingConstants.CENTER);
            titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD, TITLE_POINTS));
    
            // set up the chat view JTextArea to have word wrap
            // and to not be focusable
            chatViewArea.setWrapStyleWord(true);
            chatViewArea.setLineWrap(true);
            chatViewArea.setFocusable(false);
    
            // add it to a JScrollPane, and give the scrollpane vertical scrollbars
            JScrollPane viewScrollPane = new JScrollPane(chatViewArea);
            viewScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
    
            // set up the text entry JTextArea
            textEntryArea.setWrapStyleWord(true);
            textEntryArea.setLineWrap(true);
    
            // key bindings so that control-enter will act as enter and the enter key will "submit"
            // the user input to the chat window and the chat server
            // will allow us to use a multilined text entry area if desired instead
            // of a single lined JTextField
            setEnterKeyBinding(textEntryArea);
    
            JScrollPane entryScrollPane = new JScrollPane(textEntryArea);
            entryScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
    
            // add an empty border around entire application
            setBorder(BorderFactory.createEmptyBorder(EB_GAP, EB_GAP, EB_GAP, EB_GAP));
            // make the main layout a BorderLayout
            setLayout(new BorderLayout(BL_HGAP, BL_VGAP));
            // add our components to the GUI
            add(titleLabel, BorderLayout.PAGE_START);
            add(viewScrollPane, BorderLayout.CENTER);
            add(entryScrollPane, BorderLayout.PAGE_END);
        }
    
        // Again, use key bindings so that control-enter JTextArea will act as enter key
        // and the enter key will "submit" the user input to the chat window and the chat server.
        // When ctrl-enter is pushed the Action originally bound to the enter key will be called
        // and when enter is pushed a new Action, the EnterKeyAction, will be called
        private void setEnterKeyBinding(JTextArea textArea) {
            int condition = JComponent.WHEN_FOCUSED; // only for focused entry key
            InputMap inputMap = textArea.getInputMap(condition);
            ActionMap actionMap = textArea.getActionMap();
    
            KeyStroke entryKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
            KeyStroke ctrlEntryKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_DOWN_MASK);
    
            // first give ctrl-enter the action held by enter
            Object entryKey = inputMap.get(entryKeyStroke);
            Action entryAction = actionMap.get(entryKey);
            inputMap.put(ctrlEntryKeyStroke, ctrlEntryKeyStroke.toString());
            actionMap.put(ctrlEntryKeyStroke.toString(), entryAction);
    
            // now give enter key a new Action
            EnterKeyAction enterKeyAction = new EnterKeyAction();
            inputMap.put(entryKeyStroke, entryKeyStroke.toString());
            actionMap.put(entryKeyStroke.toString(), enterKeyAction);
        }
    
        public void appendToChatArea(final String text) {
            if (SwingUtilities.isEventDispatchThread()) {
                chatViewArea.append(text + "\n");
            } else {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        chatViewArea.append(text + "\n");
                    }
                });
            }
        }
    
        private class EnterKeyAction extends AbstractAction {
            @Override
            public void actionPerformed(ActionEvent e) {
                String text = textEntryArea.getText();
                textEntryArea.setText("");
    
                chatViewArea.append("User: " + text + "\n");
    
                // TODO send text to the chat server!
    
            }
        }
    
        private static void createAndShowGui() {
            Chat2 mainPanel = new Chat2();
    
            JFrame frame = new JFrame("My Chat Window");
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.getContentPane().add(mainPanel);
    
            // pack the JFrame so that it will size itself to its components
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    createAndShowGui();
                }
            });
        }
    }