Java Tips Weblog

  • Blog Stats

    • 2,571,569 hits
  • Categories

  • Archives

Scrollable Panel

Posted by Rob Camick on December 20, 2009

When using a JScrollPane the general rule is that the scrollbars will appear when the “preferred size” of the component added to the JViewport of the scroll pane is greater than the “size” of the viewport. There may be times when you wish to prevent a scrollbar from appearing even though the preferred size is greater than the size.

To prevent the horizontal scrollbar from appearing the first solution you might attempt would be to use the setHorizontalScrollBarPolicy() method of JScrollPane with a value of ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER. This works, in the sense that the scrollbar is not displayed, but the problem is that the view of the component is just truncated in the viewport.

A more flexible approach is to implement the Scrollable interface on your component. In this solution you would want to override the getScrollableTracksViewportWidth() method to return “true”. The width of the component is now set to be the width of the viewport (instead of the preferred width of the component). This can affect the way the layout of the component is done (in the case of using a JPanel) or it can affect the way a component is painted (in the case of a JTextArea using wrapping). However, the Scrollable interface specifies 5 different methods and since many components don’t implement this interface you will usually end up implementing all 5 methods.

The purpose of the ScrollablePanel class is to provide reasonable implementations for each of the Scrollable methods and to support easy customization of the Scrollable behaviour.

The Scrollable interface only provides a boolean value for determining whether or not the viewport size (width or height) should be used by the scrollpane when determining if scrollbars should be made visible. ScrollablePanel supports this as well as adding the concept of dynamically changing this value based on the size of the viewport. In this case the viewport size will only be used when it is larger than the panels size. This has the effect of ensuring the viewport is always full as components added to the panel will be sized to fill the area available, based on the rules of the applicable layout manager of course.

The Scrollable interface also allows you to control the scroll amount for the horizontal and vertical scrollbars at the “unit” or “block” level. Using the ScrollablePanel, you can specify scroll amounts as a percentage of the viewport size or as an actual pixel value.

The methods added to implement this support are:

  • setScrollableHeight – The valid values for this method are:
    • ScrollableSizeHint.NONE (default) – the panel will be displayed in the viewport at its preferred size and therefore all the components will be layed out at their preferred sizes as well. Scrollbars may or may not appear.
    • ScrollableSizeHint.FIT – the panel and its components will attempt to be resized to fit completely in the viewport. This may result in the components shrinking or stretching. It also allows for the possibility that the panel will be truncated if the components cannot be shrunk enough to fit in the viewport. Scrollbars will never appear.
    • ScrollableSizeHint.STRETCH – the panel and its components will generally be displayed at their preferred sizes. However, it is possible that they may be stretched in order to completely fill the viewport. Scrollbars may or may not appear.
  • setScrollableWidth – See above for the valid values.
  • setScrollableBlockIncrement
    • The “orientation” must be one of HORIZONTAL or VERTICAL.
    • The “increment type” must be one of IncrementType.PERCENT or IncrementType.PIXEL.
  • setScrollableUnitIncrement – See above for valid values.

As a simple example you might have a ScrollablePanel without a horizontal scrollbar that has a vertical block scroll of 200 percent:

ScrollablePanel panel = new ScrollablePanel(...);
panel.setScrollableWidth( ScrollablePanel.ScrollableSizeHint.FIT );
panel.setScrollableBlockIncrement(
    ScrollablePanel.VERTICAL, ScrollablePanel.IncrementType.PERCENT, 200);
JScrollPane scrollPane = new JScrollPane( panel );



The WebStart demo uses a panel with buttons using a GridLayout. This was choosen because the GridLayout will resize compnents to fill the space available. Try the different values and don’t forget to resize the frame to so how the scrollbars react with the various options. Different layout managers will have different effects.

Get The Code

ScrollablePanel.java
ScrollablePanelDemo.java
RelativeLayout.java (needed for demo)

Related Reading

Java API: javax.swing.Scrollable

26 Responses to “Scrollable Panel”

  1. tbee said

    Hmmmm. Do you have a use case for us not-quite-getting-it people for when this comes in handy?

    • Rob Camick said

      The basic idea is to force the layout of components added to the panel so that all components are completely visible within the viewport. This will only work on layout managers that support shrinking/growing of components.

      In the following example:

      a) run the code as is and you will be able to scroll through all 8 lines. Now shrink the width and a horizontal scrollbar appears. Now in order to scroll the text vertically you need first use the horizontal scroll, but then you can’t even see the text.
      b) try again using the ScrollablePanel and you will see the scrolling is much more efficient and user friendly.

      Think of a more complex forms where you have multiple components displayed vertically and each component can grow horizontally. Experience will determine when this might come in handy.

      import java.awt.*;
      import javax.swing.*;
      
      public class TextAreaDemo
      {
        public static void main(String[] args)
        {
          JPanel panel = new JPanel( new BorderLayout() );
      //    ScrollablePanel panel = new ScrollablePanel();
      //    panel.setScrollableWidth( 
      //      ScrollablePanel.Size.VIEWPORT );
          panel.setLayout( new BorderLayout() );
      
          JTextArea textArea1 = new JTextArea(5, 20);
          textArea1.setText("1\n2\n3\n4\n5\n6\n7\n8");
          textArea1.setLineWrap( true );
          textArea1.setWrapStyleWord( true );
          panel.add(new JScrollPane(textArea1), BorderLayout.NORTH);
      
          JFrame frame = new JFrame();
          frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
          frame.add( new JScrollPane(panel) );
          frame.pack();
          frame.setLocationRelativeTo( null );
          frame.setVisible(true);
        }
      }
      
      • tbee said

        Is this roughly comparable to one of the layout managers I use; it will try to fill the viewport horizontally to the max, but then wraps to a new line. If the total length is too long, a vertical scrollbar appears. The same can be done vertically and then a horizontal scrollbar appears.

        So it tries to fill out in one direction and supports a scrollbar in the other. I use this often for a list-of-icons, not unlike on the desktop.

      • Rob Camick said

        It is similiar in that it allows you to use, for example, the width of the viewport. Then the LayoutManager takes over. In the case of the WebStart demo, the GridLayout, will shrink or grow each button to fit in the width available. It does not wrap the buttons and therefore will have no effect on the vertical scrollbar.

  2. Kleopatra said

    Rob,

    good idea to make the tri-state of possible sizing behaviours explicit – so good I most probably will add it to SwingX’ JXPanel which implements Scrollable :-)

    One minor oversight I stumbled across – because JXPanel misbehaves in that respect as well – is the missing revalidate on setting the Size property. Thinking about different names as well (saying “I’m not very good” at it is a boast, as I’m extremely bad with names ;-) because Size feels a bit too general. Just for the sound of it, playing with

    – ScrollableSizing
    – ScrollableSizeTracking
    – SizeTracking
    – SizeTrackBehaviour

    As to the states, thinking about those names as well: it’s describing if and if-so-how the scrollpane will adjust the size of the contained view. So the options are more along the lines of

    – does nothing (no tracking)
    – adjust always, fit-to-viewport-size (tracking)
    – adjust conditionally, stretch-to-viewport-if-smaller (tracking)

    Hmm .. probably boils down to replace PANEL with NONE.

    Cheers
    Jeanette

    PS: sorry for capturing this – java.net is still down, so looking for a home somewhere else :-)

    • Kleopatra said

      Done in the latest build (#2219, revison #3567) of SwingX: its name is ScrollableSizeTrack (open to improvements, of course), JXPanel is configurable with states NONE, FIT, STRETCH.

      Thanks for the inspiration!
      Jeanette

    • Kleopatra said

      Name change (after some tweeting): ScrollableSizeHint

      cheers
      Jeanette

    • Rob Camick said

      Jeanette,

      Thanks for all the feedback. I’ve had the code ready to go for a while but must admit I was having trouble with the naming as well. I really didn’t like using component names (panel, viewport) but was drawing a blank.

      Its nice to have another set of eyes (or multiple eyes since you did some tweeting). I do think your names make more sense. I’ll update the code and blog later today.

  3. guardiolawp said

    Great job Rob!
    I need a layout system like the sample with buttons and a gridLayout, but I have not been able to code the sample you show so could you post the sample code?

    Thanks in advance

    • Rob Camick said

      The test code does not show you how to use the class, it shows how to respond to GUI events. However if you want to look at it you can extract the ScrollablePanelTest.java code from the ScollablePanel.jar file.

      The blog gives a simple example of how to use two of the method of the class. To create a panel with buttons you just use a loop:

      ScrollablePanel panel = new ScrollablePanel( new GridLayout(...) );
      
      for ( int i = 0; i < 40; i++ )
          panel.add( new JButton( "Button" + i ) );
      

      If you are really having a problem then create a SSCCE and use the “Contact Us” page to send an email with more details.

  4. Gusman said

    Brilliant. Just what I was looking for. Thanks for this.

  5. Bob said

    It works very well, please don’t forget to use the component within a JScrollPane.

    JScrollPane x = new JScrollPane();

    ScrollablePanel panel = new ScrollablePanel(new BorderLayout());
    panel.setScrollableWidth( ScrollablePanel.ScrollableSizeHint.NONE );
    panel.setScrollableHeight( ScrollablePanel.ScrollableSizeHint.FIT );
    panel.add(containerPane,java.awt.BorderLayout.CENTER);

    x.setViewportView(panel);
    add(x,java.awt.BorderLayout.CENTER); // this one add to a window

    Overall it forks the uniformity of scrollbars in a JScrollPane to make them behave independently, it can also be thought of as a proxy between JScrollPane and whatever you wish to stick in there (JScrollPane -> ScrollablePanel-that-controls-scrollbars <- YourControl)

    Also, take note that you can reproduce VBox and HBox functionality in Java with it, Disable JScrollPane ScrollBars, think VBox.java – thx!

  6. Bob said

    If you ever find your self in a situation where setting a Border Layaout (that’s the North/Center… one) and all off of a sudden that component stretches to fill your entire parent container, no worries! Put it in a JPanel with Flow layout. Flow layout will restore the size back to normal – inhibit the odd expansion of a Border Layout managed Component.

    o YOUR PARENT THAT GOT FILLED
    o o Put JPanel with Flow Layout here.
    o o o THE CONTROL TO WHICH YOU JUST ASSIGNED BORDER LAYOUT AND IT STARTED STRETCHING

  7. Whired said

    This has some weird issues with setBackground.

    I’m not sure what the deal is, even when the call is made to super, the background color refuses to change.

  8. Whired said

    Ah, I see.

    http://download.oracle.com/javase/1.4.2/docs/api/javax/swing/JScrollPane.html

    “This can be accomplished by setting the background color of the viewport, via scrollPane.getViewport().setBackground(). The reason for setting the color of the viewport and not the scrollpane is that by default JViewport is opaque which, among other things, means it will completely fill in its background using its background color. Therefore when JScrollPane draws its background the viewport will usually draw over it.”

    Technicalities :P

  9. Yet another time that your blog has solved my problems quickly and easily. Thanks again.

  10. Sabuj said

    Thanks a lot for this…
    Just works as expected…

  11. Aleksi said

    Thanks, saved me a lot of trouble.

  12. egavaldo said

    Thanks! That’s exactly what I was searching for…

  13. Bertrand said

    Thanks a lot, you saved my project!

Leave a comment