如何使用拆分窗格

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

JSplitPane 显示两个组件,可以并排放置,也可以一个放在另一个上面。通过拖动组件之间出现的分隔符,用户可以指定拆分窗格的总区域到达每个组件的程度。您可以通过在分割窗格内放置拆分窗格来划分三个或更多组件之间的屏幕空间,如嵌套拆分窗格中所述。

您经常将每个组件放入滚动窗格,而不是将感兴趣的组件直接添加到拆分窗格中。然后,将滚动窗格放入拆分窗格中。这允许用户查看感兴趣组件的任何部分,而不需要组件占用大量屏幕空间或适于在不同数量的屏幕空间中显示自身。

这是一个应用程序的图片,它使用拆分窗格并排显示列表和图像:

A snapshot of SplitPaneDemo


Try this:

  1. 单击“启动”按钮以使用 Java™Web Start下载 JDK 7 或更高版本)运行 SplitPaneDemo。或者,要自己编译并运行示例,请参考示例索引Launches the SplitPaneDemo example

  2. 拖动将列表和图像分成左侧或右侧的凹坑线。尝试将分隔线一直拖到窗口的边缘。

  3. 单击分隔线上的小箭头以隐藏/展开左侧或右侧组件。

以下是SplitPaneDemo中用于创建和设置拆分窗格的代码。

  1. //Create a split pane with the two scroll panes in it.
  2. splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
  3. listScrollPane, pictureScrollPane);
  4. splitPane.setOneTouchExpandable(true);
  5. splitPane.setDividerLocation(150);
  6. //Provide minimum sizes for the two components in the split pane
  7. Dimension minimumSize = new Dimension(100, 50);
  8. listScrollPane.setMinimumSize(minimumSize);
  9. pictureScrollPane.setMinimumSize(minimumSize);

此示例使用的构造器有三个参数。第一个表示分割方向。其他参数是放在拆分窗格中的两个组件。有关动态设置组件的JSplitPane方法的信息,请参阅在拆分窗格中设置组件。

此示例中的拆分窗格是水平拆分 - 两个组件并排显示 - 由构造器的JSplitPane.HORIZONTAL_SPLIT参数指定。拆分窗格提供另一个选项,使用JSplitPane.VERTICAL_SPLIT指定,将一个组件放在另一个组件之上。使用setOrientation方法创建拆分窗格后,可以更改拆分方向。

示例的拆分窗格中的分隔符顶部会出现两个小箭头。这些箭头允许用户通过单击折叠(然后展开)任一组件。当前外观确定默认情况下是否显示这些控件。在 Java 外观中,默认情况下它们处于关闭状态。 (请注意,并非所有外观都支持此功能。)示例使用setOneTouchExpandable方法打开它们。

拆分窗格的分隔符的范围部分取决于拆分窗格中组件的最小大小。有关详细信息,请参见定位分频器并限制其范围

本节的其余部分将介绍以下主题:

程序可以使用以下四种方法动态设置拆分窗格的两个组件:

  • setLeftComponent
  • setRightComponent
  • setTopComponent
  • setBottomComponent

无论拆分窗格的当前拆分方向如何,您都可以随时使用这些方法中的任何一种。调用setLeftComponentsetTopComponent是等效的,并将指定的组件设置在顶部或左侧位置,具体取决于拆分窗格的当前拆分方向。同样,对setRightComponentsetBottomComponent的调用也是等效的。这些方法用新的方法替换该位置中已有的任何组件。

与其他容器一样,JSplitPane支持add方法。拆分窗格将第一个组件添加到左侧或顶部位置。使用add的危险在于,您可能无意中调用它太多次,在这种情况下,拆分窗格的布局管理器将抛出一个相当深奥的异常。如果使用add方法并且已填充拆分窗格,则首先需要使用remove删除现有组件。

如果仅在拆分窗格中放置一个组件,则分隔符将粘贴在拆分窗格的右侧或底部,具体取决于其拆分方向。

要使拆分窗格正常工作,通常需要在拆分窗格中设置组件的最小大小,以及拆分窗格或其包含的组件的首选大小。选择应设置的尺寸是一项艺术,需要了解如何确定分割窗格的首选大小和分隔符位置。在我们详细介绍之前,让我们再看一下 SplitPaneDemo。或者,如果您赶时间,可以跳到规则列表


Try this:

  1. 单击“启动”按钮以使用 Java™Web Start下载 JDK 7 或更高版本)运行 SplitPaneDemo。或者,要自己编译并运行示例,请参考示例索引Launches the SplitPaneDemo example

    因为演示帧的大小是使用pack方法设置的,所以拆分窗格处于首选大小,SplitPaneDemo 恰好设置为。自动放置分隔线,使左侧组件处于其首选宽度,所有剩余空间均位于右侧组件。

  2. 使窗户更宽。 分隔符保持原样,额外的空间到达右侧的组件。
  3. 使窗口明显比首次出现时窄 - 可能是左侧组件的两倍宽。 同样,左侧组件的大小和分隔符位置保持不变。只有正确组件的大小会发生变化。
  4. 使窗口尽可能窄。 假设窗口使用 Java 外观提供的装饰,则窗口的大小不能小于拆分窗格的最小大小,这是由拆分窗格包含的组件的最小大小决定的。 SplitPaneDemo 显式设置这些包含组件的最小大小。
  5. 使窗口变宽,然后将分隔线拖到右边。 分频器仅在右侧组件的最小尺寸允许的范围内。如果将分隔线向左拖动,您将看到它也尊重左侧组件的最小尺寸。

现在您已经看到了分割窗格的默认行为,我们可以告诉您幕后发生了什么以及如何影响它。在本讨论中,当我们引用组件的首选或最小大小时,如果拆分窗格是水平的,则通常表示组件的首选或最小宽度;如果拆分窗格是垂直的,则通常表示其首选或最小高度。

默认情况下,初始化拆分窗格的首选大小和分隔符位置,以便拆分窗格中的两个组件处于其首选大小。如果拆分窗格未以此首选大小显示且程序未明确设置分隔符的位置,则分隔符的初始位置(以及两个组件的大小)取决于分割窗格属性,称为调整体重。如果拆分窗格最初处于其首选大小或更大,则在调整调整大小权重之前,所包含的组件以其首选大小开始。如果拆分窗格最初太小而无法以其首选尺寸显示两个组件,则在调整调整大小权重之前,它们将以最小大小开始。

拆分窗格的调整大小权重介于 0.0 和 1.0 之间,并确定在设置拆分窗格大小时如何在两个包含的组件之间分配空间 - 无论是以编程方式还是由用户调整拆分窗格大小(例如,放大其包含窗口) 。默认情况下,拆分窗格的调整大小为 0.0,表示左侧或顶部组件的大小是固定的,右侧或底部组件调整其大小以适合剩余空间。将调整大小权重设置为 0.5 会在两个组件之间平均分配任何额外或缺失的空间。将调整大小权重设置为 1.0 会使右侧或底部组件的大小保持固定。但是,当用户拖动分隔符时,调整大小权重不起作用。

只要既不包含组件低于其最小尺寸,用户也可以将分隔线拖动到任何位置。如果分隔器具有单触按钮,则用户可以使用它们使分隔器完全移动到一侧或另一侧 - 无论组件的最小尺寸是多少。

现在您已了解影响拆分窗格大小和分隔符位置的因素,下面是一些使它们运行良好的规则:

  • 要确保在拆分窗格处于其首选大小时可以拖动分隔符,请确保一个或两个包含的组件的最小大小小于包含的组件的首选大小。您可以通过调用组件上的setMinimumSize或覆盖其getMinimumSize方法来设置组件的最小大小。例如,如果您希望用户能够将分频器一直拖到两侧:

    1. Dimension minimumSize = new Dimension(0, 0);
    2. leftComponent.setMinimumSize(minimumSize);
    3. rightComponent.setMinimumSize(minimumSize);
  • To guarantee that both contained components appear, make sure that either the split pane is initially at or above its preferred size, or the minimum sizes of the contained components are greater than zero.

    如果为 splitpane 指定了首选大小,通常会发生这种情况,这取决于包含拆分窗格的布局管理器。另一个选项是在拆分窗格上显式设置一个大于所包含组件大小的首选大小。

  • 如果希望底部或右侧组件保持相同的大小,并且当拆分窗格变大时顶部或左侧组件变得灵活,请将调整大小权重设置为 1.0。您可以通过调用setResizeWeight来执行此操作:

    1. splitPane.setResizeWeight(1.0);
  • 如果您希望拆分窗格的两半在拆分窗格的额外或删除空间中共享,请将调整大小权重设置为 0.5:

    1. splitPane.setResizeWeight(0.5);
  • 确保拆分窗格包含的每个组件都具有合理的首选大小。如果组件是使用布局管理器的面板,通常只需使用它返回的值。如果组件是滚动窗格,则您有几个选择。您可以在滚动窗格上调用setPreferredSize方法,在滚动窗格中调用组件上的相应方法(例如JListJTreesetVisibleRowCount方法)。

  • 确保拆分窗格包含的每个组件都可以在不同的空间量内合理显示。例如,包含多个组件的面板应使用以合理方式使用额外空间的布局管理器。
  • If you want to set the size of contained components to something other than their preferred sizes, use the setDividerLocation method. For example, to make the left component 150 pixels wide:

    1. splitPane.setDividerLocation(150 + splitPane.getInsets().left);

    Although the split pane does its best to honor the initial divider location (150 in this case), once the user drags the divider it may no longer be possible to drag to the programmatically specified size.

    要使正确的组件宽 150 像素:

    1. splitPane.setDividerLocation(splitPane.getSize().width
    2. - splitPane.getInsets().right
    3. - splitPane.getDividerSize()
    4. - 150);

    If the split pane is already visible, you can set the divider location as a percentage of the split pane. For example, to make 25% of the space go to left/top:

    1. splitPane.setDividerLocation(0.25);

    请注意,这是根据当前大小实现的,因此如果拆分窗格可见,则非常有用。

  • 要将拆分窗格布置为刚刚出现,可能会在过程中重新定位分隔符,请在拆分窗格上调用resetToPreferredSizes()


    Note: Just changing the contained components’ preferred sizes — even if you invoke revalidate afterwards — is not enough to cause the split pane to lay itself out again. You must invoke resetToPreferredSizes as well.


以下快照显示了一个名为 SplitPaneDividerDemo 的示例,该示例演示了拆分窗格组件大小和分隔符放置。

A snapshot of SplitPaneDividerDemo

与 SplitPaneDemo 一样,SplitPaneDividerDemo 具有带单触按钮的水平分割窗格。 SplitPaneDividerDemo 具有以下附加功能:

  • 分割窗格的调整大小权重被明确设置(为 0.5)。
  • 拆分窗格以其默认首选大小显示。
  • 窗口底部的“重置”按钮调用拆分窗格上的resetToPreferredSizes
  • 拆分窗格中的组件是名为SizeDisplayer的自定义JComponent子类的实例。 SizeDisplayer在淡化(和可选)图像的背景下显示可选文本。更重要的是,它具有显示其首选和最小尺寸的矩形。
  • SplitPaneDividerDemo 将其SizeDisplayer设置为具有相同的首选大小(由于它们显示的图像同样大),但最小大小不相等。

Try this:

  1. 单击“启动”按钮以使用 Java™Web Start下载 JDK 7 或更高版本)运行 SplitPaneDividerDemo。或者,要自己编译并运行示例,请参考示例索引Launches the SplitPaneDividerDemo example

    因为演示帧的大小是使用pack方法设置的,所以拆分窗格处于其首选大小,默认情况下大小足够SizeDisplayer是他们的首选尺寸。每个SizeDisplayer的优选尺寸由红色矩形表示。自动放置分隔线,使两个组件都处于其首选宽度。

  2. 使窗户更宽。 因为分割窗格的调整大小为 0.5,所以额外的空间在左右组件之间均匀分配。分频器相应地移动。
  3. 使窗口尽可能窄。 假设窗口使用 Java 外观提供的装饰,则不会让窗口的大小小于分割窗格的最小大小,这是由它包含的SizeDisplayers的最小大小决定的。每个SizeDisplayer的最小尺寸由亮蓝色矩形表示。
  4. 使窗口稍微宽一点,然后将分隔线拖到右边。 分频器仅在右侧组件的最小尺寸允许的范围内。
  5. 确保拆分窗格小于其首选大小后,单击“重置”按钮。 请注意,两个SizeDisplayer以不同的尺寸显示,即使应用程序出现时它们的尺寸相同。原因是尽管它们的首选尺寸相同,但它们的最小尺寸却不相同。由于拆分窗格无法以其首选大小或更大尺寸显示它们,因此使用最小尺寸将它们展开。由于拆分窗格的调整大小为 0.5,因此剩余空间在组件之间平均分配。
  6. 加宽分割窗格,使其足够大,使SizeDisplayer以其首选大小显示,然后单击“重置”按钮。 分隔符再次置于中间,因此两个组件的大小相同。

以下是为 SplitPaneDividerDemo 创建 GUI 的代码:

  1. public class SplitPaneDividerDemo extends JPanel ... {
  2. private JSplitPane splitPane;
  3. public SplitPaneDividerDemo() {
  4. super(new BorderLayout());
  5. Font font = new Font("Serif", Font.ITALIC, 24);
  6. ImageIcon icon = createImageIcon("images/Cat.gif");
  7. SizeDisplayer sd1 = new SizeDisplayer("left", icon);
  8. sd1.setMinimumSize(new Dimension(30,30));
  9. sd1.setFont(font);
  10. icon = createImageIcon("images/Dog.gif");
  11. SizeDisplayer sd2 = new SizeDisplayer("right", icon);
  12. sd2.setMinimumSize(new Dimension(60,60));
  13. sd2.setFont(font);
  14. splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
  15. sd1, sd2);
  16. splitPane.setResizeWeight(0.5);
  17. splitPane.setOneTouchExpandable(true);
  18. splitPane.setContinuousLayout(true);
  19. add(splitPane, BorderLayout.CENTER);
  20. add(createControlPanel(), BorderLayout.PAGE_END);
  21. }
  22. ...
  23. }

代码是相当自我解释的,除了调用setContinuousLayout。将 continuousLayout 属性设置为 true 会在用户移动分隔符时连续绘制分割窗格的内容。默认情况下,连续布局未启用,因为它可能会对性能产生负面影响。但是,在此演示中使用它时,将分割窗格的组件尽可能更新可以改善用户体验是有意义的。

这是一个程序的图片,通过将一个拆分窗格嵌套在另一个内部来实现三向拆分:

A snapshot of SplitPaneDemo2

如果拆分窗格的顶部看起来很熟悉,那是因为程序将SplitPaneDemo创建的拆分窗格放在第二个拆分窗格中。简单的JLabel是第二个拆分窗格中的另一个组件。这不是嵌套拆分窗格的最实际用途,但它得到了重点。

Launches the SplitPaneDemo2 example

这是代码的有趣部分,您可以在 SplitPaneDemo2.java 中找到:

  1. //Create an instance of SplitPaneDemo
  2. SplitPaneDemo splitPaneDemo = new SplitPaneDemo();
  3. JSplitPane top = splitPaneDemo.getSplitPane();
  4. ...
  5. //Create a regular old label
  6. label = new JLabel("Click on an image name in the list.",
  7. JLabel.CENTER);
  8. //Create a split pane and put "top" (a split pane)
  9. //and JLabel instance in it.
  10. JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
  11. top, label);

有关修复嵌套分割窗格时可能出现的边框问题的信息,请参阅解决常见组件问题

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

使用列表的 API 分为以下几类:

方法或构造器 目的
JSplitPane()

JSplitPane(int) JSplitPane(int,boolean) JSplitPane(int,Component,Component ) JSplitPane(int,boolean,Component,Component) | 创建拆分窗格。如果存在,int参数指示分割窗格的方向,HORIZONTAL_SPLIT(默认值)或VERTICAL_SPLITboolean参数(如果存在)设置组件是否在用户拖动拆分窗格时不断重新绘制。如果未指定,则关闭此选项(称为连续布局)。 Component参数分别设置初始左侧和右侧或顶部和底部组件。 | | void setOrientation(int) int getOrientation() | 设置或获取拆分窗格的方向。使用JSplitPane中定义的HORIZONTAL_SPLITVERTICAL_SPLIT。如果未指定,则拆分窗格将水平拆分。 | | void setDividerSize(int) int getDividerSize() | 设置或获取分隔符的大小(以像素为单位)。 | | void setContinuousLayout(boolean) boolean isContinuousLayout() | 在用户拖动分隔符时设置或获取拆分窗格的组件是否连续布局和绘制。默认情况下,连续布局已关闭。 | | void setOneTouchExpandable(boolean) boolean isOneTouchExpandable() | 设置或获取拆分窗格是否在分隔符上显示控件以展开/折叠分隔符。默认值取决于外观。在 Java 外观中,它默认是关闭的。 |

方法 目的
void setTopComponent(Component)

void setBottomComponent(Component) void setLeftComponent(Component) void setRightComponent(Component) 组件 getTopComponent() 组件 getBottomComponent() 组件 getLeftComponent() 组件 getRightComponent () | 设置或获取指定的组件。无论拆分窗格的方向如何,每种方法都有效。顶部和左侧相同,底部和右侧相同。 | | void remove(Component) void removeAll() | 从拆分窗格中删除指示的组件。 | | void add(Component) | 将组件添加到拆分窗格。您只能将两个组件添加到拆分窗格。添加的第一个组件是顶部/左侧组件。添加的第二个组件是底部/右侧组件。任何添加更多组件的尝试都会导致异常。 |

方法 目的
void setDividerLocation(double)

void setDividerLocation(int) int getDividerLocation() | 设置或获取当前分隔符位置。设置分频器位置时,可以将新位置指定为百分比(double)或像素位置(int)。 | | void resetToPreferredSizes() | 移动分隔线,使两个组件都处于其首选大小。除非另有说明,否则这是拆分窗格在启动时分割自身的方式。 | | void setLastDividerLocation(int) int getLastDividerLocation() | 设置或获取分隔符的先前位置。 | | int getMaximumDividerLocation() int getMinimumDividerLocation() | 获取分隔符的最小和最大位置。通过设置拆分窗格的两个组件的最小大小来隐式设置它们。 | | void setResizeWeight(float) float getResizeWeight() | 设置或获取拆分窗格的调整大小权重,介于 0.0(默认值)和 1.0 之间的值。有关使用调整大小权重的说明和示例,请参阅定位分频器和限制其范围。 |

此表显示了一些使用JSplitPane的示例以及描述这些示例的示例。

在哪里描述 笔记
SplitPaneDemo 此页面和如何使用列表 显示具有水平拆分的拆分窗格。
SplitPaneDividerDemo 这一页 演示如何使用组件大小信息和调整大小来定位分隔符。
SplitPaneDemo2 这一页 在拆分窗格中放置拆分窗格以创建三向拆分。
TreeDemo 如何使用树木 使用具有垂直拆分的拆分窗格将树(在滚动窗格中)与编辑器窗格(在滚动窗格中)分开。不使用一键式可扩展功能。
TextComponentDemo 文本组件功能 使用具有垂直拆分的拆分窗格,以在滚动窗格中分隔文本窗格和文本区域。
TextSamplerDemo 文本组件功能 使用具有垂直拆分的拆分窗格并调整权重 0.5,以在滚动窗格中分隔文本窗格和编辑器窗格。拆分窗格位于容器的右半部分,该容器具有相当复杂的布局。使用GridLayoutBorderLayout等布局管理器以及拆分窗格的调整大小权重,以确保滚动窗格中的组件共享所有额外空间。
ListSelectionDemo 如何编写列表选择监听器 使用具有垂直拆分的拆分窗格,从包含滚动窗格上方的组合框的下部窗格中分隔上部窗格,其中包含列表和表(均在滚动窗格中)。下部窗格使用边框布局来保持组合框小,滚动窗格贪婪的空间。