如何使用滚动窗格

原文: https://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html

JScrollPane 提供组件的可滚动视图。当屏幕空间有限时,使用滚动窗格显示大型组件或其大小可动态更改的组件。用于节省屏幕空间的其他容器包括分割窗格标签窗格

创建滚动窗格的代码可以是最小的。例如,这是一个演示程序的图片,它将文本区域放在滚动窗格中,因为文本区域的大小随着文本附加到其上而动态增长:

A snapshot of ToolBarDemo

这是创建文本区域的代码,使其成为滚动窗格的客户端,并将滚动窗格添加到容器:

  1. //In a container that uses a BorderLayout:
  2. textArea = new JTextArea(5, 30);
  3. ...
  4. JScrollPane scrollPane = new JScrollPane(textArea);
  5. ...
  6. setPreferredSize(new Dimension(450, 110));
  7. ...
  8. add(scrollPane, BorderLayout.CENTER);

粗体代码行创建JScrollPane,将文本区域指定为滚动窗格的客户端。该程序不会调用JScrollPane对象上的任何方法,因为滚动窗格会自动处理所有内容:必要时创建滚动条,当用户移动滚动旋钮时重绘客户端,等等。

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

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

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

A snapshot of ScrollDemo

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


Try this::

  1. Click the Launch button to run ScrollDemo using Java™ Web Start (download JDK 7 or later). Alternatively, to compile and run the example yourself, consult the example index.Launches the ScrollDemo example

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

  3. 如果您的鼠标带有滚轮(通常位于鼠标按钮之间),请使用鼠标滚轮垂直滚动图像。
  4. 单击滚动窗格左上角的 cm 切换。行和列标题上的单位更改为英寸(或更改为厘米)。
  5. 单击滚动条上的箭头按钮。另外,尝试单击垂直滚动条上旋钮上方或下方的轨道,或单击水平滚动条左侧或右侧的轨道。
  6. 将光标移到图像上,然后按光标。继续按光标,拖动到图像外的一点并暂停。图像的可见区域朝向光标移动。此滚动滚动功能由滚动窗格和JComponent API 启用,但它由显示图像的自定义组件实现。
  7. 调整窗口大小。请注意,当滚动窗格足够大以显示整个图像时滚动条消失,当滚动窗格太小而无法显示整个图像时,滚动条会再次出现。

ScrollDemo 程序在创建滚动窗格时建立滚动窗格的客户端:

  1. //Where the member variables are declared:
  2. private ScrollablePicture picture;
  3. ...
  4. //Where the GUI is created:
  5. picture = new ScrollablePicture( ... );
  6. JScrollPane pictureScrollPane = new JScrollPane(picture);

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

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

The architecture of a scroll pane

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

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

The parts of a scroll bar

当用户上下移动垂直滚动条上的旋钮时,客户端的可见区域上下移动。类似地,当用户将水平滚动条上的旋钮向右和向左移动时,客户端的可见区域相应地来回移动。旋钮相对于其轨道的位置成比例地等于可见区域相对于客户的位置。在 Java 外观和其他一些内容中,旋钮的大小给出了关于客户端可见多少的直观线索。

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

典型程序不直接在视口或滚动条上实例化或调用方法。相反,程序使用[HTD0]实现滚动 - 精明客户端中讨论的JScrollPane API 和 API 来实现其滚动行为。一些滚动精通的组件,如JListJTableJTree也提供附加 API ,以帮助您影响其滚动行为。

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

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

JScrollPane提供的构造器中,这两个允许您在创建滚动窗格时设置滚动条策略:

  1. JScrollPane(Component, int, int)
  2. JScrollPane(int, int)

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

政策 描述
VERTICAL_SCROLLBAR_AS_NEEDED

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

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

Decorations on a scroll pane

如图所示,ScrollDemo中的滚动窗格具有自定义行和列标题。另外,因为所有四个边都是填充的,所以存在所有四个角。该程序自定义三个角 - 两个只用Rule的颜色填充它们的区域,另一个包含一个切换按钮。第四个角(右下角)是滚动窗格提供的默认角。请注意,由于行示例和列标题始终存在于此示例中,因此切换按钮也始终存在。

如果角落包含用户始终需要访问的控件,请确保始终存在与角落相交的边。例如,如果此应用程序将切换放置在滚动条相交的右下角,则如果用户调整窗口大小并且甚至其中一个滚动条消失,则切换将消失。

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

  1. //Where the member variables are defined:
  2. private Rule columnView;
  3. private Rule rowView;
  4. ...
  5. //Where the GUI is initialized:
  6. ImageIcon bee = createImageIcon("images/flyingBee.jpg");
  7. ...
  8. //Create the row and column headers.
  9. columnView = new Rule(Rule.HORIZONTAL, true);
  10. rowView = new Rule(Rule.VERTICAL, true);
  11. ...
  12. pictureScrollPane.setColumnHeaderView(columnView);
  13. pictureScrollPane.setRowHeaderView(rowView);

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

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

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

  1. //Create the corners.
  2. JPanel buttonCorner = new JPanel(); //use FlowLayout
  3. isMetric = new JToggleButton("cm", true);
  4. isMetric.setFont(new Font("SansSerif", Font.PLAIN, 11));
  5. isMetric.setMargin(new Insets(2,2,2,2));
  6. isMetric.addItemListener(this);
  7. buttonCorner.add(isMetric);
  8. ...
  9. //Set the corners.
  10. pictureScrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER,
  11. buttonCorner);
  12. pictureScrollPane.setCorner(JScrollPane.LOWER_LEFT_CORNER,
  13. new Corner());
  14. pictureScrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER,
  15. new Corner());

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

从代码中可以看出,常量表示角位置。该图显示了每个位置的常量:

Corner constants

常数在 ScrollPaneConstants 接口中定义,JScrollPane实现。

要自定义客户端组件与其滚动窗格交互的方式,可以使组件实现 Scrollable 接口。通过实现Scrollable,客户端可以指定用于查看它的视口的大小以及滚动条以显示滚动条上不同控件的点击量。您还可以指定视图是否应跟踪视口的大小。这通常在视口大于视图时使用,但视图应填充可用空间。


Note: If you can’t or don’t want to implement a scrollable client, you can specify the unit and block increments using the setUnitIncrement and setBlockIncrement methods of JScrollBar. For example, the following code sets the unit increment for vertical scrolling to 10 pixels:

  1. scrollPane.getVerticalScrollBar().setUnitIncrement(10);

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

The parts of a scroll bar

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

ScrollDemo程序的客户端是 ScrollablePictureScrollablePictureJLabel的子类,提供了所有五种Scrollable方法的实现:

  • getScrollableBlockIncrement
  • getScrollableUnitIncrement
  • getPreferredScrollableViewportSize
  • getScrollableTracksViewportHeight
  • getScrollableTracksViewportWidth

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

只要用户单击滚动条上的其中一个按钮,滚动窗格就会调用客户端的getScrollableUnitIncrement方法。只要客户端实现 Scrollable,就是这样。此方法返回要滚动的像素数。此方法的一个明显实现是返回标题标尺上刻度线之间的像素数。然而,ScrollablePicture做了一些不同的事情:它返回将图像定位在刻度线边界上所需的值。这是实现:

  1. public int getScrollableUnitIncrement(Rectangle visibleRect,
  2. int orientation,
  3. int direction) {
  4. //Get the current position.
  5. int currentPosition = 0;
  6. if (orientation == SwingConstants.HORIZONTAL) {
  7. currentPosition = visibleRect.x;
  8. } else {
  9. currentPosition = visibleRect.y;
  10. }
  11. //Return the number of pixels between currentPosition
  12. //and the nearest tick mark in the indicated direction.
  13. if (direction < 0) {
  14. int newPosition = currentPosition -
  15. (currentPosition / maxUnitIncrement)
  16. * maxUnitIncrement;
  17. return (newPosition == 0) ? maxUnitIncrement : newPosition;
  18. } else {
  19. return ((currentPosition / maxUnitIncrement) + 1)
  20. * maxUnitIncrement
  21. - currentPosition;
  22. }
  23. }

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

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

  1. public int getScrollableBlockIncrement(Rectangle visibleRect,
  2. int orientation,
  3. int direction) {
  4. if (orientation == SwingConstants.HORIZONTAL)
  5. return visibleRect.width - maxUnitIncrement;
  6. else
  7. return visibleRect.height - maxUnitIncrement;
  8. }

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

ScrollablePicture.java还有一些代码,这些代码不是Scrollable接口所必需的,但在可滚动组件中很常见:鼠标移动监听器,允许用户通过拖动来滚动图片。以下代码段中的粗体代码通过拖动实现滚动:

  1. public class ScrollablePicture extends JLabel
  2. implements Scrollable,
  3. MouseMotionListener {
  4. ...
  5. public ScrollablePicture(...) {
  6. ...
  7. setAutoscrolls(true); //enable synthetic drag events
  8. addMouseMotionListener(this); //handle mouse drags
  9. }
  10. ...
  11. public void mouseDragged(MouseEvent e) {
  12. //The user is dragging us, so scroll!
  13. Rectangle r = new Rectangle(e.getX(), e.getY(), 1, 1);
  14. scrollRectToVisible(r);
  15. }
  16. ...
  17. }

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

除非您明确设置滚动窗格的首选大小,否则滚动窗格将根据其九个组件(视口,如果存在,两个滚动条,行和列标题以及四个角)的首选大小计算它。最大的因素,也是大多数程序员关心的因素,是用于显示客户端的视口的大小。

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

如果客户端是滚动浏览器,则滚动窗格使用客户端的getPreferredScrollableViewportSize方法返回的值来计算其视口的大小。此方法的实现通常会报告滚动的首选大小,该大小小于组件的标准首选大小。例如,默认情况下,JList实现getPreferredScrollableViewportSize返回的值足以显示 8 行。

滚动精明的类,如列出文本组件,通常提供一种或多种方法让程序员影响从getPreferredScrollableViewportSize返回的大小。例如,您可以通过调用setVisibleRowCount方法设置列表或树中的可见行数。列表或树负责计算显示该行数所需的大小。

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

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

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

A snapshot of ScrollDemo2

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

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

  1. if (changed) {
  2. //Update client's preferred size because
  3. //the area taken up by the graphics has
  4. //gotten larger or smaller (if cleared).
  5. drawingArea.setPreferredSize(/* the new size */);
  6. //Let the scroll pane know to update itself
  7. //and its scroll bars.
  8. drawingArea.revalidate();
  9. }

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

有关客户端对象更改大小的另一个示例,请参阅 SplitPaneDemo

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

使用滚动窗格的 API 属于以下类别:

(JScrollPane constructors and methods) | 方法或构造器 | 目的 | | :— | :— | | JScrollPane() JScrollPane(组件) JScrollPane(int,int) JScrollPane(Component,int,int ) | 创建滚动窗格。 Component参数(如果存在)设置滚动窗格的客户端。两个int参数(如果存在)分别设置垂直和水平滚动条策略。 | | void setViewportView(Component) | 设置滚动窗格的客户端。 | | void setVerticalScrollBarPolicy(int) int getVerticalScrollBarPolicy() | 设置或获取垂直滚动策略。 ScrollPaneConstants定义了三个用于指定此策略的值:VERTICAL_SCROLLBAR_AS_NEEDED(默认值),VERTICAL_SCROLLBAR_ALWAYSVERTICAL_SCROLLBAR_NEVER。 | | void setHorizo​​ntalScrollBarPolicy(int) int getHorizo​​ntalScrollBarPolicy() | 设置或获取水平滚动策略。 ScrollPaneConstants定义了三个用于指定此策略的值:HORIZONTAL_SCROLLBAR_AS_NEEDED(默认值),HORIZONTAL_SCROLLBAR_ALWAYSHORIZONTAL_SCROLLBAR_NEVER。 | | void setViewportBorder(Border) Border getViewportBorder() | 设置或获取视口周围的边框。这比在组件上设置边框更受欢迎。 | | boolean isWheelScrollingEnabled() | 设置或获取是否响应鼠标滚轮进行滚动。默认情况下启用鼠标滚轮滚动。 |

(JScrollPane methods) | 方法 | 目的 | | :— | :— | | 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_CORNER,和UPPER_TRAILING_CORNER。 |

方法 目的
int getScrollableUnitIncrement(Rectangle,int,int)

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

方法 目的
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的示例以及描述这些示例的示例。

在哪里描述 笔记
ToolBarDemo 本节,

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

如果您使用 JavaFX 进行编程,请参阅滚动窗格