这些Java教程是针对JDK 8编写的。本页面中描述的示例和实践不利用后续版本中引入的改进,并可能使用不再可用的技术。
请参阅Java语言更改,了解Java SE 9及后续版本中更新的语言特性的摘要。
请参阅JDK发行说明,了解所有JDK版本的新功能、增强功能和已删除或弃用选项的信息。
JScrollPane
提供了一个组件的可滚动视图。当屏幕空间有限时,使用滚动窗格来显示一个较大的组件或一个尺寸可以动态变化的组件。其他用于节省屏幕空间的容器包括分割窗格和选项卡窗格。
创建滚动窗格的代码可以很简洁。例如,下面是一个演示程序的图片,它将一个文本区域放在滚动窗格中,因为文本区域的大小会随着文本的添加而动态增长:
下面的代码创建了文本区域,将其设置为滚动窗格的客户端,并将滚动窗格添加到容器中:
//在使用BorderLayout的容器中: textArea = new JTextArea(5, 30); ... JScrollPane scrollPane = new JScrollPane(textArea); ... setPreferredSize(new Dimension(450, 110)); ... add(scrollPane, BorderLayout.CENTER);
加粗的代码行创建了JScrollPane
,指定了文本区域作为滚动窗格的客户端。程序不会在JScrollPane
对象上调用任何方法,因为滚动窗格会自动处理一切:在必要时创建滚动条,当用户移动滚动条时重新绘制客户端等等。
您可能已经注意到前面的代码设置了滚动窗格容器的首选大小。在Java外观中,这个首选大小恰好比我们创建文本区域时请求的5行所需的高度要小一点,因此滚动条最初会显示一个垂直滚动条。如果我们不限制滚动窗格容器的大小,滚动窗格将足够大,以便文本区域显示使用JTextArea
构造函数指定的完整的5行30列。有关使滚动窗格具有所需大小的技巧,请参阅调整滚动窗格的大小。
本节的其余部分讨论以下主题:
这是一个使用自定义滚动窗格查看照片的应用程序的快照:
这个应用程序中的滚动窗格与之前的演示程序中的滚动窗格非常不同。滚动窗格不显示文本,而是包含了一张图片。滚动窗格还有两个滚动条、一行标题、一列标题和四个角落,其中三个已经自定义。
JComponent
API启用的,但是它是由显示图片的自定义组件实现的。在创建滚动窗格时,ScrollDemo程序会为滚动窗格的客户端进行设置:
//在这里声明成员变量: private ScrollablePicture picture; ... //创建GUI时: picture = new ScrollablePicture( ... ); JScrollPane pictureScrollPane = new JScrollPane(picture);
滚动窗格的客户端也被称为视图或视口视图。你可以通过调用setViewportView
方法来动态更改客户端。请注意,JScrollPane
没有对应的getViewportView
方法。如果你需要再次引用客户端对象,你可以将其缓存到一个变量中,或者在滚动窗格上调用getViewport().getViewportView()
方法。
当用户在滚动窗格中操作滚动条时,客户端的可见区域会相应地改变。下图展示了滚动窗格与其客户端之间的关系,并指示了滚动窗格委托的类:
滚动窗格使用一个JViewport
实例来管理客户端的可见区域。视口负责根据滚动条的位置来定位和调整客户端的大小,并将其显示出来。
滚动窗格可以使用两个独立的JScrollBar
实例作为滚动条。滚动条为用户提供了操作可见区域的接口。下图显示了滚动条的三个区域:滑块(有时称为拇指)、(箭头)按钮和轨道。
当用户在垂直滚动条上上下移动滑块时,客户端的可见区域也会相应地上下移动。类似地,当用户在水平滚动条上左右移动滑块时,客户端的可见区域也会相应地左右移动。滑块相对于轨道的位置与可见区域相对于客户端的位置成比例。在Java外观和其他一些外观中,滑块的大小可以提供有关客户端可见区域的视觉线索。
通过点击箭头按钮,用户可以按单位增量进行滚动。通过在轨道内点击,用户可以按块增量进行滚动。如果用户使用带有滚轮的鼠标,那么用户可以使用鼠标滚轮垂直滚动。鼠标滚轮滚动的量因平台而异。例如,默认情况下,在Windows XP上,鼠标滚轮每次滚动三个单位增量;鼠标控制面板允许您指定不同数量的单位增量或使用块增量。有关单位和块增量的更多信息,请参见实现可滚动的客户端。
典型的程序不会直接实例化或调用视口(viewport)或滚动条的方法。相反,程序使用JScrollPane API和实现可滚动客户端中讨论的API来实现滚动行为。一些具有滚动功能的组件,如JList、JTable和JTree,还提供其他API来帮助您影响它们的滚动行为。
在ScrollDemo
应用程序中,滚动窗格在启动时有两个滚动条。如果将窗口放大,两个滚动条都会消失,因为它们不再需要。如果然后将窗口的高度缩小而不改变其宽度,垂直滚动条会重新出现。进一步的实验将显示,在此应用程序中,两个滚动条根据需要出现和消失。这个行为由滚动窗格的滚动条策略控制,实际上是两个策略:每个滚动条都有自己的策略。
ScrollDemo
不明确设置滚动窗格的滚动条策略-它使用默认值。您可以在创建滚动窗格时设置策略,也可以在运行时动态更改它们。
在JScrollPane
提供的构造函数中,这两个构造函数可以在创建滚动窗格时设置滚动条策略:
JScrollPane(Component, int, int) JScrollPane(int, int)
第一个int
指定垂直滚动条的策略;第二个指定水平滚动条的策略。您还可以使用setHorizontalScrollBarPolicy
和setVerticalScrollBarPolicy
方法动态设置策略。无论是使用构造函数还是方法,都可以使用JScrollPane
实现的ScrollPaneConstants
接口中定义的以下常量:
策略 | 描述 |
---|---|
VERTICAL_SCROLLBAR_AS_NEEDED HORIZONTAL_SCROLLBAR_AS_NEEDED |
默认值。当视口小于客户端时,滚动条出现;当视口大于客户端时,滚动条消失。 |
VERTICAL_SCROLLBAR_ALWAYS HORIZONTAL_SCROLLBAR_ALWAYS |
始终显示滚动条。如果视口足够大以显示整个客户端,滚动条的滑块消失。 |
VERTICAL_SCROLLBAR_NEVER HORIZONTAL_SCROLLBAR_NEVER |
从不显示滚动条。如果您不希望用户直接控制所显示的客户端的部分,或者如果您只希望他们使用非滚动条技术(如拖动),请使用此选项。 |
滚动窗格绘制的区域由最多九个部分组成:中心、四个边和四个角。中心是所有滚动窗格中唯一总是存在的组件。除了滚动条,边可以包含列和行标题。只有当两个相交的边都包含可见组件时,角组件才可见。
如图所示,ScrollDemo
中的滚动窗格具有自定义的行和列标题。此外,由于所有四个边都被填充,四个角都存在。程序自定义了三个角 — 其中两个只是用与Rule
相同的颜色填充它们的区域,另一个包含一个切换按钮。第四个角,也就是右下角,是滚动窗格默认提供的。请注意,因为在这个示例中行和列标题总是存在,切换按钮也总是存在。
如果一个角包含用户需要始终访问的控件,请确保相交在角上的边始终存在。例如,如果该应用程序将切换按钮放在滚动条相交的右下角,则在用户调整窗口大小并且其中一个滚动条消失时,切换按钮将消失。
滚动窗格的行和列标题由自定义的JComponent
子类Rule
提供,它以厘米或英寸为单位绘制标尺。以下是创建和设置滚动窗格的行和列标题的代码:
//在成员变量定义的位置: private Rule columnView; private Rule rowView; ... //在初始化GUI的位置: ImageIcon bee = createImageIcon("images/flyingBee.jpg"); ... //创建行和列标题。 columnView = new Rule(Rule.HORIZONTAL, true); rowView = new Rule(Rule.VERTICAL, true); ... pictureScrollPane.setColumnHeaderView(columnView); pictureScrollPane.setRowHeaderView(rowView);
您可以使用任何组件作为滚动窗格的行和列标题。滚动窗格将行和列标题放在它们自己的JViewPort
中。因此,当水平滚动时,列标题会跟随移动,而当垂直滚动时,行标题会跟随移动。确保行和列具有与视图相同的宽度和高度,因为JScrollPane不强制这些值具有相同的大小。如果宽度和高度不同,可能无法获得所需的行为。
作为JComponent
的子类,我们自定义的Rule
类将其渲染代码放在paintComponent
方法中。Rule
的渲染代码确保只在当前剪切边界内绘制,以确保快速滚动。您自定义的行和列标题也应该这样做。
您还可以使用任何组件作为滚动窗格的角落。 ScrollDemo
通过在左上角放置一个切换按钮以及在右上角和左下角放置自定义Corner
对象来说明这一点。下面是创建Corner
对象并调用setCorner
来放置它们的代码:
//创建角落。 JPanel buttonCorner = new JPanel(); //使用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); ... //设置角落。 pictureScrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, buttonCorner); pictureScrollPane.setCorner(JScrollPane.LOWER_LEFT_CORNER, new Corner()); pictureScrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER, new Corner());
请记住,每个角的大小取决于相交的边的大小。对于某些组件,您必须确保组件的特定实例适合其角。例如,该程序在切换按钮上设置字体和边距,以使其适应标题所建立的空间。对于Corner
类来说,这不是问题,因为该类使用纯色填充其整个边界。
从代码中可以看出,常量指示了角的位置。以下是每个位置的常量:
这些常量在JScrollPane
实现的ScrollPaneConstants
接口中定义。
要自定义客户端组件与其滚动窗格的交互方式,您可以使组件实现Scrollable
接口。通过实现Scrollable
,客户端可以指定用于查看它的视口的大小以及点击滚动条上不同控件时滚动的量。您还可以指定视图是否应跟踪视口的大小。这通常用于当视口大于视图时,但视图应填充可用空间的情况。
setUnitIncrement
和setBlockIncrement
方法来指定单位和块增量,这些方法属于JScrollBar
类。例如,以下代码将垂直滚动的单位增量设置为10个像素:
scrollPane.getVerticalScrollBar().setUnitIncrement(10);
这里再次介绍滚动条的三个控制区域:旋钮、按钮和轨道。
您可能已经注意到,在ScrollDemo
中操作滚动条时,点击按钮会将图像滚动到刻度边界。您还可能注意到,点击轨道会将图片滚动一个"屏幕"的大小。更一般地说,按钮按单位增量滚动可见区域,而轨道按块增量滚动可见区域。您在示例中看到的行为不是滚动窗格的默认行为,而是客户端在实现Scrollable
接口时指定的行为。
ScrollDemo
程序的客户端是ScrollablePicture
。 ScrollablePicture
是JLabel
的子类,提供了所有五个Scrollable
方法的实现:
getScrollableBlockIncrement
getScrollableUnitIncrement
getPreferredScrollableViewportSize
getScrollableTracksViewportHeight
getScrollableTracksViewportWidth
ScrollablePicture
主要通过实现Scrollable
接口来影响单位和块增量。然而,它必须为所有五个方法提供实现。因此,它为其他三个方法提供了合理的默认值,您可能希望将其复制到您的滚动类中。
滚动窗格在用户点击滚动条上的按钮时调用客户端的getScrollableUnitIncrement
方法。只要客户端实现了Scrollable接口,就会调用该方法。该方法返回要滚动的像素数。该方法的一个明显实现是返回标头刻度之间的像素数。ScrollablePicture
则执行了不同的操作:它返回将图像定位在刻度边界上所需的值。以下是其实现:
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { //获取当前位置 int currentPosition = 0; if (orientation == SwingConstants.HORIZONTAL) { currentPosition = visibleRect.x; } else { currentPosition = visibleRect.y; } //返回当前位置到最近的刻度线之间的像素数 if (direction < 0) { int newPosition = currentPosition - (currentPosition / maxUnitIncrement) * maxUnitIncrement; return (newPosition == 0) ? maxUnitIncrement : newPosition; } else { return ((currentPosition / maxUnitIncrement) + 1) * maxUnitIncrement - currentPosition; } }
如果图像已经在刻度线边界上,此方法返回刻度线之间的像素数。否则,返回从当前位置到最近刻度线的像素数。
同样,滚动窗格在每次用户点击轨道时调用客户端的getScrollableBlockIncrement
方法,但前提是客户端实现了Scrollable接口。下面是ScrollablePicture对该方法的实现:
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { if (orientation == SwingConstants.HORIZONTAL) return visibleRect.width - maxUnitIncrement; else return visibleRect.height - maxUnitIncrement; }
此方法返回可见矩形的高度减去一个刻度线。这是典型的行为,但在垂直滚动时为真,否则为宽度。块增量应略小于视口,以留下一些前一个可见区域的上下文。例如,文本区域可以保留一两行文本作为上下文,表格可以保留一行或一列(取决于滚动方向)。
ScrollablePicture.java
还包含了一个不是Scrollable
接口所必需的代码,但在可滚动组件中很常见:一个鼠标移动监听器,允许用户通过从中拖动来滚动图片。下面代码片段中的加粗代码实现了拖动滚动:
public class ScrollablePicture extends JLabel implements Scrollable, MouseMotionListener { ... public ScrollablePicture(...) { ... setAutoscrolls(true); //启用合成拖动事件 addMouseMotionListener(this); //处理鼠标拖动 } ... public void mouseDragged(MouseEvent e) { //用户正在拖动,所以滚动! Rectangle r = new Rectangle(e.getX(), e.getY(), 1, 1); scrollRectToVisible(r); } ... }
该片段在用户将图片从图片拖到图片外部的位置并暂停时滚动图片。 setAutoscrolls
方法由 JComponent
定义,用于辅助滚动拖动,但不实现它。将 autoscrolls 属性设置为 true
使组件在鼠标不移动时(因为它在组件外停止拖动)触发合成的鼠标拖动事件。组件的鼠标动作监听器需要监听这些事件并做出相应的反应。
除非显式设置滚动窗格的首选大小,否则滚动窗格会根据其九个组件的首选大小(视口和如果存在的话,两个滚动条、行和列标题以及四个角落)计算它的首选大小。最大的因素,也是大多数程序员关心的因素,是用于显示客户端的视口的大小。
如果客户端不具备滚动功能,则滚动窗格会自动调整大小,以便客户端以其首选大小显示。对于典型的不具备滚动功能的客户端,这使得滚动窗格变得多余。也就是说,滚动窗格没有滚动条,因为客户端的首选大小足够大,可以显示整个客户端。在这种情况下,如果客户端不会动态改变大小,您应该通过设置滚动窗格的首选大小或其容器的首选大小来限制滚动窗格的大小。
如果客户端具备滚动功能,则滚动窗格使用客户端的 getPreferredScrollableViewportSize
方法返回的值来计算其视口的大小。该方法的实现通常会为滚动返回一个较小的首选大小,该大小小于组件的标准首选大小。例如,默认情况下,JList
的 getPreferredScrollableViewportSize
方法返回的值刚好足以显示八行。
具备滚动功能的类,如列表、表格、文本组件和树,通常提供一个或多个方法,允许程序员影响 getPreferredScrollableViewportSize
返回的大小。例如,您可以通过调用 setVisibleRowCount
方法来设置列表或树中可见行的数量。列表或树会自动计算出需要显示该行数的大小。
有关与滚动相关的方法,请参阅与滚动相关的其他类中的方法,这些方法由 JScrollPane
以外的类提供。还记住,如果不喜欢 getPreferredScrollableViewportSize
返回的值,始终可以设置滚动窗格或其容器的首选大小。
更改滚动窗格的客户区大小是一个两步过程。首先,设置客户区的首选大小。然后,在客户区上调用revalidate
,让滚动窗格知道它应该更新自身和滚动条。让我们看一个例子。
下面是一个应用程序的图片,当用户放置一个圆形,其边界超出当前客户区的边界时,会改变客户区的大小。当用户清除绘图区域时,程序也会改变客户区的大小:
您可以在ScrollDemo2.java
中找到此示例的完整源代码,该示例基于教程读者John Vella提供的示例。您可以运行ScrollDemo2(下载JDK 7或更高版本)。
下面是在需要时更改绘图区域大小的代码:
if (changed) { //更新客户区的首选大小,因为图形所占用的区域已经变大或变小(如果已清除)。 drawingArea.setPreferredSize(/* 新大小 */); //让滚动窗格知道要更新自身和滚动条。 drawingArea.revalidate(); }
请注意,当客户区大小更改时,滚动条会相应调整。滚动窗格不会重新调整大小,视口也不会。
请参考SplitPaneDemo
,了解另一个客户对象大小更改的示例。
以下表格列出了常用的滚动相关构造函数和方法。您最有可能在JScrollPane
对象上调用的其他方法是其超类提供的setPreferredSize
等方法。请参阅JComponent API以获取常用继承方法的表格。
使用滚动窗格的API可分为以下几类:
方法或构造函数 | 目的 |
---|---|
JScrollPane() JScrollPane(Component) JScrollPane(int, int) JScrollPane(Component, int, int) |
创建一个滚动窗格。当存在 Component 参数时,设置滚动窗格的客户端。当存在两个 int 参数时,分别设置垂直和水平滚动条的策略。 |
void setViewportView(Component) |
设置滚动窗格的客户端。 |
void setVerticalScrollBarPolicy(int) int getVerticalScrollBarPolicy() |
设置或获取垂直滚动策略。 ScrollPaneConstants 定义了三个值来指定这个策略: VERTICAL_SCROLLBAR_AS_NEEDED (默认值)、 VERTICAL_SCROLLBAR_ALWAYS 和 VERTICAL_SCROLLBAR_NEVER 。 |
void setHorizontalScrollBarPolicy(int) int getHorizontalScrollBarPolicy() |
设置或获取水平滚动策略。 ScrollPaneConstants 定义了三个值来指定这个策略: HORIZONTAL_SCROLLBAR_AS_NEEDED (默认值)、 HORIZONTAL_SCROLLBAR_ALWAYS 和 HORIZONTAL_SCROLLBAR_NEVER 。 |
void setViewportBorder(Border) Border getViewportBorder() |
设置或获取视口周围的边框。这比在组件上设置边框更好。 |
boolean isWheelScrollingEnabled() | 设置或获取鼠标滚轮滚动时是否发生滚动。默认情况下启用鼠标滚轮滚动。 |
方法 | 目的 |
---|---|
void setColumnHeaderView(Component) void setRowHeaderView(Component) |
为滚动窗格设置列或行标题。 |
void setCorner(String, Component) Component getCorner(String) |
设置或获取指定的角落。int 参数指定了哪个角落,并且必须是 ScrollPaneConstants 中定义的以下常量之一:UPPER_LEFT_CORNER、UPPER_RIGHT_CORNER、LOWER_LEFT_CORNER、LOWER_RIGHT_CORNER、LOWER_LEADING_CORNER、LOWER_TRAILING_CORNER、UPPER_LEADING_CORNER 和 UPPER_TRAILING_CORNER。 |
方法 | 目的 |
---|---|
int getScrollableUnitIncrement(Rectangle, int, int) int getScrollableBlockIncrement(Rectangle, int, int) ( Scrollable 接口要求) |
获取单位或块的滚动增量(以像素为单位)。Rectangle 参数是当前可见矩形的边界。第一个int 参数要么是SwingConstants.HORIZONTAL ,要么是SwingConstants.VERTICAL ,取决于用户点击的滚动条。第二个int 参数指示滚动的方向。小于0的值表示向上或向左滚动。大于0的值表示向下或向右滚动。 |
Dimension getPreferredScrollableViewportSize() ( Scrollable 接口要求) |
获取视口的首选大小。这允许客户端影响其显示的视口的大小。如果视口大小不重要,请实现此方法返回getPreferredSize 。 |
boolean getScrollableTracksViewportWidth() boolean getScrollableTracksViewportHeight() ( Scrollable 接口要求) |
获取滚动面板是否应强制客户端与视口具有相同的宽度或高度。这两个方法中的任何一个返回true 都会有效地禁止水平或垂直滚动。 |
void setAutoscrolls(boolean) (在 JComponent 中) |
设置是否在用户将鼠标拖到组件外部并停止时生成合成的鼠标拖动事件;这些事件对于通过拖动来进行滚动是必要的。默认情况下,该值为false ,但许多可滚动的组件(如JTable 和自定义组件)会将该值设置为true 。 |
JComponent
中)void setVisibleRowCount(int)
JList
中)getPreferredScrollableViewportSize
void ensureIndexIsVisible(int)
JList
中)scrollRectToVisible
void setVisibleRowCount(int)
JTree
中)getPreferredScrollableViewportSize
void scrollPathToVisible(TreePath)
JTree
中)scrollRectToVisible
void setScrollsOnExpand(boolean)
JTree
中)void setPreferredScrollableViewportSize(Dimension)
JTable
中)getPreferredScrollableViewportSize
这个表格展示了使用JScrollPane
的示例以及这些示例所在的位置。
示例 | 所在位置 | 备注 |
---|---|---|
ToolBarDemo |
本节, 如何使用工具栏 |
展示了一个简单但典型的滚动窗格的用法。 |
ScrollDemo |
本节 | 使用了滚动窗格的许多功能。 |
ScrollDemo2 |
本节 | 展示了如何改变客户端的大小。 |
SplitPaneDemo |
如何使用分割窗格, 如何使用列表 |
在一个滚动窗格中放置了一个列表和一个标签。同时,展示了如何处理滚动窗格的客户端大小变化的情况。 |
TableDemo |
如何使用表格 | 在一个滚动窗格中放置了一个表格。 |
TextSamplerDemo |
使用文本组件 | 分别在一个滚动窗格中放置了一个文本区域、一个编辑器面板和一个文本面板。 |
TreeDemo |
如何使用树 | 在一个滚动窗格中放置了一个树。 |
如果您正在使用JavaFX,请参考滚动窗格。