4

I have a form with lots of text fields and some of those text fields may contain very long strings. To make it work I made those text fields scrollable using this code:

JScrollPane scroll = new JScrollPane(textField);
scroll.setPreferredSize(new Dimension((int)textField.getPreferredSize().getWidth(), (int)textField.getPreferredSize().getHeight() * 2));

Then I put scroll into my form using GridBagLayout.

Second line in my example is required for scroller to show up. But it has downside. When I resize window to fit whole text in text field, then scroll disapears leaving me with just two times higher then others text field, which looks ridiculous. How can I make this all work and show me normal size of text field after scroller is hidden?

EDIT:

You may use following as a demo code to reproduce the issue:

import javax.swing.*;
import java.awt.*;

public class ScrollTextDemo extends JFrame{

public  ScrollTextDemo(){
    super();
    this.setPreferredSize(new Dimension(500, 300));
    JPanel panel = new JPanel();
    panel.setLayout(new GridBagLayout());
    JTextField textField = new JTextField("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
    textField.setCursor(new Cursor(0));
    textField.setEditable(false);
    JScrollPane scroll = new JScrollPane(textField);
    scroll.setPreferredSize(new Dimension(70, 40) );
    GridBagConstraints gbc = new GridBagConstraints();
    gbc.gridx = 1;
    gbc.gridy = 1;
    gbc.fill = GridBagConstraints.HORIZONTAL;
    gbc.weightx = 0.5;
    gbc.insets = new Insets(5, 5, 0, 5);
    panel.add(scroll,gbc);
    //let's add one more text field without scroll bar to compare
    JTextField textField2 = new JTextField("abc");
    gbc = new GridBagConstraints();
    gbc.gridx = 1;
    gbc.gridy = 2;
    gbc.fill = GridBagConstraints.HORIZONTAL;
    gbc.weightx = 0.5;
    gbc.insets = new Insets(5, 5, 0, 5);
    panel.add(textField2,gbc);
    this.add(panel);
} 

public static void main(String args[]){
    ScrollTextDemo demo = new ScrollTextDemo();
    demo.pack();
    demo.setVisible(true);
}
}
GrayR
  • 1,225
  • 2
  • 18
  • 32
  • How do the surroundings look? I.e. in what container with what layoutmanager do you put the JScrollPane? – Hauke Ingmar Schmidt Feb 29 '12 at 01:43
  • JPanel which uses GridBagLayout. – GrayR Feb 29 '12 at 01:45
  • Oh, sorry, it was there. You need to show us the layout configuration as this determines sizes and locations of components. – Hauke Ingmar Schmidt Feb 29 '12 at 01:48
  • 2
    @GrayR Have you considered always displaying the scrollbar ? You can do that by passing JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS in the JScrollBarPane constructor. Is this an acceptable trade-off ? – Jerome Feb 29 '12 at 02:32
  • *"To make it work I made those text fields scrollable"* Text fields are scrollable by default. – Andrew Thompson Feb 29 '12 at 04:13
  • @his: I've added some sample code to reproduce the issue. – GrayR Feb 29 '12 at 08:11
  • @Andrew Thompson : yes it is scrollable, sorry English is not my native language. What I actually meant is "... to make text field displayed with scroll bar ...". Still, other people seem to understand me here. – GrayR Feb 29 '12 at 08:17
  • @Jerome: thank you, nice idea. I belive it can be done if everything else fails, still I hope there is another way. Anyway, now I have a backup plan :), thank you. – GrayR Feb 29 '12 at 08:22
  • +1 for the code. Code speaks louder than mere words. :) – Andrew Thompson Feb 29 '12 at 08:24
  • @GrayR nice question(after your edit), nice answer both +1 – mKorbel Feb 29 '12 at 09:06

2 Answers2

4

For this , in the absence of a good SSCCE, I think you hadn't provided any constraint that goes for fill, which is used for

Used when the component's display area is larger than the component's requested size to determine whether and how to resize the component. Valid values (defined as GridBagConstraints constants) include NONE (the default), HORIZONTAL (make the component wide enough to fill its display area horizontally, but do not change its height), VERTICAL (make the component tall enough to fill its display area vertically, but do not change its width), and BOTH (make the component fill its display area entirely).

So you must add something like this to your GridBagConstraints

constraintsGridBag.fill = GridBagConstraints.HORIZONTAL;

This will only allow it to expand HORIZONTALLY not both ways.

** EDIT : As for the added code **

Never specify setPreferredSize(...) for any component in Swing. Let the Layout Manager you are using, take care for that. Remove all setPreferredSize(...) thingies, will let it remain in normal size upon resizing.

*EDIT 2 : *

Code to tell you what I am saying :

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;

import java.awt.event.*;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;

public class GridBagTest extends JFrame
{
    private JPanel topPanel;
    private JPanel bottomPanel;

    public GridBagTest()
    {
        setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();      
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        //gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.weightx = 1.0;
        gbc.weighty = 0.8;

        // Setting TOP PANEL.
        topPanel  = new JPanel();
        topPanel.setLayout(new GridBagLayout());
        GridBagConstraints constraintsTopPanel = new GridBagConstraints();
        constraintsTopPanel.gridwidth = 2; // Specifies that this component will take two columns.
        constraintsTopPanel.gridheight = 1; // specifies that the component will take one row.
        /*
         *  fill with HORIZONTAL, means the component upon resize, will
         * only expand along the X-Axis.
         */
        constraintsTopPanel.fill = GridBagConstraints.NONE;
        constraintsTopPanel.insets = new Insets(5, 5, 5, 5);
        constraintsTopPanel.ipadx = 2;
        constraintsTopPanel.ipady = 2;
        constraintsTopPanel.weightx = 0.3;
        constraintsTopPanel.weighty = 0.2;

        constraintsTopPanel.gridx = 0;
        constraintsTopPanel.gridy = 0;
        JTextField tfield1 = new JTextField("kajslkajfkl dsjlafj lksdj akljsd lfkajflkdj lkaj flkdjalk jflkaj lkfdsj salkj flkaj flkja dslkfjal ksjdflka jlfjd aflsdj", 10);
        topPanel.add(tfield1, constraintsTopPanel);

        constraintsTopPanel.gridx = 2;
        constraintsTopPanel.gridy = 0;
        final JTextField tfield2 = new JTextField("kajslkajfkl dsjlafj lksdj akljsd lfkajflkdj lkaj flkdjalk jflkaj lkfdsj salkj flkaj flkja dslkfjal ksjdflka jlfjd aflsdj", 10);              
        topPanel.add(tfield2, constraintsTopPanel);

        constraintsTopPanel.gridx = 4;
        constraintsTopPanel.gridy = 0;
        JTextField tfield3 = new JTextField("kajslkajfkl dsjlafj lksdj akljsd lfkajflkdj lkaj flkdjalk jflkaj lkfdsj salkj flkaj flkja dslkfjal ksjdflka jlfjd aflsdj", 10);
        topPanel.add(tfield3, constraintsTopPanel);

        topPanel.setBackground(Color.WHITE);
        add(topPanel, gbc);

        constraintsTopPanel.gridx = 0;
        constraintsTopPanel.gridy = 2;
        constraintsTopPanel.gridwidth = 6; // Specifies that this component will take two columns.
        constraintsTopPanel.gridheight = 1; // specifies that the component will take one row.

        JButton button = new JButton("REMOVE");
        button.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent ae)
            {
                topPanel.remove(tfield2);
                topPanel.revalidate();
                topPanel.repaint();
            }
        });
        topPanel.add(button, constraintsTopPanel);

        //Setting BOTTOM PANEL.
        bottomPanel = new JPanel();
        bottomPanel.setLayout(new BorderLayout());
        bottomPanel.setBackground(Color.DARK_GRAY);     
        JLabel label3 = new JLabel("I am a new JLABEL for the bottom JPanel", JLabel.CENTER);
        label3.setForeground(Color.WHITE);
        bottomPanel.add(label3, BorderLayout.CENTER);       
        gbc.weighty = 0.2;      
        add(bottomPanel, gbc);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);

        pack();
        setVisible(true);
    }

    public static void main(String... args)
    {
        javax.swing.SwingUtilities.invokeLater(new Runnable()
            {
                public void run()
                {
                    new GridBagTest();
                }
            });
    }
}
nIcE cOw
  • 23,893
  • 7
  • 41
  • 127
  • You are right about SSCCE, I've added some sample code. You are wrong about HORIZONTAL, though I understand that without code I added it was difficult to understand my problem. – GrayR Feb 29 '12 at 08:09
  • "Remove all setPreferredSize(...)" - have you tried it before suggesting? It makes text field unreadable. – GrayR Feb 29 '12 at 08:24
  • I really don't know why you need to add JScrollBar to the JTextFields, since they are bydefault scrollable. Moreover when you make an object of JTextField, do specify the columns for it too, say like this `JTextField textField2 = new JTextField("abc", 10);`, – nIcE cOw Feb 29 '12 at 08:32
  • If you add very long text to the text field without putting it into scroller, then text field will be sized to fit all the text in it, so the panel which contains text field will be sized too and you will need to scroll or resize the panel. This panel also contains other text fields, they also will be sized even if text fitted in them completely. This all leads to really bad looking GUI. – GrayR Feb 29 '12 at 08:45
  • @GrayR : If they are getting resized to fit all the text, then use `gbc.fill = GridBagConstraints.NONE;`, this won't allow it to expand further. – nIcE cOw Feb 29 '12 at 08:54
  • you really need to start check your ideas before suggesting. It also doesn't work. Tried it on sample code provided in initial question. – GrayR Feb 29 '12 at 08:58
  • @GrayR : Check This code out and tell me isn't what I am saying implies to this code ? Seems like you yourself are confused about how to use GridBagLayout, rather you start reading it's Tutorial again. Better look at My code and I had tested that for the code provided by you, it works on my side, lest I got some JDK made by my own self, which is not available in the market for the rest of the poeple. – nIcE cOw Feb 29 '12 at 08:59
  • When I run it, I see small text field where I have to move caret to view the whole long text.In my example text field inside scroll bar is not editable.I also wrote earlier that I use it just to show user the info. User will not be able to move the caret, moreover I belive it's not convenient for user even if he could move the caret.You keep suggesting new ideas,but you're not helping with my question, asuming that what I am doing is initially a bad idea. Let's assume I know what I am doing and I need exactly what I am asking for: text field with scroll bar, but without vertical sizing issues. – GrayR Feb 29 '12 at 09:22
  • +1 for recommending _not_ to use setXXSize, ever :-) @GrayR - believe it or not, confusing LayoutManagers is a bad idea, always. http://stackoverflow.com/questions/7229226/should-i-avoid-the-use-of-setpreferredmaximumminimumsize-methods-in-java-swi If the one you choose for doing your layout doesn't behave like you expect, choose one that does – kleopatra Feb 29 '12 at 09:56
  • I will not agree with you. It's not a matter of layout manager, I've tried several of them and everywhere keep getting same issue. – GrayR Feb 29 '12 at 10:01
  • @GrayR : Whatever you want to assume, just assume, but stop pointing fingers at others, like as you said "have you tried it before suggesting?" and blah blah, if I am answering, I must be giving some input from my side too to help you out, instead of that you always complaining things like "you really need to start check your ideas before suggesting. It also doesn't work." If you seems you are really that good, why don't you solve it by yourself, instead of pointing fingers to someone who is trying to help you. – nIcE cOw Feb 29 '12 at 10:01
  • @Gagandeep Bali : well you really tried to help, and I am thankfull for that, really. But you couldn't. What I'am saying is that all your suggestions aren't relative to the question, imo. – GrayR Feb 29 '12 at 10:05
0

Well the best I've got is looking ugly in code, but does exactly what I need to the textField. Below is changed sample code from initial question. I'd be thankfull for any ideas how to make it better:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;

public class ScrollTextDemo extends JFrame{

public  ScrollTextDemo(){
    super();
    this.setPreferredSize(new Dimension(500, 300));
    JPanel panel = new JPanel();
    panel.setLayout(new GridBagLayout());
    JTextField textField = new JTextField("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
    textField.setCursor(new Cursor(0));
    textField.setEditable(false);
    JScrollPane scroll = new JScrollPane(textField, JScrollPane.VERTICAL_SCROLLBAR_NEVER,JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    GridBagConstraints gbc = new GridBagConstraints();
    gbc.gridx = 1;
    gbc.gridy = 1;
    gbc.fill = GridBagConstraints.HORIZONTAL;
    gbc.weightx = 0.5;
    //gbc.ipady = 20;//gives some room for scroll to appear and don't hide text area under the scroll.
    gbc.insets = new Insets(5, 5, 0, 5);
    panel.add(scroll,gbc);
    //let's add one more text field without scroll bar to compare
    JTextField textField2 = new JTextField("bbbbbbbb");
    gbc = new GridBagConstraints();
    gbc.gridx = 1;
    gbc.gridy = 2;
    gbc.fill = GridBagConstraints.HORIZONTAL;
    gbc.weightx = 0.5;
    gbc.insets = new Insets(5, 5, 0, 5);
    panel.add(textField2,gbc);
    scroll.addComponentListener( new ScrollTextComponentListener(scroll, textField2));
    this.add(panel);
}



public static void main(String args[]){
    ScrollTextDemo demo = new ScrollTextDemo();
    demo.pack();

    demo.setVisible(true);
}
}

class ScrollTextComponentListener implements ComponentListener {
private boolean scrollVisible;
private JScrollPane scroll;
private JComponent compareComponent;

public ScrollTextComponentListener(JScrollPane scroll, JComponent compareComponent) {
    this.scroll = scroll;
    this.compareComponent = compareComponent;
}

private boolean isScrollVisible() {
    return scroll.getHorizontalScrollBar().getModel().getExtent() != scroll.getHorizontalScrollBar().getModel().getMaximum();
}

private void setScrollSize(){
    boolean scrollVisible = isScrollVisible();
    if (scrollVisible){
        scroll.setPreferredSize(new Dimension(compareComponent.getWidth(),compareComponent.getHeight()*2));
        //also had to set both min and max sizes, because only preffered size didn't always work
        scroll.setMinimumSize(new Dimension(compareComponent.getWidth(),compareComponent.getHeight()*2));
        scroll.setMaximumSize(new Dimension(compareComponent.getWidth(),compareComponent.getHeight()*2));
    }
    else {
        scroll.setPreferredSize(new Dimension(compareComponent.getWidth(),compareComponent.getHeight()));
        scroll.setMinimumSize(new Dimension(compareComponent.getWidth(),compareComponent.getHeight()));
        scroll.setMaximumSize(new Dimension(compareComponent.getWidth(),compareComponent.getHeight()));
    }
    this.scrollVisible = scrollVisible;
}

@Override
public void componentResized(ComponentEvent e) {
    if (isScrollVisible() != scrollVisible) setScrollSize();
}

@Override
public void componentMoved(ComponentEvent e) {
}

@Override
public void componentShown(ComponentEvent e) {
    setScrollSize();
}

@Override
public void componentHidden(ComponentEvent e) {
}

}

GrayR
  • 1,225
  • 2
  • 18
  • 32