Chapter 7.
Scrolling Panes
In this chapter:
JScrollPane
Grab-and-drag scrolling
Programmatic scrolling
7.1 JScrollPane
class [Link]
Using JScrollPane is normally very simple. Any component or container can be placed
in a JScrollPane and scrolled. Simply create a JScrollPane by passing its constructor
the component youd like to scroll:
JScrollPanejsp=newJScrollPane(myLabel);
Normally, our use of JScrollPane will not be much more extensive than the one line of
code shown above. The following is a simple JScrollPane demo appliction. Figure 7.1
illustrates:
Figure 7.1 JScrollPane demo
<<file [Link]>>
The Code: [Link]
see \Chapter7\1
[Link].*;
[Link].*;
publicclassScrollPaneDemoextendsJFrame
{
1
publicScrollPaneDemo(){
super("JScrollPaneDemo");
ImageIconii=newImageIcon("[Link]");
JScrollPanejsp=newJScrollPane(newJLabel(ii));
getContentPane().add(jsp);
setSize(300,250);
setVisible(true);
}
publicstaticvoidmain(String[]args){
newScrollPaneDemo();
}
}
When you run this example try scrolling by pressing or holding down any of the scroll
bar buttons. You will find this unacceptably slow because the scrolling occurs one pixel
at a time. We will see how to control this shortly.
Many components use a JScrollPane internally to display their contents, such as
JComboBox and JList. We are normally expected to place all multi-line text components
inside scroll panes (although this is not default behavior).
UI Guideline : Using Scroll Panes
For many applications it is best to avoid the introduction of a scrollpane and concentrate on
puting the required data on a screen such that scrolling is unnecessary. However, this is
not always possible. When you do need to introduce scrolling put some thought into the
type of data and application. If possible try to introduce scrolling in only 1 direction. For
example, with text documents, western culture has been used to scrolling vertically
since Egyptian times. Usability studies for World Wide Web pages have shown that
readers can find data quickly when vertically scrolling. Scrolling horizontally, however,
is labourious and difficult with text. Try to avoid it. With visual information, e.g. tables of
information, it may be more appropriate for horizontal scrolling, but try to avoid
horizontal and vertical scrolling if possible.
We can access a JScrollPanes scroll bars directly with its getXXScrollBar() and
setXXScrollBar() methods, where XX is either HORIZONTAL or VERTICAL.
Reference: In chapter 13 well talk more about JScrollBars
7.1.1 Scrollbar policies
abstract interface [Link]
We can specify specific policies for when and when not to display a JScrollPanes
horizontal and vertical scrollbars. We simply use its setVerticalScrollBarPolicy()
and setHorizontalScrollBarPolicy() methods, provding one of three constants
defined in the ScrollPaneConstants interface:
HORIZONTAL_SCROLLBAR_AS_NEEDED
HORIZONTAL_SCROLLBAR_NEVER
HORIZONTAL_SCROLLBAR_ALWAYS
VERTICAL_SCROLLBAR_AS_NEEDED
VERTICAL_SCROLLBAR_NEVER
VERTICAL_SCROLLBAR_ALWAYS
2
For example, to enforce the display of the vertical scrollbar at all times and always keep
the horizontal scrollbar hidden, we could do the following:
[Link](
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
[Link](
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
7.1.2 JViewport
class [Link]
The JViewport class is the container that is really responsible for displaying a specific
visible region of the component in JScrollPane. We can set/get a viewports view (the
component it contains) using its setView() and getView() methods. We can control
how much of this component JViewport displays by setting its extent size to a specified
Dimension using its setExtentSize() method. We can also specify where the origin
(upper left corner) of a JViewport should begin displaying its contained component by
providing specific coordinates (as a Point) of the contained component to the
setViewPosition() method. In fact, when we scroll a component in a JScrollPane this
view position is constantly being changed by the scrollbars.
Note: JViewport enforces a view position that lies within the view component only. We
cannot set negative or extremely large view positions (as of JDK1.2.2 we can set
negative view positions). However, since the view position is the upper right hand
corner of the viewport, we are still allowed to set the view position such that only part
of the viewport is filled. We will show how to watch for this, and stop it from happening,
in some of the examples below.
Whenever a change is made to the position or size of the visible portion of the view,
JViewport fires ChangeEvents. We can register ChangeListeners to capture these events
using JViewports addChangeListener() method. These are the only events that are
associated with JScrollPane by default. For instance, whenever we scroll using
JScrollPanes scroll bars, its main viewport, as well as its row and column header
viewports (see below), will each fire ChangeEvents.
The visible region of JViewports view can be retrieved as a Rectangle or Dimension
instance using the getViewRect() and getViewSize() methods respectively. This will
give us the current view position as well as the extent width and hieght. The view
position alone can be retrieved with getViewPosition(), which returns a Point
instance. To remove a component from JViewport we use its remove() method.
We can translate specific JViewport coordinates to the coordinates of its contained
component by passing a Point instance to its toViewCoordinates() method. We can do
the same for a region by passing toViewCoordinates() a Dimension instance. We can
also manually specify the visible region of the view component by passing a Dimension
instance to JViewports scrollRectToVisible() method.
We can retreive JScrollPanes main JViewport by calling its getViewport() method, or
assign it a new one using setViewport(). We can replace the component in this
viewport through JScrollPanes setViewportView() method, but there is no
getViewportView() counterpart. Instead we must first access its JScrollPanes
3
JViewport by calling getViewport(), and then call getView() on that (as discussed
above). Typically, to access a JScrollPanes main child component we would do the
following:
ComponentmyComponent=[Link]().getView();
7.1.3 ScrollPaneLayout
class [Link]
By default JScrollPanes layout is managed by an instance of ScrollPaneLayout.
JScrollPane can contain up to nine components and it is ScrollPaneLayouts job to
make sure that they are positioned correctly. These components are:
A JViewport containing the main component to be scrolled.
A JViewport used as the row header. This viewports view position changes vertically
in sync with the main viewport.
A JViewport used as the column header. This viewports view position changes
horizontally in sync with the main viewport.
Four components for placement in each corner of the scroll pane.
Two JScrollBars for vertical and horizontal scrolling.
The corner components will only be visible if the scrollbars and headers surrounding
them are also visible. To assign a component to a corner position we can call
JScrollPanes setCorner() method. This method takes both a String and a component
as parameters. The String is used to identify which corner this component is to be
placed in, and is recognized by ScrollPaneLayout. In fact ScrollPaneLayout identifies
each JScrollPane component with a unique String. Figure 7.2 illustrates:
Figure 7.2 JScrollPane components as identified by ScrollPaneLayout
<<file [Link]>>
4
To assign JViewports as the row and column headers we use JScrollPanes
setRowHeader() and setColumnHeader() methods respectively. We can also avoid the
creation of a JViewport ourselves by passing the component to be placed in the row or
column viewport to JScrollPanes setRowHeaderView() or setColumnHeaderView()
methods.
Because JScrollPane is most often used to scroll images, the most obvious use for the
row and column headers is to function as some sort of ruler. Here we present a basic
example showing how to populate each corner with a label and create some simple
rulers for the row and column headers that display ticks every 30 pixels, and render
themselves based on their current viewport position. Figure 7.3 illustrates:
Figure 7.3 JScrollPane demo with 4 corners, row header, and column header.
<<file [Link]>>
The Code: [Link]
see \Chapter7\2
[Link].*;
[Link].*;
publicclassHeaderDemoextendsJFrame
{
publicHeaderDemo(){
super("JScrollPaneDemo");
ImageIconii=newImageIcon("[Link]");
JScrollPanejsp=newJScrollPane(newJLabel(ii));
JLabel[]corners=newJLabel[4];
for(inti=0;i<4;i++){
corners[i]=newJLabel();
corners[i].setBackground([Link]);
corners[i].setOpaque(true);
corners[i].setBorder([Link](
[Link](2,2,2,2),
[Link]([Link],1)));
}
5
JLabelrowheader=newJLabel(){
Fontf=newFont("Serif",[Link]|[Link],10);
publicvoidpaintComponent(Graphicsg){
[Link](g);
Rectangler=[Link]();
[Link](f);
[Link]([Link]);
for(inti=30(r.y%30);i<[Link];i+=30){
[Link](0,r.y+i,3,r.y+i);
[Link](""+(r.y+i),6,r.y+i+3);
}
}
publicDimensiongetPreferredSize(){
returnnewDimension(
25,(int)[Link]().getHeight());
}
};
[Link]([Link]);
[Link](true);
JLabelcolumnheader=newJLabel(){
Fontf=newFont("Serif",[Link]|[Link],10);
publicvoidpaintComponent(Graphicsg){
[Link](g);
Rectangler=[Link]();
[Link](f);
[Link]([Link]);
for(inti=30(r.x%30);i<[Link];i+=30){
[Link](r.x+i,0,r.x+i,3);
[Link](""+(r.x+i),r.x+i10,16);
}
}
publicDimensiongetPreferredSize(){
returnnewDimension(
(int)[Link]().getWidth(),25);
}
};
[Link]([Link]);
[Link](true);
[Link](rowheader);
[Link](columnheader);
[Link](JScrollPane.LOWER_LEFT_CORNER,corners[0]);
[Link](JScrollPane.LOWER_RIGHT_CORNER,corners[1]);
[Link](JScrollPane.UPPER_LEFT_CORNER,corners[2]);
[Link](JScrollPane.UPPER_RIGHT_CORNER,corners[3]);
getContentPane().add(jsp);
setSize(400,300);
setVisible(true);
}
publicstaticvoidmain(String[]args){
newHeaderDemo();
}
}
Notice that the row and column headers use the graphics clipping area in their
paintComponent()
routine
for
optimal
efficiency.
We
also
override
the
getPreferredSize() method so that the proper width (for the row header) and height
6
(for the column header) will be used by ScrollPaneLayout. The other dimensions are
obtained by simply grabbing the labels preferred size, as they are completely
controlled by ScrollPaneLayout.
We are certainly not limited to labels for corners and row headers or within the main
viewport itself. As we mentioned in the beginning of this chapter, any comoponent can
be placed in a JViewport and scrolled in a JScrollPane.
7.1.4. The Scrollable interface
The Scrollable interface describes five methods that allow us to customize how
JScrollPane scrolls its contents. Specifically, by implementing this interface we can
specify how many pixels are scrolled when a scroll bar button or scroll bar paging area
(the empty region between the scroll bar thumb and the buttons) is pressed. There are
two methods that control this functionality: getScrollableBlockIncrement() and
getScrollableUnitIncrement(). The former is used to return the amount to scroll when
a scroll bar paging area is pressed, the latter is used when the button is pressed.
Reference: In text components, these two methods are implemented so that scrolling will
move one line of text at a time. (JTextComponent implements the Scrollable
interface.)
The other three methods of this interface involve JScrollPanes communication with
the
main
viewport.
The
getScrollableTracksViewportWidth()
and
getScrollableTracksHeight() methods can return true to disable scrolling in the
horizontal or vertical direction respecively. Normally these just return false. The
getPreferredSize() method is supposed to return the preferred size of the viewport
that will contain this component (the component implementing the Scrollable
interface). Normally we just return the preferred size of the component for this.
The following code shows how to implement the Scrollable interface to create a
custom JLabel whose unit and block increments will be 10 pixels. As we saw in the
example in the beginning of this chapter, scrolling one pixel at a time is tedious at best.
Increasing this to a 10 pixel increment provides a more natural feel.
The Code: [Link]
see \Chapter7\3
[Link].*;
[Link].*;
publicclassScrollableDemoextendsJFrame
{
publicScrollableDemo(){
super("JScrollPaneDemo");
ImageIconii=newImageIcon("[Link]");
JScrollPanejsp=newJScrollPane(newMyScrollableLabel(ii));
getContentPane().add(jsp);
setSize(300,250);
setVisible(true);
}
publicstaticvoidmain(String[]args){
newScrollableDemo();
}
}
7
classMyScrollableLabelextendsJLabelimplementsScrollable
{
publicMyScrollableLabel(ImageIconi){
super(i);
}
publicDimensiongetPreferredScrollableViewportSize(){
returngetPreferredSize();
}
publicintgetScrollableBlockIncrement(Rectangler,
intorietation,intdirection){
return10;
}
publicbooleangetScrollableTracksViewportHeight(){
returnfalse;
}
publicbooleangetScrollableTracksViewportWidth(){
returnfalse;
}
publicintgetScrollableUnitIncrement(Rectangler,
intorientation,intdirection){
return10;
}
}
7.2 Grab-and-drag scrolling
Many paint programs and document readers (such as Adobe Acrobat) support grab-anddrag scrolling. This is the ability to click on an image and drag it in any direction with
the mouse. It is fairly simple to implement, however, we must take care to make the
operation smooth without allowing the view to be scrolled past its extremities.
JViewport takes care of the negative direction for us, as it does not allow the view
position coordinates to be less than 0. But it will allow us to change the view position to
very large values, which can result in the viewport displaying a portion of the view
smaller than the viewport itself.
Note: As of JDK1.2.2 we are allowed to specify negative view position coordinates.
Weve modified the simple example from the beginning of this chapter to support graband-drag scrolling.
The Code: [Link]
see \Chapter7\4
[Link].*;
[Link].*;
[Link].*;
[Link].*;
publicclassGrabAndDragDemoextendsJFrame
{
publicGrabAndDragDemo(){
super("GrabanddragDemo");
8
ImageIconii=newImageIcon("[Link]");
JScrollPanejsp=newJScrollPane(newGrabAndScrollLabel(ii));
getContentPane().add(jsp);
setSize(300,250);
setVisible(true);
WindowListenerl=newWindowAdapter(){
publicvoidwindowClosing(WindowEvente){
[Link](0);
}
};
addWindowListener(l);
}
publicstaticvoidmain(String[]args){
newGrabAndDragDemo();
}
}
classGrabAndScrollLabelextendsJLabel
{
publicGrabAndScrollLabel(ImageIconi){
super(i);
MouseInputAdaptermia=newMouseInputAdapter(){
intm_XDifference,m_YDifference;
Containerc;
publicvoidmouseDragged(MouseEvente){
c=[Link]();
if(cinstanceofJViewport){
JViewportjv=(JViewport)c;
Pointp=[Link]();
intnewX=p.x([Link]()m_XDifference);
intnewY=p.y([Link]()m_YDifference);
intmaxX=[Link]()
[Link]();
intmaxY=[Link]()
[Link]();
if(newX<0)
newX=0;
if(newX>maxX)
newX=maxX;
if(newY<0)
newY=0;
if(newY>maxY)
newY=maxY;
[Link](newPoint(newX,newY));
}
}
publicvoidmousePressed(MouseEvente){
setCursor([Link](
Cursor.MOVE_CURSOR));
m_XDifference=[Link]();
m_YDifference=[Link]();
}
publicvoidmouseReleased(MouseEvente){
setCursor([Link](
9
Cursor.DEFAULT_CURSOR));
}
};
addMouseMotionListener(mia);
addMouseListener(mia);
}
}
Understanding the Code:
Class GrabAndScrollLabel
This class extends JLabel and overrides the JLabel(Imageicon ii) constructor. The
GrabAndScrollLabel constructor starts by calling the super class version and then
proceeds to set up a MouseInputAdapter. This adapter is the heart of the
GrabAndScrollLabel class.
The adapter uses three variables:
int m_XDifference: x coordinate saved on a mouse press event and used for
dragging horizontally
int m_YDifference: y coordinate saved on a mouse press event and used for
dragging vertically
Container c: used to hold a local reference to the parent container in the
mouseDragged() method.
The mousePressed() method changes the cursor to MOVE_CURSOR, and stores the event
coordinates in variables m_XDifference and m_YDifference to be use in
mouseDragged().
The mouseDragged() method first grabs a reference to the parent and checks if it is a
JViewport. If it isnt we do nothing. If it is we store the current view position and
calculate the new view position the drag will bring us into:
Pointp=[Link]();
intnewX=p.x([Link]()m_XDifference);
intnewY=p.y([Link]()m_YDifference);
When dragging components, normally this would be enough (as we will see in future
chapters), however we must make sure that we do not move this label in such a way
that the viewport is not filled by it. So we calculate the maximum allowable x and y
coordinates by subtracting the viewport dimensions from the size of this label (since the
view position coordinates are upper-left hand corner):
intmaxX=[Link]()
[Link]();
intmaxY=[Link]()
[Link]();
The remainder of this method compares the newX and newY values with the maxX and
maxY values, and adjusts the view position accordingly. If newX or newY is ever greater
than the maxX or maxY values respectively, we use the max values instead. If newX or
newY is ever less than 0 (which can happen only with JDK1.2.2), we use 0 instead. This is
necessary to allow smooth scrolling in all situations.
10
7.3 Scrolling programmatically
We are certainly not required to use a JScrollPane for scrolling. We can place a
component in a JViewport and control the scrolling ourselves if we like. This is what
JViewport was designed for, it just happens to be used by JScrollPane as well. Weve
constructed this example to show how to implement our own scrolling in a JViewport.
Four buttons are used for scrolling. We enable and disable these buttons based on
whether the view component is at any of its extremities. These buttons are assigned
keyboard mnemonics which we can use as an alternative to clicking.
This example also shows how to use a ChangeListener to capture ChangeEvents that
are fired when the JViewport changes state. The reason we need to capture these
events is that when our viewport is resized bigger than its view component child, the
scrolling buttons should become disabled. If these buttons are disabled and the
viewport is then resized so that it is no longer bigger than its child view component, the
buttons should then become enabled. It is quite simple to capture and process these
events as you will see below. (As with all of the examples we have presented, it may
help if you run this example before stepping through the code.)
Figure 7.4 Programmatic scrolling with JViewport.
<<file [Link]>>
The Code: [Link]
see \Chapter7\5
[Link].*;
[Link].*;
[Link].*;
[Link].*;
11
publicclassButtonScrollextendsJFrame
{
protectedJViewportm_viewport;
protectedJButtonm_up;
protectedJButtonm_down;
protectedJButtonm_left;
protectedJButtonm_right;
protectedintm_pgVert;
protectedintm_pgHorz;
publicButtonScroll(){
super("ScrollingProgrammatically");
setSize(400,400);
getContentPane().setLayout(newBorderLayout());
ImageIconshuttle=newImageIcon("[Link]");
m_pgVert=[Link]()/5;
m_pgHorz=[Link]()/5;
JLabellbl=newJLabel(shuttle);
m_viewport=newJViewport();
m_viewport.setView(lbl);
m_viewport.addChangeListener(newChangeListener(){
publicvoidstateChanged(ChangeEvente){
enableButtons(
[Link].m_viewport.getViewPosition());
}
});
getContentPane().add(m_viewport,[Link]);
JPanelpv=newJPanel(newBorderLayout());
m_up=createButton("up",'u');
ActionListenerlst=newActionListener(){
publicvoidactionPerformed(ActionEvente){
movePanel(0,1);
}
};
m_up.addActionListener(lst);
[Link](m_up,[Link]);
m_down=createButton("down",'d');
lst=newActionListener(){
publicvoidactionPerformed(ActionEvente){
movePanel(0,1);
}
};
m_down.addActionListener(lst);
[Link](m_down,[Link]);
getContentPane().add(pv,[Link]);
JPanelph=newJPanel(newBorderLayout());
m_left=createButton("left",'l');
lst=newActionListener(){
publicvoidactionPerformed(ActionEvente){
movePanel(1,0);
}
};
m_left.addActionListener(lst);
[Link](m_left,[Link]);
12
m_right=createButton("right",'r');
lst=newActionListener(){
publicvoidactionPerformed(ActionEvente){
movePanel(1,0);
}
};
m_right.addActionListener(lst);
[Link](m_right,[Link]);
getContentPane().add(ph,[Link]);
WindowListenerwndCloser=newWindowAdapter(){
publicvoidwindowClosing(WindowEvente){
[Link](0);
}
};
addWindowListener(wndCloser);
setVisible(true);
movePanel(0,0);
}
protectedJButtoncreateButton(Stringname,charmnemonics){
JButtonbtn=newJButton(newImageIcon(name+"[Link]"));
[Link](newImageIcon(name+"[Link]"));
[Link](newImageIcon(name+"[Link]"));
[Link]("Move"+name);
[Link](false);
[Link](newInsets(0,0,0,0));
[Link](false);
[Link](mnemonics);
returnbtn;
}
protectedvoidmovePanel(intxmove,intymove){
Pointpt=m_viewport.getViewPosition();
pt.x+=m_pgHorz*xmove;
pt.y+=m_pgVert*ymove;
pt.x=[Link](0,pt.x);
pt.x=[Link](getMaxXExtent(),pt.x);
pt.y=[Link](0,pt.y);
pt.y=[Link](getMaxYExtent(),pt.y);
m_viewport.setViewPosition(pt);
enableButtons(pt);
}
protectedvoidenableButtons(Pointpt){
if(pt.x==0)
enableComponent(m_left,false);
elseenableComponent(m_left,true);
if(pt.x>=getMaxXExtent())
enableComponent(m_right,false);
elseenableComponent(m_right,true);
if(pt.y==0)
enableComponent(m_up,false);
elseenableComponent(m_up,true);
if(pt.y>=getMaxYExtent())
13
enableComponent(m_down,false);
elseenableComponent(m_down,true);
}
protectedvoidenableComponent(JComponentc,booleanb){
if([Link]()!=b)
[Link](b);
}
protectedintgetMaxXExtent(){
returnm_viewport.getView().getWidth()m_viewport.getWidth();
}
protectedintgetMaxYExtent(){
returnm_viewport.getView().getHeight()m_viewport.getHeight();
}
publicstaticvoidmain(Stringargv[]){
newButtonScroll();
}
}
Understanding the Code
Class ButtonScroll
Several instance variables are declared:
JViewportm_viewport: viewport to display a large image.
JButtonm_up: push button to scroll up programmatically.
JButtonm_down: push button to scroll down programmatically.
JButtonm_left: push button to scroll left programmatically.
JButtonm_right: push button to scroll right programmatically.
intm_pgVert: number of pixels for a vertical scroll.
intm_pgHorz: number of pixels for a horizontal scroll.
The constructor of the ButtonScroll class creates and initializes the GUI components
for this example. A BorderLayout is used to manage the components in this frame's
content pane. JLabellbl holding a large image is placed in the viewport, m_viewport,
to provide programmatic viewing capabilities. This JViewport is added to the center of
our frame.
As we mentioned above, we need to capture the ChangeEvents that are fired when our
JViewport changes size so that we can enable and disable our buttons accordingly. We
do this by simply attaching a ChangeListener to our viewport and call our
enableButtons() method (see below) from stateChanged():
m_viewport.addChangeListener(newChangeListener(){
publicvoidstateChanged(ChangeEvente){
enableButtons(
[Link].m_viewport.getViewPosition());
}
});
Two buttons m_up and m_down are created for scrolling in the vertical direction. Method
14
createButton() is used to create a new JButton component and set a group of
properties for it (see below). Each of the new buttons receives an ActionListener which
calls the movePanel() method in response to a mouse click. These two buttons are
added to the intermediate container, JPanelpv, which is added to the EAST side of our
frames content pane. Similarly, two buttons, m_left and m_right, are created for
scrolling in the horizontal direction and added to the SOUTH region of the content pane.
Method createButton() creates a new JButton component and sets a group of
properties for it. This method takes two parameters: the name of the scrolling direction
as a String and the button's mnemonic as a char. This method assumes that three
image files are prepared:
[Link]: the default icon.
[Link]: the pressed icon.
[Link]: the disabled icon.
These images are loaded as ImageIcons and attached to the button with the associated
setXX() method:
JButtonbtn=newJButton(newImageIcon(name+"[Link]"));
[Link](newImageIcon(name+"[Link]"));
[Link](newImageIcon(name+"[Link]"));
[Link]("Moves"+name);
[Link](false);
[Link](newInsets(0,0,0,0));
[Link](false);
[Link](mnemonic);
returnbtn;
Then we remove any border or content area painting, so the presentation of our button
is completely determined by our icons. Finally we set the tool tip text and mnemonic
and return that component instance.
Method movePanel() programmatically scrolls the image in the viewport in the direction
determined by two parameters: xmove and ymove. These parameters can have values -1,
0, or 1. To determine the actual amount of scrolling we multiply these parameters by
m_pgHorz (m_pgVert). Local variable Pointpt determines a new viewport position. It is
limited so the resulting view will not display any empty space (not belonging to the
displaying image), similar to how we enforce the viewport view position in the grab-anddrag scrolling example above. Finally, method setViewPosition() is called to scroll to
the new position and enableButtons() enables/disables buttons according to the new
position:
Pointpt=m_viewport.getViewPosition();
pt.x+=m_pgHorz*xmove;
pt.y+=m_pgVert*ymove;
pt.x=[Link](0,pt.x);
pt.x=[Link](getMaxXExtent(),pt.x);
pt.y=[Link](0,pt.y);
pt.y=[Link](getMaxYExtent(),pt.y);
m_viewport.setViewPosition(pt);
enableButtons(pt);
Method enableButtons() disables a button if scrolling in the corresponding direction is
not possible and enables it otherwise. For example, if the viewport positions x
15
coordinate is 0 we can disable the scroll left button (remember that the view position
will never be negative, as enforced by JViewport):
if(pt.x<=0)
enableComponent(m_left,false);
elseenableComponent(m_left,true);
...Similarly, if the viewport positions x coordinate is greater than or equal to our
maximum allowable x position (determined by getMaxXExtent()) we disable the scroll
right button:
if(pt.x>=getMaxXExtent())
enableComponent(m_right,false);
elseenableComponent(m_right,true);
Methods getMaxXExtent() and getMaxYExtent() return the maximum coordinates
available for scrolling in the horizontal (vertical direction) by subtracting the appropriate
viewport dimension from the appropriate dimension of the child component.
Running the Code
Note: The shuttle image for this example was found at [Link]
Note how different images completely determine the presentation of our buttons. Press
the buttons and note how the image is scrolled programmatically. Use the keyboard
mnemonic as an alternative way to press the buttons, and note how this mnemonic is
displayed in the tool tip text. Note how a button is disabled when scrolling in the
corresponding direction is no longer available, and are enabled otherwise. Now try
resizing the frame and note how the buttons will change state depending on whether
the viewport is bigger or smaller than its child component.
16