11.2 JSplitPane类
类似于Box容器,JSplitPane容器允许我们在单行或单列中显示组件。然而Box可以包含任意数量的组件,JSplitPane只可以用来显示两个组件。组件可以变化尺寸并通过一个可移动的分隔栏进行分隔。分隔栏可以使得用户可以通过拖拽分隔栏来调整所包含组件的尺寸。图11-5显示了垂直与水平分割面板,同时显示在移动分隔栏之前与之后的样子。
11.2.1 创建JSplitPane
JSplitPane有五个构造函数。通过这些构造函数,我们可以初始化所包含组件对的方向,设置continuousLayout属性或是为容器初始化组件对。
- public JSplitPane()
- JSplitPane splitPane = new JSplitPane();
- public JSplitPane(int newOrientation)
- JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
- public JSplitPane(int newOrientation, boolean newContinuousLayout)
- JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true);
- public JSplitPane(int newOrientation, Component newLeftComponent,
- Component newRightComponent)
- JComponent topComponent = new JButton("Top Button");
- JComponent bottomComponent = new JButton("Bottom Button");
- JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
- topComponent, bottomComponent);
- public JSplitPane(int newOrientation, boolean newContinuousLayout,
- Component newLeftComponent, Component newRightComponent)
- JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true,
- topComponent, bottomComponent);
除非特别指定,默认方向为水平方向。方向可以通过JSplitPane的常量VERTICAL_SPLIT或HORIZONTAL_SPLIT来指定。continuousLayout属性设置瘊定了当用户拖动分隔栏时分隔面板如何响应。当设置为false(默认)时,在拖动时只有分隔符被重绘。当设置为true时,在用户拖拽分隔栏时,JSplitPane会调整尺寸并重绘分隔栏每一边的组件。
注意,如果方向为JSplitPane.VERTICAL_SPLIT,我们可以将上部的组件看作左侧组件,而将下部组件看作右侧组件。
如果我们使用无参数的构造函数,分隔面板内的初始组件集合由按钮组成(两个JButton组件)。其他的两个构造函数显示的设置了初始的两个组件。奇怪的是,其余的两个构造函数默认情况下并没有提供容器内的组件。要添加或修改JSplitPane内的组件,请参看稍后的“修改JSplitPane组件”一节。
11.2.2 JSplitPane属性
表11-2显示了JSplitPane的17个属性。
![](https://i-blog.csdnimg.cn/blog_migrate/c1c4da2f699eafe14b3a26c60760d3c5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6e8b986147792fc4ccc5775144b2da5e.png)
设置方向
除了在构造函数中初始化方向以外,我们可以通过将方向属性修改为JSplitPane.VERTICAL_SPLIT或是JSplitPane.HORIZONTAL_SPLIT来修改JSplitPane方向。如果我们试着将属性修改为非等同的设置,则会抛出IllegalArgumentException。
不推荐在运行时动态修改方向,因为这会使用户感到迷惑。然而,如果我们正在使用可视化开发工具,我们可以在创建JSplitPane之后显示设置方向属性。当没有进行可视化编程时,我们通常在创建JSplitPane时初始化方向。
修改JSplitPane组件
有四个读写属性可以用来处理JSplitPane内组件的不同位置:bottomComponent, leftComponent, rightComponent与topComponent。事实上,这四个属性表示两种内部组件:左边与上部组件是一种;右边与下部组件表示另一种。
我们应该使用与我们的JSplitPane的方向相适应的属性。使用不合适的属性方法会使得程序员的维护生命十分困难。想像一下,在创建用户界面之后,在六个月之后看到如下的代码:
- JComponent leftButton = new JButton("Left");
- JComponent rightButton = new JButton("Right");
- JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
- splitPane.setLeftComponent(leftButton);
- splitPane.setRightComponent(rightButton);
如果我们看一下代码,基于变量名以及setXXXComponent()方法的使用,我们也许会认为屏幕在左边包含一个按钮,而右边也是一个按钮。但是实例化的JSplitPane具有一个垂直方向,所创建的界面如图11-6所示。所用的变量是按钮的标签,而不是他们的位置。
如果setTopComponent()与setBottomComponent()方法使用更好的变量名,代码会更容易理解:
- JComponent topButton = new JButton("Left");
- JComponent bottomButton = new JButton("Right");
- JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
- splitPane.setTopComponent(topButton);
- splitPane.setBottomComponent(bottomButton);
移动JSplitPane分隔符
初始时,分隔符显示在上部组件的下面或是左边组件的右边合适尺寸处。任何时候,我们可以通过调用JSplitPane的restToPreferredSizes()方法来重新设置分隔位置。如果我们要编程来定位分隔符,我们可以通过setDividerLocation(newLocation)来修改dividerLocation属性。这个属性可以修改一个int位置,表示距离上部或左边的绝对距离,或者是设置为一个0.0与1.0之间的double值,表示JSplitPane容器宽度的百分比。
注意,如果将属性设置为0.0与1.0范围之外的double值则会抛出IllegalArgumentException。
如果我们要设置分隔符的位置,我们必须等到组件已经被实现。本质上,这就意味着组件必须可见。有多种方法可以实现这一操作,最直接的方法就是向JSplitPane关联一个HierarchyListener,并且监听HierarchyEvent何时变为SHOWING_CHANGED类型。下面的代码片段演示了这一操作,将分隔符位置修改为75%。
- HierarchyListener hierarchyListener = new HierarchyListener() {
- public void hierarchyChanged(HierarchyEvent e) {
- long flags = e.getChangeFlags();
- if ((flags & HierarchyEvent.SHOWING_CHANGED) ==
- HierarchyEvent.SHOWING_CHANGED) {
- splitPane.setDividerLocation(.75);
- }
- }
- };
- splitPane.addHierarchyListener(hierarchyListener);
尽管我们可以使用double值设置dividerLocation属性,我们只会获得了一个标识绝对位置的int值。
调整组件尺寸与使用可扩展的分隔符
对于JSplitPane内的组件调整尺寸存在限制。JSplitPane会考虑到每一个所包含组件的最小尺寸。如果拖动分隔符使得一个组件缩小到小于其最小尺寸,则滚动面板不会允许用户拖动分隔符超过这个最小尺寸。
注意,我们可以编程实现将分隔符放在任意位置,甚至是使得组件小于其最小尺寸。然而这并不是一个好主意,因为组件最小尺寸的存在是有原因的。
如果组件的最小维度对于JSplitPane来说过大,我们需要修改组件的最小尺寸,从而分隔符可以使用组件的空间。对于AWT组件,修改一个标准组件的最小尺寸需要子类派生。对于Swing组件,我们可以简单的通过一个新的Dimension来调用JComponent的setMinimumSize()方法。然而,最小尺寸的设置要合理。如果我们显式的缩小其最小尺寸,组件就不会正常的工作。
有一个更好的方法可以使得一个组件比其他组件占用更多的空间:将JSplitPane的onTouchExpandable属性设置为true。当这个属性为真时,就会为分隔符添加一个图标,从而使得用户可以完全折叠起两个组件中的一个来为另一个组件指定全部的空间。在图11-7的盒子中,图标是一个上下箭头的组合。
图11-7显示了这个图标显示的样子(通过Ocean观感渲染)并且演示了在选择分隔符上的向上箭头来将下部的组件扩展为其全部尺寸时的样子。再一次点击分隔符上的图标会使得组件又回到其先前的位置。点击分隔符上图标以外的位置会将分隔符定位到使得折叠的组件位于其最优尺寸处。
注意,并没有较容易的方法来修改扩展分隔符的图标或是修改分隔符如何渲染。这两方面都是通过BasicSplitPaneDivider子类来定义并且在用于特定观感类型的BasicSplitPaneUI子类的createDefaultDivider()方法中创建的。我们可以简单修改分隔符周围的边框,这是一个自定义边框。
lastDividerLocation属性可以使得我们或是系统查询前一个分隔符位置。当用户选择maximizer图标来取消JSplitPane中的一个组件的最小化时,JSplitPane会使用这个属性。
小心,要小心其最小尺寸是基于容器尺寸或是其初始尺寸的组件。将这些属性放置在JSplitPane中也许会要求我们手动设置组件的minimum或是最优尺寸。当用在JSplitPane中时最常引起问题的组件就是JTextArea与JScrollPane。
调整JSplitPane尺寸
如果在JSplitPane中存在其所包含的组件的最优尺寸所不需要的额外空间时,这个空间会依据resizeWeight属性设置进行分配。这个属性的初始设置为0.0,意味着右边或是下边的组件会获得额外的空间。将这个设置修改为1.0会将所有的空间指定给左边或上部的组件。0.5则会在两个组件之间分隔面板。图11-8显示了这些变化的效果。
11.2.3 监听JSplitPane属性变化
JSplitPane类定义了下列的常量来帮助监听边界属性的变化:
- CONTINUOUS_LAYOUT_PROPERTY
- DIVIDER_LOCATION_PROPERTY
- DIVIDER_SIZE_PROPERTY
- LAST_DIVIDER_LOCATION_PROPERTY
- ONE_TOUCH_EXPANDABLE_PROPERTY
- ORIENTATION_PROPERTY
- RESIZE_WEIGHT_PROPERTY
监听用户何时移动分隔符的一个方法就是监听lastDividerLocation属性的变化。列表11-2中的示例将一个PropertyChangeListener关联到JSplitPane,从而显示当前的分隔符位置,当前的最后位置以及前一个最后位置。分隔符上面与下面的组件是OvalPanel类(在第四章中讨论),绘制来填充组件的维度。这个组件有助于演示将continuousLayout属性设置true的效果状态。
- package swingstudy.ch11;
- import java.awt.BorderLayout;
- import java.awt.EventQueue;
- import java.beans.PropertyChangeEvent;
- import java.beans.PropertyChangeListener;
- import javax.swing.JComponent;
- import javax.swing.JFrame;
- import javax.swing.JSplitPane;
- import swingstudy.ch04.OvalPanel;
- public class PropertySplit {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- Runnable runner = new Runnable() {
- public void run() {
- JFrame frame = new JFrame("Property Split");
- frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- // create/configure split pane
- JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
- splitPane.setContinuousLayout(true);
- splitPane.setOneTouchExpandable(true);
- // create top component
- JComponent topComponent = new OvalPanel();
- splitPane.setTopComponent(topComponent);
- // create bottom component
- JComponent bottomComponent = new OvalPanel();
- splitPane.setBottomComponent(bottomComponent);
- // create PropertyChangeListener
- PropertyChangeListener propertyChangeListener = new PropertyChangeListener() {
- public void propertyChange(PropertyChangeEvent event) {
- JSplitPane sourceSplitPane = (JSplitPane)event.getSource();
- String propertyName = event.getPropertyName();
- if(propertyName.equals(JSplitPane.LAST_DIVIDER_LOCATION_PROPERTY)){
- int current = sourceSplitPane.getDividerLocation();
- System.out.println("Current: "+current);
- Integer last = (Integer)event.getNewValue();
- System.out.println("Last: "+last);
- Integer priorLast = (Integer)event.getOldValue();
- System.out.println("Prior last: "+priorLast);
- }
- }
- };
- // attach listener
- splitPane.addPropertyChangeListener(propertyChangeListener);
- frame.add(splitPane, BorderLayout.CENTER);
- frame.setSize(300, 150);
- frame.setVisible(true);
- }
- };
- EventQueue.invokeLater(runner);
- }
- }
如下面的示例输出所示,当我们运行前面的程序时,我们会注意到lastDividerLocation属性的变化来反映分隔符的拖动。当用户停止拖动分隔符时,最后设置被设置为dividerLocation属性的前一个设置,而不是用户开始拖动时的初始设置值。当用户拖动分隔符时,当前值变为最后一个值然后变为前一个最后值。
Current: 11 Last: -1 Prior last: 0 Current: 12 Last: 11 Prior last: -1 Current: 12 Last: 12 Prior last: 11 Current: 12 Last: 11 Prior last: 12 Current: 15 Last: 12 Prior last: 11 Current: 15 Last: 15 Prior last: 12 Current: 15 Last: 12 Prior last: 15 Current: 112 Last: 15 Prior last: 12 Current: 112 Last: 112 Prior last: 15 Current: 112 Last: 15 Prior last: 112
注意,PropertyChangeListener并不支持JSplitPane类的BOTTOM, DIVIDER, LEFT, RIGHT与TOP常量。相反,他们是为add(Component component, Object constraints)方法所用的内部约束。
11.2.4 自定义JSplitPane类型
每一个可安装的Swing观感提供了不同的JSplitPane外观以及组件的默认UIResource值集合。图11-9显示了预安装的观感类型集合的JSplitPane容器外观:Motif,Windows以及Ocean。
表11-3显示了JSplitPane可用的UIResource相关的属性集合。对于JSplitPane组件,有25个不同的属性,包括3个分隔符特定的属性。