S-JIS[2009-03-21/2009-10-25] 変更履歴

JSplitPane(Swing)

JSplitPaneは、2つのコンポーネントを保持し、間の仕切りをユーザーが変更してペインの大きさを変えられるもの。
(ユーザーが大きさを変えられるというところがJPanel(GridLayout)等と異なる)


基本形

	/**
	 * スプリットペインの初期化
	 */
	private void initPane(Container c) {
		//エディター,テーブル,ツリースクロール等の他のコンポーネント
		Component p1 = 〜;
		Component p2 = 〜;

		JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT); //上下分割
		split.add(p1);
		split.add(p2);
		split.setDividerSize(4);

		c.add(split);
	}
		JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, false, p1, p2);
内容
HORIZONTAL_SPLIT 左右分割(横分割)。縦線で仕切られ、左右に動かせる。
VERTICAL_SPLIT 上下分割(縦分割)。横線(水平線)で仕切られ、上下に動かせる。

コンポーネントを2つ指定する必要があるが、同一のコンポーネント(インスタンス)を指定することは出来ない。
(1つのJEditorPaneを別々のJScrollPaneに入れて登録してもダメ)

間の仕切り線のことは『ディバイダー』と呼ぶ。
JSplitPaneに登録したコンポーネントによっては、ユーザーがディバイダーを動かせないこともある。
(JEditorPaneを2つだとダメで、JScrollPaneでラップしたJEditorPaneが2つならOK)
たぶん初期サイズによるのだと思うが、確認していない…いずれにしても、JScrollPaneと併せて使うのが良さそう。

メソッド 説明 備考
getDividerSize() ディバイダーのサイズ(太さ)を返す。 Look&Feelによって、デフォルトの幅は異なる。
(JDK1.6で試したら、)標準だと10、Windows外観だと5。
たぶん4くらいが一番無難。
setDividerSize(サイズ) ディバイダーのサイズを変更する。
getDividerLocation() ディバイダーの位置を返す。  
setDividerLocation(位置) ディバイダーの位置を設定する。
setDividerLocation(割合) ディバイダーの位置を0〜1.0の割合で指定する。
getMinimumDividerLocation() ディバイダーの有効な最小位置。[2009-10-25]
getMaximumDividerLocation() ディバイダーの有効な最大位置。[2009-10-25]

第1領域を最大(第2領域を最小)にする

左右分割の場合、デフォルトでは「左領域が推奨サイズ」で表示されるが、「右領域が最小サイズ」で表示したいことがある。[2009-10-25]
(別の表現をすれば、「左領域が最大サイズ」で表示したい)
(上下分割なら、デフォルトは「上領域が推奨サイズ」だが、「上領域が最大サイズ」「下領域が最小サイズ」で表示したい)

単純に考えると、getMaximumDividerLocation()で一番右の仕切り位置を取得し、それを新しい位置としてセットし直せばいい。
が、getMaximumDividerLocation()は一度表示されてから(厳密には各コンポーネントのサイズが計算されてから)でないと正しい値を返さない。
なので、例えばJFrameであればsetVisible(true)を呼び出して表示するのだから、それをオーバーライドすればいい。


さらに(これはJDK1.6.0_13のJSplitPaneの内部処理クラスであるBasicSplitPaneUIのバグだと思うのだが)、
マウスでドラッグしないと正しい値が返ってこない。
または、デフォルトのディバイダーサイズを変更している場合でないと(マウスでドラッグするまで)正しい値が返ってこない。

BasicSplitPaneUI#getMaximumDividerLocation():

	public int getMaximumDividerLocation(JSplitPane jc) {
		Dimension splitPaneSize = splitPane.getSize();
		int maxLoc = 0;
		Component rightC = splitPane.getRightComponent();
〜
		maxLoc -= dividerSize;
〜
		return Math.max(getMinimumDividerLocation(splitPane), maxLoc);
}

要するに各コンポーネントのサイズ(splitPaneというフィールドを使っていて、引数のjcは使われてないし(苦笑))を取得して最大位置を計算しているのだが、その中でdividerSizeというフィールドを使っている
これはディバイダーの幅を自前で保持しているフィールドらしいのだが、デフォルト状態では0のまま(爆)
つまりディバイダーの幅の分だけ間違った値が返ってくる。

JSplitPane#setDividerSize()以前と異なる値をセットする(同じ値だとBasicSplitPaneUI内の変更は行われない)か、マウスでドラッグすると
BasicSplitPaneUI#dividerSizeが正しい値になるので、JSplitPane#getMaximumDividerLocation()も正しい結果を返すようになる。


以上のことを勘案し、JFrameでJSplitPaneの第1領域(左・上)を最大にするには、以下のようにすればよい。

@SuppressWarnings("serial")
public class MaxSplitFrame extends JFrame {

	protected JSplitPane split;

	private boolean firstVisible;
〜
	protected void initPane(Container container) {
		Component comp1 = 〜;
		Component comp2 = 〜;

		split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		split.add(comp1);
		split.add(comp2);
		{ //BasicSplitPaneUIのバグ対応(ディバイダーサイズを変更する)
			int ds = split.getDividerSize();
			split.setDividerSize(ds + 1);
			split.setDividerSize(ds);
		}

		container.add(split);

		firstVisible = true;
	}

	@Override
	public void setVisible(boolean b) {
		super.setVisible(b);

		if (b && firstVisible) {
			//初めて表示するときだけ、ディバイダーの初期位置を最大位置にする
			firstVisible = false;
			int loc = split.getMaximumDividerLocation();
			split.setDividerLocation(loc);
		}
	}
}

Swing目次へ戻る / Java目次へ戻る
メールの送信先:ひしだま