Solved

How to determine if a JScrollPane's vertical scrollbar is visible/necessary/drawn?

Posted on 2004-04-07
19
4,394 Views
Last Modified: 2007-12-19
Hi all,

I have scoured the web far and wide, and have finally given up, I think.  What I want to do is conceptually very simple, but I have yet to find a method for implementing it that produces the desired results.  Here's the scenario:

In my JApplet, I have a JScrollPane wrapping a JTextArea.  I have the scrollbar policy set to JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED (the default).  I also have a button in my applet, which I want to conditionally enable only if (a) there no scrollbars are required for the JScrollPane or if (b) the scrollbar position is at the very bottom of the text area.

I've been able to accomplish the latter piece by code in an adjustmentValueChanged() event handler (my applet implements AdjustmentListener) that looks like this:

...
JScrollBar scrollBar = (JScrollBar)e.getSource();

int value = scrollBar.getValue();
int max = scrollBar.getMaximum();
int extent = scrollBar.getModel().getExtent();
       
if ((value != 0) && (value == (max - extent)))
{
    // code to disable button goes here
    ...
}

However, I have been unable to figure out how to accomplish part (a), because I have not been able to figure out how to programmatically determine if the vertical scrollbar is even drawn/necessary.  Here's several things I've tried:

- JScrollPane.getVerticalScrollBar() returns a non-null JScrollBar!  (why, even when no scrollbar is drawn???)
- JScrollBar.isValid() (on the scrollbar that's returned from getVerticalScrollBar()) returns true even when there is no scrollbar
- JScrollBar.isVisible() returns false even when there *is* a scrollbar
- JScrollBar.getMaximum() always returns a positive value (not 0 or anything simple) even when there is no scrollbar

So - the question to all you experts out there - how on *EARTH* can I possibly do this?  Am I being blind and missing the obvious?  Also, please keep in mind that I am developing for JVM 1.3.x and higher - all solutions have to be targeted for Java 1.3.

Help!!!  (This question is both urgent *and* extremely difficult, at least to me, hence the 500 pts!  :-)
0
Comment
Question by:d1G1t4L
  • 7
  • 7
  • 3
  • +1
19 Comments
 
LVL 92

Expert Comment

by:objects
ID: 10780409
add a HierarchyListener to your vertical scroll bar, it'll get fired whenever the scoll bar is added/removed.
0
 
LVL 30

Expert Comment

by:mayankeagle
ID: 10780809
>> JScrollPane.getVerticalScrollBar() returns a non-null JScrollBar!  (why, even when no scrollbar is drawn???)

Its not that a new scroll-bar will be instantiated everytime the scroll-bar appears and that it will be made null when it disappears. When you make the JScrollPane and set the policy for it, the JScrollBar objects will be constructed. They might just be invisble, depending upon whether the text exceeds the JTextArea's width/ height or not (and depending upon your policy). It doesn't mean that they are null.
0
 
LVL 1

Author Comment

by:d1G1t4L
ID: 10780838
mayankeagle - so how do I determine if the JScrollBar is visible or not?  isVisible() doesn't work... isShowing() doesn't work either.  There has to be some (relatively easy) way of determining if the text to be rendered by the JTextArea will require a scrollbar.  I'm not particularly tied to any one way of achieving my end goal - which is really just to find out if a scrollbar needs to be drawn and/or is drawn.

objects - I did a little research with the API reference, and found that a HierarchyEvent should effectively tell me the same thing as Component.isShowing().  First of all - is this a correct statement?  Because if so, then isShowing() returns false whether or not the JScrollBar is visible (see above).  If my statement is incorrect, though, do you have any sample code you can add to show me what the hierarchyChanged() event handler should look like?  I'm not sure about this whole business of HieararchyEvents, either - are they generated for Swing objects like JScrollBar?
0
 
LVL 1

Author Comment

by:d1G1t4L
ID: 10780854
In re-reading my first post, I found one point which was perhaps unclear.  The text to be rendered in the JTextArea is not static.  More specifically, it is based on an applet PARAM, and so can be of variable length.  This is why I need to only enable the button in the applet if (a) the JTextArea has no scrollbar or (b) if the scroll position is at the maximum value.
0
 
LVL 30

Expert Comment

by:mayankeagle
ID: 10780869
Maybe that you could try the getSize () method which it inherits from Component. It would return a Dimension. The JScrollBar's dimension would be rigid along (fixed) one axis and variable in the other (scrolling) axis.
0
 
LVL 92

Expert Comment

by:objects
ID: 10780885
> and found that a HierarchyEvent should effectively tell me the same thing as Component.isShowing().  
> First of all - is this a correct statement?

Not really. The event tells when/how things have changed.

> do you have any sample code you can add to show me what the hierarchyChanged() event handler should look like?

      public  void hierarchyChanged(HierarchyEvent e)
       {
             JScrollBar sb = (JScrollBar) e.getSource();
             if (sb.isVisible())
            {
                // showing
            }
            else
            {
                // not showing
            }
       }
0
 
LVL 1

Author Comment

by:d1G1t4L
ID: 10780905

mayankeagle - OK, I thought your new suggestion would work for sure!  It did not.  :-(  The size (width and height) comes up as 0x0 regardless of whether or not the JScrollBar is visible.

Another thought here - are all these dimensions/metrics/functions returning possibly incorrect data because the scrollbar has not been drawn yet (though I can't imagine why this might even be the case)?  The method call sequence is something like this:

1. Applet's init() method creates custom panel (deriving from JPanel) - invokes custom panel's constructor
2. Custom panel constructor creates JTextArea and JScrollPane and saves JScrollPane reference in a member variable
3. Applet Constructor calls hasScrollbar() method (which I've defined) in custom panel class
4. My hasScrollBar() method gets the JScrollBar from the JScrollPane (via getVerticalScrollBar())
5. hasScrollBar() uses the as-yet non-functional method (of many already described) to determine whether or not the scroll pane has a scrollbar.
0
 
LVL 92

Expert Comment

by:objects
ID: 10780934
here's example of using a HierarchyListener:

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

public class ScrollTest implements HierarchyListener
{
      public static void main(String[] args)
      {
            JFrame f = new JFrame();
            JTextArea ta = new JTextArea();
            JScrollPane sp = new JScrollPane(ta);
            f.getContentPane().add(sp);
            f.pack();
            f.show();
            sp.getVerticalScrollBar().addHierarchyListener(new ScrollTest());
      }
      
      public  void hierarchyChanged(HierarchyEvent e)
       {
             JScrollBar sb = (JScrollBar) e.getSource();
             System.out.println("Scrollbar is visible: "+sb.isVisible());
       }
       
}
0
 
LVL 1

Author Comment

by:d1G1t4L
ID: 10781041

objects - OK, finally I'm making some progress.  I implemented code similar to yours - essentially, I added a state variable (a boolean) that I set to the value of the isVisible().  Just for kicks, I also added some println()s to see how often the event was being fired and to report the value of my state variable from my hasScrollbar() method.  This is what I found for the situation where the ScrollBar is visible:

hierarchyChanged() fired! ScrollBar is visible
hierarchyChanged() fired! ScrollBar is visible
hierarchyChanged() fired! ScrollBar is visible
hierarchyChanged() fired! ScrollBar is not visible
_hasScrollBar = false
hierarchyChanged() fired! ScrollBar is visible
hierarchyChanged() fired! ScrollBar is visible

The "_hasScrollBar = false" output is generated by my hasScrollbar() method, while the other output is all from the hierarchyChanged() handler.  So what is happening, then, is that the hierarchy is changed twice after we've (incorrectly) determined the scrollbar to not be visible.  (This also answers my question of why isVisible() directly from my hasScrollbar() method was reporting 'false'.)

I thought about tying the hierarchyChanged() handler back to the parent JApplet by way of a custom Listener class (to which I'd register for a notification event with an add listener method, etc., etc.), and then directly enabling my button when the ScrollBar was not visible and disabling otherwise.  But this can lead to problems if the hierarchy is changed after I've already changed the button status.  So this is not a reliable mechanism - or at least, I don't yet know how to make it reliable.

Any thoughts?  You have certainly gotten me far closer to my goal than any other solution I've attempted - if you can make this HierarchyEvent-based solution work, you've got the points!

Thanks for your help so far!
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 92

Expert Comment

by:objects
ID: 10781063
> But this can lead to problems if the hierarchy is changed after I've already changed the button status

If you're only listener for HierarchChange events on the vertical scroll bar then how would problems occur?
What other changes would cause an event to be fired?

0
 
LVL 1

Author Comment

by:d1G1t4L
ID: 10781110

> If you're only listener for HierarchChange events on the vertical scroll bar then how would problems occur?
> What other changes would cause an event to be fired?

objects - that's the question to which I don't have an answer.  I can't even imagine why the event is fired as many times as it currently is.  I am naturally unsure, then, if there's any external stimulus that may cause the JScrollPane code to change the status of the scrollbar.

I would guess that once the scrollbar is either visible or not visible, its hierarchy wouldn't need to be changed - but this is not happening currently.  Naturally, I'm a little hesitant to go with something I'm not sure about.
0
 
LVL 92

Expert Comment

by:objects
ID: 10781160
What happens when you run the example I posted above?
When I run it events are only fired when the scrollbar either becomes visible or is hidden.
0
 
LVL 37

Expert Comment

by:zzynx
ID: 10781391
This test app seems to indicate that the listener objects suggested works as intended.
Whenever you change the appearing/disappearing by sizing the main window, the correct message is printed out.

/*
 * MyApp.java
 *
 */

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

public class MyApp extends JFrame implements HierarchyListener {
   
    /** Creates a new instance of MyApp */
    public MyApp() {
       
        JPanel mainPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        final JPanel p = new JPanel();

        p.setLayout(new GridBagLayout());
        p.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
        GridBagLayout grid = (GridBagLayout) p.getLayout();
        GridBagConstraints c = new GridBagConstraints();
        c.anchor = GridBagConstraints.WEST;
        int row = 0;
        int column = 0;
        for(int i = 0;  i < 40;  ++i)
        {
            c.insets = new Insets(2, 2, 2, 2);
            c.gridx = i%4;
            c.gridy = i/4;
            c.fill = GridBagConstraints.NONE;
            JButton b = new JButton("This is button " + (i + 1));
            grid.setConstraints(b, c);
            p.add(b); // Just to try out.
        }
        p.setMaximumSize(new Dimension(600, Integer.MAX_VALUE));
       
        mainPanel.add(p);
         JScrollPane sp = new JScrollPane(
            mainPanel,
            ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
            ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED
         );
         sp.getVerticalScrollBar().addHierarchyListener(this);

         getContentPane().setLayout(new BorderLayout());
         getContentPane().add(sp, BorderLayout.CENTER);
         
         setSize(new Dimension(300,300));
         setLocationRelativeTo(null);
    }
   
    public  void hierarchyChanged(HierarchyEvent e)
    {
        JScrollBar sb = (JScrollBar) e.getSource();
        System.out.println("Scrollbar is visible: "+ sb.isVisible());
    }
   
    public static void main(String[] args) {
        MyApp app = new MyApp();
        app.show();
    }
   
}
0
 
LVL 37

Expert Comment

by:zzynx
ID: 10781490
This demo app is even closer to what you want.
Click the "Long Msg"/"Short Msg" buttons and see "The button!" button toggle between enabled/disabled.

/*
 * ScrollBarDetectionDemo.java
 *
 */

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

public class ScrollBarDetectionDemo extends javax.swing.JFrame
                                    implements HierarchyListener {
   
    public ScrollBarDetectionDemo() {
        initComponents();
        theScrollPane.getVerticalScrollBar().addHierarchyListener(this);
        setSize( new Dimension(350, 150) );
        setLocationRelativeTo(null);
    }
   
    private void initComponents() {
        theScrollPane = new javax.swing.JScrollPane();
        theTextArea = new javax.swing.JTextArea();
        southPanel = new javax.swing.JPanel();
        longButton = new javax.swing.JButton();
        shortButton = new javax.swing.JButton();
        theButton = new javax.swing.JButton();

        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                exitForm(evt);
            }
        });

        theScrollPane.setPreferredSize(new java.awt.Dimension(20, 50));
        theScrollPane.setViewportView(theTextArea);

        getContentPane().add(theScrollPane, java.awt.BorderLayout.NORTH);

        longButton.setText("Long Msg");
        longButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                longButtonActionPerformed(evt);
            }
        });

        southPanel.add(longButton);

        shortButton.setText("Short Msg");
        shortButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                shortButtonActionPerformed(evt);
            }
        });

        southPanel.add(shortButton);

        theButton.setText("The button!");
        southPanel.add(theButton);

        getContentPane().add(southPanel, java.awt.BorderLayout.SOUTH);

        pack();
    }

    private void shortButtonActionPerformed(java.awt.event.ActionEvent evt) {
        theTextArea.setText("One line message One line message");
    }

    private void longButtonActionPerformed(java.awt.event.ActionEvent evt) {
        theTextArea.append("\r\nAnother line2 message");
        theTextArea.append("\r\nAnother line3 message");
        theTextArea.append("\r\nAnother line4 message");
        theTextArea.append("\r\nAnother line5 message");
        theTextArea.append("\r\nAnother line6 message");
    }
   
    private void exitForm(java.awt.event.WindowEvent evt) {
        System.exit(0);
    }
   
    public void hierarchyChanged(HierarchyEvent e)
    {
        JScrollBar sb = (JScrollBar) e.getSource();
        System.out.println("Scrollbar is visible: "+ sb.isVisible());
        theButton.setEnabled( sb.isVisible() );
    }
   
    public static void main(String args[]) {
        new ScrollBarDetectionDemo().show();
    }
   
   
    // Variables declaration - do not modify
    private javax.swing.JButton longButton;
    private javax.swing.JButton shortButton;
    private javax.swing.JPanel southPanel;
    private javax.swing.JButton theButton;
    private javax.swing.JScrollPane theScrollPane;
    private javax.swing.JTextArea theTextArea;
    // End of variables declaration
   
}
0
 
LVL 1

Author Comment

by:d1G1t4L
ID: 10786689

objects:

> What happens when you run the example I posted above?
> When I run it events are only fired when the scrollbar either becomes visible or is hidden.

When I ran your example, it worked as you stated.  (More on this in a bit)

zzynx & objects:

I definitely appreciate the code you have both posted.  However, the problem now, I think, is that in an applet, the hierarchyChanged() event is fired several times.  I adapted the two examples you posted and came up with the following applet code:

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;

import javax.swing.*;
import javax.swing.event.*;

public class ScrollTest extends JApplet implements HierarchyListener
{    
    JButton _button = null;
    JScrollPane _scrollPane = null;

    public void init()
    {
        _button = new JButton("Button");
      _button.setEnabled(false);

      JTextArea textArea = new JTextArea();
      textArea.setText(this.getParameter("content"));
      textArea.setEditable(false);
      textArea.setWrapStyleWord(true);
      textArea.setLineWrap(true);

      _scrollPane = new JScrollPane(textArea);
      _scrollPane.getVerticalScrollBar().addHierarchyListener(this);
      
        JPanel contentPane = new JPanel(new BorderLayout());
        contentPane.add(_scrollPane, BorderLayout.CENTER);
        contentPane.add(_button, BorderLayout.SOUTH);

        this.setContentPane(contentPane);
    }

    public void hierarchyChanged(HierarchyEvent e)
    {
      JScrollBar sb = (JScrollBar)e.getSource();

      System.out.println(sb.isVisible());
      //_button.setEnabled(!sb.isVisible());
    }
}

I then wrote a quick-and-dirty test HTML page that feeds the applet with two possible content strings, a short one and a long one:

<html>
<script language="JavaScript">
var longParam = "<param name=\"content\" value=\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Praesent tincidunt purus.\">";
var shortParam = "<param name=\"content\" value=\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\">";
var appletStart = "<applet code=\"ScrollTest.class\" width=\"100\" height=\"100\">";
var appletEnd = "</applet>";

function longContent()
{
      document.getElementById("applet").innerHTML = appletStart + longParam + appletEnd;
}

function shortContent()
{
      document.getElementById("applet").innerHTML = appletStart + shortParam + appletEnd;
}
</script>
<body>
<input type="button" value="long content" onclick="longContent()">
<input type="button" value="short content" onclick="shortContent()">
<div id="applet">
      <!-- applet HTML auto generated by longContent() / shortContent() -->
</div>
</body>
</html>

So with these two pieces, I've now built a good model of what I would need to implement in my actual applet.

When I ran the test applet with the "long content," the console output looked like this:

true
true
true
false
true
true

Note that this is much like the output that I was seeing in my original applet.

When I ran the test applet with the "short content," the output looked like this:

true
true
true
false
false

Which is also like what I was seeing in my original applet.

The *really* weird part - when I uncomment the following line:

      //_button.setEnabled(!sb.isVisible());

I *still* don't see the desired behavior in the resultant test applet!  What's going on here?  Have I stumbled on an obscure VM bug?  (I don't think so.)  Is there another way of accomplishing my goal that does not involve HierarchyEvents?
0
 
LVL 92

Accepted Solution

by:
objects earned 500 total points
ID: 10787166
I'd not add the HierarchyListener until after the applet has been laid out, ie. in start();. Otherwise you'll get various events at startup.
You should also remove the listener or else you could end up getting notified multiple times of the same event.

>  Is there another way of accomplishing my goal that does not involve HierarchyEvents?

I don't think so because that is exactly the event you are interested in.
0
 
LVL 37

Expert Comment

by:zzynx
ID: 10787280
>> Otherwise you'll get various events at startup.
Could be true. But is that really a problem?

>> You should also remove the listener or else ...
When?

>> I *still* don't see the desired behavior in the resultant test applet!
So you are saying that even when the last traced line says "true" (=long content) the button is NOT disabled?
or even when the last traced line says "false" (=short content) the button is NOT enabled?
Or do you even have other combinations? Weird!
0
 
LVL 92

Expert Comment

by:objects
ID: 10787829
0
 
LVL 1

Author Comment

by:d1G1t4L
ID: 10788078

Hrm - I seem to have forgotten to actually add my note.  Anyway - objects gets the points because his idea of using HierarchyEvents was the first (and only) that actually worked.

zzynx - thanks for your help, but objects provided sample code that I could have used successfully before you did, and he also helped with the key to making this all work: the addHierarchyListener() should be implemented in the applet's start().

Thanks to all who contributed!
0

Featured Post

What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

Join & Write a Comment

Java contains several comparison operators (e.g., <, <=, >, >=, ==, !=) that allow you to compare primitive values. However, these operators cannot be used to compare the contents of objects. Interface Comparable is used to allow objects of a cl…
Java functions are among the best things for programmers to work with as Java sites can be very easy to read and prepare. Java especially simplifies many processes in the coding industry as it helps integrate many forms of technology and different d…
Viewers learn about the third conditional statement “else if” and use it in an example program. Then additional information about conditional statements is provided, covering the topic thoroughly. Viewers learn about the third conditional statement …
This tutorial will introduce the viewer to VisualVM for the Java platform application. This video explains an example program and covers the Overview, Monitor, and Heap Dump tabs.

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now