如何使用滚动窗格

JScrollPane提供了组件的可滚动视图。当屏幕空间有限时,请使用滚动窗格显示较大的组件,或者其大小可以动态更改的组件。用于节省屏幕空间的其他容器包括split panestabbed panes

创建滚动窗格的代码可能最少。例如,下面是一个演示程序的图片,该示例程序将文本区域放在滚动窗格中,因为文本区域的大小会随着文本的追加而动态增 Long:

ToolBarDemo 的快照

以下是用于创建文本区域,使其成为滚动窗格的 Client 端以及将滚动窗格添加到容器的代码:

//In a container that uses a BorderLayout:
textArea = new JTextArea(5, 30);
...
JScrollPane scrollPane = new JScrollPane(textArea);
...
setPreferredSize(new Dimension(450, 110));
...
add(scrollPane, BorderLayout.CENTER);

粗体代码行创建JScrollPane,将文本区域指定为滚动窗格的 Client 端。该程序不会调用JScrollPane对象上的任何方法,因为滚动窗格会自动处理所有事情:在必要时创建滚动条,在用户移动滚动旋钮时重新绘制 Client 端,依此类推。

您可能已经注意到,前面的代码设置了滚动窗格容器的首选大小。在 Java 外观中,此首选大小恰好比文本区域显示我们创建时要求的 5 行所需的高度要小一些,因此滚动条最初会显示一个垂直滚动条。如果我们不限制滚动窗格容器的大小,则滚动窗格将足够大以使文本区域能够显示由JTextArea构造函数指定的完整 5 行和 30 列。有关使滚动窗格达到所需大小的技术的信息,请参考调整滚动窗格的大小

本节的其余部分讨论以下主题:

滚动窗格的工作方式

这是使用自定义滚动窗格查看照片的应用程序的快照:

ScrollDemo 的快照

该应用程序中的滚动窗格看起来与上一个演示程序中的滚动窗格完全不同。该滚动窗格不显示文本,而是包含图像。滚动窗格还具有两个滚动条,一个行标题,一个列标题和四个角,其中三个角已自定义。

Try this::

  • 单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行 ScrollDemo。另外,要自己编译和运行示例,请查阅example index

  • 移动滚动条上的旋钮。观看图像滚动,水平和垂直标尺一起滚动。

  • 如果您的鼠标带有滚轮(通常位于鼠标按钮之间),请使用鼠标滚轮垂直滚动图像。

  • 单击滚动窗格左上角的 cm 切换。行和列标题上的单位更改为英寸(或重新转换为厘米)。

  • 单击滚动条上的箭头按钮。另外,try单击垂直滚动条上的旋钮上方或下方的轨道,或单击水平滚动条的左侧或右侧的轨道。

  • 将光标移到图像上,然后按光标。continue 按光标,将其拖动到图像外的一点并暂停。图像的可见区域移向光标。此滚动拖动功能由滚动窗格和JComponent API 启用,但由显示图像的自定义组件实现。

  • 调整窗口大小。请注意,当滚动窗格足够大以显示整个图像时,滚动条会消失;而当滚动窗格太小而无法显示整个图像时,滚动条会再次出现。

ScrollDemo 程序在创建滚动窗格时构建滚动窗格的 Client 端:

//Where the member variables are declared:
private ScrollablePicture picture;
...
//Where the GUI is created:
picture = new ScrollablePicture( ... );
JScrollPane pictureScrollPane = new JScrollPane(picture);

滚动窗格的 Client 端也称为* view viewport view *。您可以通过调用setViewportView方法来动态更改 Client 端。请注意,JScrollPane没有相应的getViewportView方法。如果需要再次引用 Client 端对象,则可以将其缓存在变量中或在滚动窗格上调用getViewport().getViewportView()

当用户操作滚动窗格中的滚动条时,可见的 Client 端区域会相应更改。此图显示了滚动窗格及其 Client 端之间的关系,并指示了滚动窗格委托提供的类:

滚动窗格的体系结构

滚动窗格使用JViewport实例来 管理Client 端的可见区域。视口负责根据滚动条的位置定位和调整 Client 端的大小并显示它。

滚动窗格可以使用两个单独的JScrollBar实例作为滚动条。滚动条为用户提供了操纵可见区域的interface。下图显示了滚动条的三个区域:旋钮(有时称为* thumb *),(箭头)按钮和轨道。

滚动条的各个部分

当用户上下移动垂直滚动条上的旋钮时,Client 端的可见区域也会上下移动。类似地,当用户左右移动水平滚动条上的旋钮时,Client 端的可见区域会相应地来回移动。旋钮相对于其轨迹的位置与可见区域相对于 Client 的位置成比例地相等。在 Java 外观和其他外观中,旋钮的大小为可见的 Client 端可见性提供了视觉提示。

通过单击箭头按钮,用户可以按单位增量滚动。通过单击轨道内的内容,用户可以按块增量滚动。如果用户使用带有滚轮的鼠标,则用户可以使用鼠标滚轮垂直滚动。鼠标滚轮滚动的数量取决于平台。例如,在 Windows XP 上,默认情况下,鼠标滚轮滚动三个单位增量;滚动鼠标滚轮则以三个单位为增量。鼠标控制面板允许您指定不同数量的单位增量,或者改为使用块增量。有关单位和块增量的更多信息,请参见实现精通滚动 Client 端

典型的程序不会直接在视口或滚动条上实例化或调用方法。相反,程序使用JScrollPane API 和实现精通滚动 Client 端中讨论的 API 实现滚动行为。 JListJTableJTree等一些精通滚动的组件还提供additional API来帮助您影响其滚动行为。

设置滚动条策略

启动时,ScrollDemo应用程序中的滚动窗格具有两个滚动条。如果将窗口放大,则两个滚动条都会消失,因为不再需要它们。如果然后在不更改其宽度的情况下缩小窗口的高度,则垂直滚动条会重新出现。进一步的实验将显示,在此应用程序中,两个滚动条都会消失并根据需要重新出现。此行为由滚动窗格的* scroll bar policy *控制,实际上,这是两个策略:每个滚动条都有自己的策略。

ScrollDemo没有显式设置滚动窗格的滚动条策略-它使用默认值。您可以在创建滚动窗格或动态更改它们时设置策略。

JScrollPane提供的构造函数中,这两个可让您在创建滚动窗格时设置滚动条策略:

JScrollPane(Component, int, int)
JScrollPane(int, int)

第一个int指定垂直滚动条的策略;第二个指定水平滚动条的策略。您还可以使用setHorizontalScrollBarPolicysetVerticalScrollBarPolicy方法动态设置策略。对于构造函数和方法,请使用在ScrollPaneConstantsinterface(由JScrollPane实现)中定义的以下常量之一:

PolicyDescription
VERTICAL_SCROLLBAR_AS_NEEDED

HORIZONTAL_SCROLLBAR_AS_NEEDED
默认值。滚动条在视口小于 Client 端时显示,而在视口大于 Client 端时消失。
VERTICAL_SCROLLBAR_ALWAYS
HORIZONTAL_SCROLLBAR_ALWAYS
始终显示滚动条。如果视口足够大以显示整个 Client 端,则旋钮消失。
VERTICAL_SCROLLBAR_NEVER
HORIZONTAL_SCROLLBAR_NEVER
从不显示滚动条。如果您不希望用户直接控制显示 Client 端的哪一部分,或者您希望他们仅使用非滚动条技术(例如拖动),请使用此选项。

提供定制装饰

滚动窗格绘制的区域最多包括九个部分:中心,四个侧面和四个角。中心是所有滚动窗格中始终存在的唯一组件。除了滚动条,侧面还可以包含列标题和行标题。仅当在该角相交的两边都包含可见分量时,该角组件才可见。

滚动窗格上的装饰

如图所示,ScrollDemo中的滚动窗格具有自定义的行和列标题。此外,由于所有四个侧面均已填充,因此存在所有四个角。该程序自定义了三个角-两个仅用与Rule相同的颜色填充其区域,另一个角包含一个切换按钮。右下角是第四个角,是滚动窗格提供的默认值。注意,由于在此示例中行标题和列标题始终存在,因此切换按钮也始终存在。

如果拐角处包含用户始终需要访问的控件,请确保始终出现在拐角处相交的边。例如,如果此应用程序将切换switch放置在滚动条相交的右下角,则如果用户调整窗口大小并且甚至滚动条之一都消失了,则切换将消失。

滚动窗格的行标题和列标题由自定义的JComponent子类Rule提供,该子类以厘米或英寸为单位绘制标尺。这是创建和设置滚动窗格的行和列标题的代码:

//Where the member variables are defined:
private Rule columnView;
private Rule rowView;
...
//Where the GUI is initialized:
ImageIcon bee = createImageIcon("images/flyingBee.jpg");
...
//Create the row and column headers.
columnView = new Rule(Rule.HORIZONTAL, true);
rowView = new Rule(Rule.VERTICAL, true);

...
pictureScrollPane.setColumnHeaderView(columnView);
pictureScrollPane.setRowHeaderView(rowView);

您可以将任何组件用于滚动窗格的行标题和列标题。滚动窗格将行标题和列标题放置在自己的JViewPort s 中。因此,在水平滚动时,列标题跟随,而在垂直滚动时,行标题跟随。确保行和列与视图具有相同的宽度和高度,因为 JScrollPane 不会将这些值强制具有相同的大小。如果一个与另一个不同,则您可能无法获得所需的行为。

作为JComponent子类,我们的自定义Rule类将其呈现代码放入paintComponent方法中。 Rule渲染代码注意仅在当前剪切范围内进行绘制,以确保快速滚动。您的自定义行标题和列标题应该执行相同的操作。

您还可以将任何组件用于滚动窗格的角。 ScrollDemo通过在左上角放置切换按钮,并在右上和左下角放置自定义Corner对象来说明这一点。这是创建Corner对象并调用setCorner放置它们的代码:

//Create the corners.
JPanel buttonCorner = new JPanel(); //use FlowLayout
isMetric = new JToggleButton("cm", true);
isMetric.setFont(new Font("SansSerif", Font.PLAIN, 11));
isMetric.setMargin(new Insets(2,2,2,2));
isMetric.addItemListener(this);
buttonCorner.add(isMetric);
...
//Set the corners.
pictureScrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER,
                            buttonCorner);
pictureScrollPane.setCorner(JScrollPane.LOWER_LEFT_CORNER,
                            new Corner());
pictureScrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER,
                            new Corner());

请记住,每个角的大小由相交的边的大小确定。对于某些组件,您必须注意组件的特定实例适合其角落。例如,该程序在切换按钮上设置字体和边距,以使其适合标题所构建的空间。 Corner类不是问题,因为该类使用纯色为整个边界着色,无论它们碰巧是什么。

从代码中可以看到,常数指示角位置。该图显示了每个位置的常数:

Corner constants

常量在JScrollPane实现的ScrollPaneConstantsinterface中定义。

实现精通滚动的 Client 端

要自定义 Client 端组件与其滚动窗格进行交互的方式,可以使该组件实现Scrollableinterface。通过实现Scrollable,Client 端既可以指定用于查看它的视口的大小,也可以指定在滚动条上单击不同控件时要滚动的视点的大小。您还可以指定视图是否应跟踪视口的大小。通常在视口大于视图时使用,但视图应填充可用空间。

Note:

如果您不能或不想实现可滚动 Client 端,则可以使用JScrollBarsetUnitIncrementsetBlockIncrement方法指定单位和块增量。例如,以下代码将垂直滚动的单位增量设置为 10 个像素:

scrollPane.getVerticalScrollBar().setUnitIncrement(10);

这里还是滚动条的三个控制区域:旋钮,按钮和轨道。

滚动条的各个部分

您可能已经注意到,在ScrollDemo中使用滚动条时,单击按钮会将图像滚动到刻度线边界。您可能还已经注意到,单击曲目会以“屏幕显示”滚动图片。更一般地,按钮以单位增量滚动可见区域,并且轨道以块增量滚动可见区域。在示例中看到的行为不是滚动窗格的默认行为,而是由 Client 端在其Scrollableinterface的实现中指定的。

ScrollDemo程序的 Client 端是ScrollablePictureScrollablePictureJLabel的子类,它提供所有五个Scrollable方法的实现:

  • getScrollableBlockIncrement

  • getScrollableUnitIncrement

  • getPreferredScrollableViewportSize

  • getScrollableTracksViewportHeight

  • getScrollableTracksViewportWidth

ScrollablePicture实现Scrollableinterface主要是为了影响单位和块的增量。但是,它必须提供所有五种方法的实现。因此,它为您可能想为精通滚动类的其他三种方法提供了合理的默认值。

每当用户单击滚动条上的按钮之一时,滚动窗格就会调用 Client 端的getScrollableUnitIncrement方法。只要 Client 端实现 Scrollable,这就是事实。此方法返回要滚动的像素数。此方法的一个明显实现是返回 Headers 标尺上刻度线之间的像素数。但是ScrollablePicture的作用不同:它返回将图像放置在刻度线边界上所需的值。这是实现:

public int getScrollableUnitIncrement(Rectangle visibleRect,
                                      int orientation,
                                      int direction) {
    //Get the current position.
    int currentPosition = 0;
    if (orientation == SwingConstants.HORIZONTAL) {
        currentPosition = visibleRect.x;
    } else {
        currentPosition = visibleRect.y;
    }

    //Return the number of pixels between currentPosition
    //and the nearest tick mark in the indicated direction.
    if (direction < 0) {
        int newPosition = currentPosition -
                         (currentPosition / maxUnitIncrement)
                          * maxUnitIncrement;
        return (newPosition == 0) ? maxUnitIncrement : newPosition;
    } else {
        return ((currentPosition / maxUnitIncrement) + 1)
                 * maxUnitIncrement
                 - currentPosition;
    }
}

如果图像已经在刻度线边界上,则此方法返回刻度线之间的像素数。否则,它将返回从当前位置到最近的刻度的像素数。

同样,每次用户单击轨道时,滚动窗格都会调用 Client 端的getScrollableBlockIncrement方法,但前提是 Client 端实现 Scrollable。这是此方法的ScrollablePicture的实现:

public int getScrollableBlockIncrement(Rectangle visibleRect,
                                       int orientation,
                                       int direction) {
    if (orientation == SwingConstants.HORIZONTAL)
        return visibleRect.width - maxUnitIncrement;
    else
        return visibleRect.height - maxUnitIncrement;
}

此方法返回可见矩形的高度减去刻度线。这种行为很典型,但是如果垂直滚动则为 true,否则为宽度。块增量应该比视口小一点,以便为上下文保留一些先前的可见区域。例如,一个文本区域可能会保留一两行文本作为上下文,而一个表则可能会保留一行或一列(取决于滚动方向)。

ScrollablePicture.java具有Scrollableinterface不需要的另一段代码,但在可滚动组件中很常见:鼠标移动侦听器,使用户可以通过拖动来滚动图片。以下代码段中的粗体代码通过拖动来实现滚动:

public class ScrollablePicture extends JLabel
                               implements Scrollable,
                                          MouseMotionListener {
    ...
    public ScrollablePicture(...) {
        ...
        setAutoscrolls(true); //enable synthetic drag events
        addMouseMotionListener(this); //handle mouse drags
    }
    ...
    public void mouseDragged(MouseEvent e) {
        //The user is dragging us, so scroll!
        Rectangle r = new Rectangle(e.getX(), e.getY(), 1, 1);
        scrollRectToVisible(r);
    }
...
}

每当用户从图片拖动到图片之外的位置并暂停时,此代码片段就会滚动图片。 setAutoscrolls方法是由JComponent定义的,Object 是帮助(但不实现)通过拖动滚动。将 autoscrolls 属性设置为true可使组件触发合成的鼠标拖曳事件,即使鼠标不动(因为它停止,在组件外部拖动,中间拖动)。由组件的鼠标运动侦听器侦听这些事件并做出相应的反应。

调整滚动窗格的大小

除非您明确设置滚动窗格的首选大小,否则滚动窗格将基于其九个组件(视口以及两个滚动条,行标题和列标题以及四个角)的首选大小对其进行计算。程序员最关心的最大因素是用于显示 Client 端的视口的大小。

如果 Client 端不精通滚动,则滚动窗格会自行调整大小,以便 Client 端以其首选大小显示。对于典型的精明 Client 端,这会使滚动窗格变得多余。也就是说,滚动窗格没有滚动条,因为 Client 端的首选大小足以显示整个 Client 端。在这种情况下,如果 Client 端不能动态更改大小,则可能应通过设置其首选大小或容器的首选大小来限制滚动窗格的大小。

如果 Client 端精通滚动,则滚动窗格将使用 Client 端的getPreferredScrollableViewportSize方法返回的值来计算其视口的大小。此方法的实现通常报告滚动的首选大小,该大小小于组件的标准首选大小。例如,默认情况下,JListgetPreferredScrollableViewportSize实现返回的值刚好足以显示八行。

诸如liststablestext componentstrees之类的精通滚动的类通常提供一种或多种方法,这些方法可让程序员影响getPreferredScrollableViewportSize返回的大小。例如,可以通过调用setVisibleRowCount方法来设置列表或树中可见行的数量。列表或树负责弄清楚显示该行数所需的大小。

有关JScrollPane以外的类提供的与滚动相关的方法的信息,请参考其他与滚动相关的类中的方法。记住-如果您不喜欢getPreferredScrollableViewportSize返回的值,则可以始终设置滚动窗格或其容器的首选大小。

动态更改 Client 端的大小

更改滚动窗格的 Client 端大小是一个两步过程。首先,设置 Client 的首选大小。然后,在 Client 端上调用revalidate,以使滚动窗格知道它应该更新自身及其滚动条。让我们来看一个例子。

这是一张应用程序的图片,每当用户放置一个边界超出 Client 当前边界的圆时,该应用程序都会更改 Client 的大小。当用户清除绘图区域时,该程序还会更改 Client 端的大小:

ScrollDemo2 的快照

您可以在ScrollDemo2.java中找到此示例的完整源代码,该代码基于教程阅读器 John Vella 提供的示例。您可以run ScrollDemo2(下载 JDK 7 或更高版本)。

以下是在必要时更改绘图区域大小的代码:

if (changed) {
    //Update client's preferred size because
    //the area taken up by the graphics has
    //gotten larger or smaller (if cleared).
    drawingArea.setPreferredSize(/* the new size */);

    //Let the scroll pane know to update itself
    //and its scroll bars.
    drawingArea.revalidate();
}

请注意,当 Client 端更改大小时,滚动条会调整。滚动窗格不会调整大小,视口也不会调整大小。

有关 Client 端对象更改大小的另一个示例,请参考SplitPaneDemo

滚动窗格 API

下表列出了常用的与滚动相关的构造函数和方法。您最可能在JScrollPane对象上调用的其他方法是其超类提供的诸如setPreferredSize之类的方法。有关常用继承方法的表,请参见JComponent API

使用滚动窗格的 API 分为以下类别:

设置滚动窗格
(JScrollPane构造函数和方法)

方法或构造函数Purpose
JScrollPane()

JScrollPane(Component)
JScrollPane(int, int)
JScrollPane(Component,int,int)
创建滚动窗格。 Component参数(如果存在)设置滚动窗格的 Client 端。存在的两个int参数分别设置垂直和水平滚动条策略。
void setViewportView(Component)设置滚动窗格的 Client 端。
void setVerticalScrollBarPolicy(int)
int getVerticalScrollBarPolicy()
设置或获取垂直滚动策略。 ScrollPaneConstants定义了三个用于指定此策略的值:VERTICAL_SCROLLBAR_AS_NEEDED(默认值),VERTICAL_SCROLLBAR_ALWAYSVERTICAL_SCROLLBAR_NEVER
void setHorizontalScrollBarPolicy(int)
int getHorizontalScrollBarPolicy()
设置或获取水平滚动策略。 ScrollPaneConstants定义了三个用于指定此策略的值:HORIZONTAL_SCROLLBAR_AS_NEEDED(默认值),HORIZONTAL_SCROLLBAR_ALWAYSHORIZONTAL_SCROLLBAR_NEVER
void setViewportBorder(Border)
Border getViewportBorder()
设置或获取视口周围的边框。这比在组件上设置边框要好。
boolean isWheelScrollingEnabled()设置或获取是否响应鼠标滚轮进行滚动。默认情况下,鼠标滚轮滚动是启用的。

装饰滚动窗格
(JScrollPane个方法)

MethodPurpose
void setColumnHeaderView(Component)

void setRowHeaderView(Component)
设置滚动窗格的列或行标题。
void setCorner(String,Component)
Component getCorner(String)
设置或获取指定的拐角。 int参数指定哪个角,并且必须是ScrollPaneConstants中定义的以下常量之一:UPPER_LEFT_CORNERUPPER_RIGHT_CORNERLOWER_LEFT_CORNERLOWER_RIGHT_CORNERLOWER_LEADING_CORNERLOWER_TRAILING_CORNERUPPER_LEADING_CORNERUPPER_TRAILING_CORNER

实现精通滚动的 Client 端

MethodPurpose
int getScrollableUnitIncrement(矩形,整数,整数)

int getScrollableBlockIncrement(Rectangle,int,int)
(由Scrollableinterface要求)
获取单位或块的增量,以像素为单位。 Rectangle参数是当前可见矩形的边界。第一个int参数是SwingConstants.HORIZONTALSwingConstants.VERTICAL,具体取决于用户单击的滚动条。第二个int参数指示要滚动的方向。小于 0 的值表示向上或向左。大于 0 的值表示向下或向右。
Dimension getPreferredScrollableViewportSize()
(Scrollableinterface要求)
获取视口的首选大小。这允许 Client 端影响显示它的视口的大小。如果视口大小不重要,请实现此方法以返回getPreferredSize
boolean getScrollableTracksViewportWidth()
boolean getScrollableTracksViewportHeight()
(Scrollableinterface要求)
获取滚动窗格是否应强制 Client 端与视口相同的宽度或高度。从这两种方法中的任一种返回的值true均有效地禁止了水平或垂直滚动。
void setAutoscrolls(boolean)
(在JComponent中)
设置当用户将鼠标拖动到组件外并停止时是否应生成合成鼠标拖动事件;这些事件对于通过拖动进行滚动是必需的。默认情况下,该值为false,但是许多可滚动组件(例如JTable和自定义组件)将该值设置为true

其他与滚动相关的类中的方法

MethodPurpose
void scrollRectToVisible(Rectangle)

(在JComponent中)
如果组件位于支持滚动的容器(如滚动窗格)中,则调用此方法将滚动滚动窗格,以使指定的矩形可见。
void setVisibleRowCount(int)
int getVisibleRowCount()
(在JList中)
设置或获取列表中有多少行可见。 getPreferredScrollableViewportSize方法使用可见行计数来计算其返回值。
void ensureIndexIsVisible(int)
(在JList中)
滚动以使指定索引处的行可见。此方法调用scrollRectToVisible,并且仅在列表位于支持滚动的容器(例如滚动窗格)中时起作用。
void setVisibleRowCount(int)
int getVisibleRowCount()
(在JTree中)
设置或获取树的可见行数。 getPreferredScrollableViewportSize方法使用可见行计数来计算其返回值。
void scrollPathToVisible(TreePath)
void scrollRowToVisible(int)
(在JTree中)
滚动以使在指定索引处的指定树路径或行可见。这些方法调用scrollRectToVisible,并且仅在树位于支持滚动的容器(例如滚动窗格)中时才起作用。
void setScrollsOnExpand(boolean)
boolean getScrollsOnExpand()
(在JTree中)
设置或获取用户扩展节点时是否自动发生滚动。默认为 True。仅当树位于支持滚动的容器(例如滚动窗格)中时,此功能才起作用。
void setPreferredScrollableViewportSize(Dimension)
(在JTable中)
设置要由getPreferredScrollableViewportSize返回的值。

使用滚动窗格的示例

下表显示了使用JScrollPane的示例,并在其中描述了这些示例。

ExampleWhere DescribedNotes
ToolBarDemoThis section,

如何使用工具栏
显示滚动窗格的简单但典型的用法。
ScrollDemo此部分使用许多滚动窗格的铃声和口哨声。
ScrollDemo2此部分显示如何更改 Client 端的大小。
SplitPaneDemo如何使用分割窗格,
如何使用清单
在滚动窗格中放置列表和标签。此外,还显示了如何处理滚动窗格的 Client 端更改大小的情况。
TableDemo如何使用表格在滚动窗格中放置表格。
TextSamplerDemo使用 Literals 组件在滚动窗格中分别放置文本区域,编辑器窗格和文本窗格。
TreeDemo如何使用树木在滚动窗格中放置树。

如果您使用 JavaFX 编程,请参见Scroll Pane