Search code examples
javaswingjpanelresizejtextarea

JTextArea doesn't resize


I tried to set JTextArea to JPanel as

JPanel panel=new JPanel(new BorderLayout());
JTextArea ta=new JTextArea();
ta.setColumns(20);
ta.setEditable(false);
ta.setLineWrap(true);
ta.setRows(5);
ta.setWrapStyleWord(true);

panel.add(ta,BorderLayout.CENTER);

JPanel panel1=new JPanel();
panel1.setLayout(new VerticalLayout(5,VerticalLayout.BOTH));
panel1.add(panel);

JFrame frame=new JFrame();
frame.getContentPane().add(panel1);
    ...

The thing is... when the panel1 is re-sized wider and then back narrower the JTextArea becomes cut off. I mean its rows do not restore to 5 but hold as 1 and so all its text is in one line , of course, the WrapStyleWord is inactive :S

So my question is how to make the JTextArea restore to its original scale on frame re-sized?

Here is the VerticalLayout code.

...

Well, based on Guillaume Polet snippet, I tried to write some kind of vertical panels list but the previously mentioned problem takes place :( Here is the code

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.util.Hashtable;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

public class TestTextArea {

    private void initUI() {
        JFrame frame = new JFrame("test");


        JPanel listPanel = new JPanel();
        listPanel.setLayout(new VerticalLayout(5, VerticalLayout.BOTH));


        JPanel mainPanel=new JPanel(new GridLayout());
        JScrollPane sp=new JScrollPane();
        sp.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        sp.setViewportView(listPanel);

        mainPanel.add(sp);

        listPanel.add(new MyPanel());
        listPanel.add(new MyPanel());
        listPanel.add(new MyPanel());
        listPanel.add(new MyPanel());
        listPanel.add(new MyPanel());

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(mainPanel);
        frame.pack();
        frame.setVisible(true);
    }

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

            @Override
            public void run() {
                new TestTextArea().initUI();
            }
        });
    }

    class MyPanel extends JPanel
    {
        public MyPanel(){

        this.setLayout(new BorderLayout());

        JTextArea ta = new JTextArea();
        ta.setText("Hello world Hello world Hello world Hello world " + "Hello world Hello world Hello world Hello world "
                + "Hello world Hello world Hello world Hello world " + "Hello world Hello world Hello world Hello world ");
        ta.setColumns(20);
        ta.setEditable(false);
        ta.setLineWrap(true);
        ta.setRows(5);
        ta.setWrapStyleWord(true);

        this.add(ta, BorderLayout.CENTER);


        }

    }

    public static class VerticalLayout implements LayoutManager {

        /**
         * The horizontal alignment constant that designates centering. Also used to designate center anchoring.
         */
        public final static int CENTER = 0;
        /**
         * The horizontal alignment constant that designates right justification.
         */
        public final static int RIGHT = 1;
        /**
         * The horizontal alignment constant that designates left justification.
         */
        public final static int LEFT = 2;
        /**
         * The horizontal alignment constant that designates stretching the component horizontally.
         */
        public final static int BOTH = 3;

        /**
         * The anchoring constant that designates anchoring to the top of the display area
         */
        public final static int TOP = 1;
        /**
         * The anchoring constant that designates anchoring to the bottom of the display area
         */
        public final static int BOTTOM = 2;
        private int vgap; // the vertical vgap between components...defaults to 5
        private int alignment; // LEFT, RIGHT, CENTER or BOTH...how the components are justified
        private int anchor; // TOP, BOTTOM or CENTER ...where are the components positioned in an overlarge space
        private Hashtable comps;

        // Constructors
        /**
         * Constructs an instance of VerticalLayout with a vertical vgap of 5 pixels, horizontal centering and anchored to the top of the
         * display area.
         */
        public VerticalLayout() {
            this(5, CENTER, TOP);
        }

        /**
         * Constructs a VerticalLayout instance with horizontal centering, anchored to the top with the specified vgap
         *
         * @param vgap
         *            An int value indicating the vertical seperation of the components
         */
        public VerticalLayout(int vgap) {
            this(vgap, CENTER, TOP);
        }

        /**
         * Constructs a VerticalLayout instance anchored to the top with the specified vgap and horizontal alignment
         *
         * @param vgap
         *            An int value indicating the vertical seperation of the components
         * @param alignment
         *            An int value which is one of <code>RIGHT, LEFT, CENTER, BOTH</code> for the horizontal alignment.
         */
        public VerticalLayout(int vgap, int alignment) {
            this(vgap, alignment, TOP);
        }

        /**
         * Constructs a VerticalLayout instance with the specified vgap, horizontal alignment and anchoring
         *
         * @param vgap
         *            An int value indicating the vertical seperation of the components
         * @param alignment
         *            An int value which is one of <code>RIGHT, LEFT, CENTER, BOTH</code> for the horizontal alignment.
         * @param anchor
         *            An int value which is one of <code>TOP, BOTTOM, CENTER</code> indicating where the components are to appear if the
         *            display area exceeds the minimum necessary.
         */
        public VerticalLayout(int vgap, int alignment, int anchor) {
            this.vgap = vgap;
            this.alignment = alignment;
            this.anchor = anchor;
        }

        // ----------------------------------------------------------------------------
        private Dimension layoutSize(Container parent, boolean minimum) {
            Dimension dim = new Dimension(0, 0);
            Dimension d;
            synchronized (parent.getTreeLock()) {
                int n = parent.getComponentCount();
                for (int i = 0; i < n; i++) {
                    Component c = parent.getComponent(i);
                    if (c.isVisible()) {
                        d = minimum ? c.getMinimumSize() : c.getPreferredSize();
                        dim.width = Math.max(dim.width, d.width);
                        dim.height += d.height;
                        if (i > 0) {
                            dim.height += vgap;
                        }
                    }
                }
            }
            Insets insets = parent.getInsets();
            dim.width += insets.left + insets.right;
            dim.height += insets.top + insets.bottom + vgap + vgap;
            return dim;
        }

        // -----------------------------------------------------------------------------
        /**
         * Lays out the container.
         */
        @Override
        public void layoutContainer(Container parent) {
            Insets insets = parent.getInsets();
            synchronized (parent.getTreeLock()) {
                int n = parent.getComponentCount();
                Dimension pd = parent.getSize();
                int y = 0;
                // work out the total size
                for (int i = 0; i < n; i++) {
                    Component c = parent.getComponent(i);
                    Dimension d = c.getPreferredSize();
                    y += d.height + vgap;
                }
                y -= vgap; // otherwise there's a vgap too many
                // Work out the anchor paint
                if (anchor == TOP) {
                    y = insets.top;
                } else if (anchor == CENTER) {
                    y = (pd.height - y) / 2;
                } else {
                    y = pd.height - y - insets.bottom;
                }
                // do layout
                for (int i = 0; i < n; i++) {
                    Component c = parent.getComponent(i);
                    Dimension d = c.getPreferredSize();
                    int x = insets.left;
                    int wid = d.width;
                    if (alignment == CENTER) {
                        x = (pd.width - d.width) / 2;
                    } else if (alignment == RIGHT) {
                        x = pd.width - d.width - insets.right;
                    } else if (alignment == BOTH) {
                        wid = pd.width - insets.left - insets.right;
                    }
                    c.setBounds(x, y, wid, d.height);
                    y += d.height + vgap;
                }
            }
        }

        // -----------------------------------------------------------------------------
        @Override
        public Dimension minimumLayoutSize(Container parent) {
            return layoutSize(parent, false);
        }

        // -----------------------------------------------------------------------------
        @Override
        public Dimension preferredLayoutSize(Container parent) {
            return layoutSize(parent, false);
        }

        // ----------------------------------------------------------------------------
        /**
         * Not used by this class
         */
        @Override
        public void addLayoutComponent(String name, Component comp) {
        }

        // -----------------------------------------------------------------------------
        /**
         * Not used by this class
         */
        @Override
        public void removeLayoutComponent(Component comp) {
        }

        // -----------------------------------------------------------------------------
        @Override
        public String toString() {
            return getClass().getName() + "[vgap=" + vgap + " align=" + alignment + " anchor=" + anchor + "]";
        }
    }

}

I am not pretty sure how to make JTextArea come to its original width? Or maybe there is some more optimal way?


Solution

  • It seems like you are having trouble posting an SSCCE, so maybe you can get started from this one, possibly change it, and then edit your question showing us the problem you are having.

    EDIT: (Changed SSCCE according to updated question)

    The problem is that the ViewPortView of the scrollpane does not implements Scrollable. Try the code below with the dedicated class ScrollablePanel which is added as the ViewPortView.

    import java.awt.BorderLayout;
    import java.awt.Component;
    import java.awt.Container;
    import java.awt.Dimension;
    import java.awt.GridLayout;
    import java.awt.Insets;
    import java.awt.LayoutManager;
    import java.awt.Rectangle;
    import java.util.Hashtable;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JScrollBar;
    import javax.swing.JScrollPane;
    import javax.swing.JTextArea;
    import javax.swing.Scrollable;
    import javax.swing.SwingConstants;
    import javax.swing.SwingUtilities;
    
    public class TestTextArea {
    
        private void initUI() {
            JFrame frame = new JFrame("test");
    
            class ScrollablePanel extends JPanel implements Scrollable {
    
                /**
                 * Returns the preferred size of the viewport for a view component. This is implemented to do the default behavior of returning
                 * the preferred size of the component.
                 * 
                 * @return the <code>preferredSize</code> of a <code>JViewport</code> whose view is this <code>Scrollable</code>
                 */
                @Override
                public Dimension getPreferredScrollableViewportSize() {
                    return getPreferredSize();
                }
    
                /**
                 * Components that display logical rows or columns should compute the scroll increment that will completely expose one new row
                 * or column, depending on the value of orientation. Ideally, components should handle a partially exposed row or column by
                 * returning the distance required to completely expose the item.
                 * <p>
                 * The default implementation of this is to simply return 10% of the visible area. Subclasses are likely to be able to provide a
                 * much more reasonable value.
                 * 
                 * @param visibleRect
                 *            the view area visible within the viewport
                 * @param orientation
                 *            either <code>SwingConstants.VERTICAL</code> or <code>SwingConstants.HORIZONTAL</code>
                 * @param direction
                 *            less than zero to scroll up/left, greater than zero for down/right
                 * @return the "unit" increment for scrolling in the specified direction
                 * @exception IllegalArgumentException
                 *                for an invalid orientation
                 * @see JScrollBar#setUnitIncrement
                 */
                @Override
                public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
                    switch (orientation) {
                    case SwingConstants.VERTICAL:
                        return visibleRect.height / 10;
                    case SwingConstants.HORIZONTAL:
                        return visibleRect.width / 10;
                    default:
                        throw new IllegalArgumentException("Invalid orientation: " + orientation);
                    }
                }
    
                /**
                 * Components that display logical rows or columns should compute the scroll increment that will completely expose one block of
                 * rows or columns, depending on the value of orientation.
                 * <p>
                 * The default implementation of this is to simply return the visible area. Subclasses will likely be able to provide a much
                 * more reasonable value.
                 * 
                 * @param visibleRect
                 *            the view area visible within the viewport
                 * @param orientation
                 *            either <code>SwingConstants.VERTICAL</code> or <code>SwingConstants.HORIZONTAL</code>
                 * @param direction
                 *            less than zero to scroll up/left, greater than zero for down/right
                 * @return the "block" increment for scrolling in the specified direction
                 * @exception IllegalArgumentException
                 *                for an invalid orientation
                 * @see JScrollBar#setBlockIncrement
                 */
                @Override
                public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
                    switch (orientation) {
                    case SwingConstants.VERTICAL:
                        return visibleRect.height;
                    case SwingConstants.HORIZONTAL:
                        return visibleRect.width;
                    default:
                        throw new IllegalArgumentException("Invalid orientation: " + orientation);
                    }
                }
    
                /**
                 * Returns true if a viewport should always force the width of this <code>Scrollable</code> to match the width of the viewport.
                 * For example a normal text view that supported line wrapping would return true here, since it would be undesirable for wrapped
                 * lines to disappear beyond the right edge of the viewport. Note that returning true for a <code>Scrollable</code> whose
                 * ancestor is a <code>JScrollPane</code> effectively disables horizontal scrolling.
                 * <p>
                 * Scrolling containers, like <code>JViewport</code>, will use this method each time they are validated.
                 * 
                 * @return true if a viewport should force the <code>Scrollable</code>s width to match its own
                 */
                @Override
                public boolean getScrollableTracksViewportWidth() {
                    return true;
                }
    
                /**
                 * Returns true if a viewport should always force the height of this <code>Scrollable</code> to match the height of the
                 * viewport. For example a columnar text view that flowed text in left to right columns could effectively disable vertical
                 * scrolling by returning true here.
                 * <p>
                 * Scrolling containers, like <code>JViewport</code>, will use this method each time they are validated.
                 * 
                 * @return true if a viewport should force the Scrollables height to match its own
                 */
                @Override
                public boolean getScrollableTracksViewportHeight() {
                    return false;
                }
    
            }
    
            JPanel listPanel = new ScrollablePanel();
            listPanel.setLayout(new VerticalLayout(5, VerticalLayout.BOTH));
    
            JPanel mainPanel = new JPanel(new GridLayout());
            JScrollPane sp = new JScrollPane();
            sp.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
            sp.setViewportView(listPanel);
    
            mainPanel.add(sp);
    
            listPanel.add(new MyPanel());
            listPanel.add(new MyPanel());
            listPanel.add(new MyPanel());
            listPanel.add(new MyPanel());
            listPanel.add(new MyPanel());
    
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(mainPanel);
            frame.pack();
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    new TestTextArea().initUI();
                }
            });
        }
    
        class MyPanel extends JPanel {
            public MyPanel() {
    
                this.setLayout(new BorderLayout());
    
                JTextArea ta = new JTextArea();
                ta.setText("Hello world Hello world Hello world Hello world " + "Hello world Hello world Hello world Hello world "
                        + "Hello world Hello world Hello world Hello world " + "Hello world Hello world Hello world Hello world ");
                ta.setColumns(20);
                ta.setEditable(false);
                ta.setLineWrap(true);
                ta.setRows(5);
                ta.setWrapStyleWord(true);
    
                this.add(ta, BorderLayout.CENTER);
    
            }
    
        }
    
        public static class VerticalLayout implements LayoutManager {
    
            /**
             * The horizontal alignment constant that designates centering. Also used to designate center anchoring.
             */
            public final static int CENTER = 0;
            /**
             * The horizontal alignment constant that designates right justification.
             */
            public final static int RIGHT = 1;
            /**
             * The horizontal alignment constant that designates left justification.
             */
            public final static int LEFT = 2;
            /**
             * The horizontal alignment constant that designates stretching the component horizontally.
             */
            public final static int BOTH = 3;
    
            /**
             * The anchoring constant that designates anchoring to the top of the display area
             */
            public final static int TOP = 1;
            /**
             * The anchoring constant that designates anchoring to the bottom of the display area
             */
            public final static int BOTTOM = 2;
            private int vgap; // the vertical vgap between components...defaults to 5
            private int alignment; // LEFT, RIGHT, CENTER or BOTH...how the components are justified
            private int anchor; // TOP, BOTTOM or CENTER ...where are the components positioned in an overlarge space
            private Hashtable comps;
    
            // Constructors
            /**
             * Constructs an instance of VerticalLayout with a vertical vgap of 5 pixels, horizontal centering and anchored to the top of the
             * display area.
             */
            public VerticalLayout() {
                this(5, CENTER, TOP);
            }
    
            /**
             * Constructs a VerticalLayout instance with horizontal centering, anchored to the top with the specified vgap
             * 
             * @param vgap
             *            An int value indicating the vertical seperation of the components
             */
            public VerticalLayout(int vgap) {
                this(vgap, CENTER, TOP);
            }
    
            /**
             * Constructs a VerticalLayout instance anchored to the top with the specified vgap and horizontal alignment
             * 
             * @param vgap
             *            An int value indicating the vertical seperation of the components
             * @param alignment
             *            An int value which is one of <code>RIGHT, LEFT, CENTER, BOTH</code> for the horizontal alignment.
             */
            public VerticalLayout(int vgap, int alignment) {
                this(vgap, alignment, TOP);
            }
    
            /**
             * Constructs a VerticalLayout instance with the specified vgap, horizontal alignment and anchoring
             * 
             * @param vgap
             *            An int value indicating the vertical seperation of the components
             * @param alignment
             *            An int value which is one of <code>RIGHT, LEFT, CENTER, BOTH</code> for the horizontal alignment.
             * @param anchor
             *            An int value which is one of <code>TOP, BOTTOM, CENTER</code> indicating where the components are to appear if the
             *            display area exceeds the minimum necessary.
             */
            public VerticalLayout(int vgap, int alignment, int anchor) {
                this.vgap = vgap;
                this.alignment = alignment;
                this.anchor = anchor;
            }
    
            // ----------------------------------------------------------------------------
            private Dimension layoutSize(Container parent, boolean minimum) {
                Dimension dim = new Dimension(0, 0);
                Dimension d;
                synchronized (parent.getTreeLock()) {
                    int n = parent.getComponentCount();
                    for (int i = 0; i < n; i++) {
                        Component c = parent.getComponent(i);
                        if (c.isVisible()) {
                            d = minimum ? c.getMinimumSize() : c.getPreferredSize();
                            dim.width = Math.max(dim.width, d.width);
                            dim.height += d.height;
                            if (i > 0) {
                                dim.height += vgap;
                            }
                        }
                    }
                }
                Insets insets = parent.getInsets();
                dim.width += insets.left + insets.right;
                dim.height += insets.top + insets.bottom + vgap + vgap;
                return dim;
            }
    
            // -----------------------------------------------------------------------------
            /**
             * Lays out the container.
             */
            @Override
            public void layoutContainer(Container parent) {
                Insets insets = parent.getInsets();
                synchronized (parent.getTreeLock()) {
                    int n = parent.getComponentCount();
                    Dimension pd = parent.getSize();
                    int y = 0;
                    // work out the total size
                    for (int i = 0; i < n; i++) {
                        Component c = parent.getComponent(i);
                        Dimension d = c.getPreferredSize();
                        y += d.height + vgap;
                    }
                    y -= vgap; // otherwise there's a vgap too many
                    // Work out the anchor paint
                    if (anchor == TOP) {
                        y = insets.top;
                    } else if (anchor == CENTER) {
                        y = (pd.height - y) / 2;
                    } else {
                        y = pd.height - y - insets.bottom;
                    }
                    // do layout
                    for (int i = 0; i < n; i++) {
                        Component c = parent.getComponent(i);
                        Dimension d = c.getPreferredSize();
                        int x = insets.left;
                        int wid = d.width;
                        if (alignment == CENTER) {
                            x = (pd.width - d.width) / 2;
                        } else if (alignment == RIGHT) {
                            x = pd.width - d.width - insets.right;
                        } else if (alignment == BOTH) {
                            wid = pd.width - insets.left - insets.right;
                        }
                        c.setBounds(x, y, wid, d.height);
                        y += d.height + vgap;
                    }
                }
            }
    
            // -----------------------------------------------------------------------------
            @Override
            public Dimension minimumLayoutSize(Container parent) {
                return layoutSize(parent, false);
            }
    
            // -----------------------------------------------------------------------------
            @Override
            public Dimension preferredLayoutSize(Container parent) {
                return layoutSize(parent, false);
            }
    
            // ----------------------------------------------------------------------------
            /**
             * Not used by this class
             */
            @Override
            public void addLayoutComponent(String name, Component comp) {
            }
    
            // -----------------------------------------------------------------------------
            /**
             * Not used by this class
             */
            @Override
            public void removeLayoutComponent(Component comp) {
            }
    
            // -----------------------------------------------------------------------------
            @Override
            public String toString() {
                return getClass().getName() + "[vgap=" + vgap + " align=" + alignment + " anchor=" + anchor + "]";
            }
        }
    
    }