WisdomSoft - for your serial experiences.

9.5 レイヤーペイン

レイヤーペインは、重ねられたコンポーネントの描画順序を優先順位で管理します。

9.5.1 コンポーネントの階層

レイアウトマネージャが null に設定されているコンテナにコンポーネントを追加した場合、そのコンポーネントに設定されている位置とサイズがそのまま利用されます。通常は、プラットフォームや特定の状況に依存しないために、レイアウトマネージャを採用するべきですが、プログラムの用途によって、null のレイアウトが必要な場合もあるでしょう。

特に、null のレイアウトが必要になるのは、コンポーネントを重ね合わせるようなレイアウトを行う場合でしょう。レイアウトマネージャが null であれば、コンポーネントに設定されている位置とサイズが使われるため、場合によっては複数のコンポーネントが重なってしまいます。通常のアプリケーションであればこのような衝突は好ましくありません。

複数のコンポーネントの表示位置が重なってしまった場合、一方のコンポーネントが、もう一方のコンポーネントの一部を覆い隠すように表示されてしまいます。このとき、どちらのコンポーネントを優先して表示するかという情報を Z オーダーと呼びます。コンポーネントが表示される順序は、AWT アーキテクチャによって明確に定められており、最初に追加したコンポーネントが最も優先的な Z オーダーを持ち、最後に追加されたコンポーネントは、逆に優先度が低くなります。

場合によっては、こうしたコンポーネントの表示順序をプログラムで制御する必要があります。Z オーダーはコンテナにコンポーネントが追加された順序で決定されるものなので、プログラムから制御するには不便でした。そこで、プログラムでコンポーネントの表示順を具体的に表示することができるレイヤーペイン javax.swing.JLayeredPane クラスを利用します。

javax.swing.JLayeredPane クラス
java.lang.Object
  |
  +--java.awt.Component
        |
        +--java.awt.Container
              |
              +--javax.swing.JComponent
                    |
                    +--javax.swing.JLayeredPane
public class JLayeredPane extends JComponent implements Accessible

ルートペインにはレイヤーペインを内部で保持しており、このレイヤーペインにコンテンツペインを配置しています。コンテンツペインは他のコンポーネントを表示する土台となるためのコンテナなので、コンテンツペインはあらゆるコンポーネントよりも、原則としては優先順位が低くなくてはいけません。実際に、ルートペインは、レイヤーペインに含まれているコンテンツペインの優先度を極めて低く設定してあります。

表1 JLayeredPane クラスのコンストラクタと主なメソッド
コンストラクタ 解説
public JLayeredPane() 新しい JLayeredPane を作成する。
メソッド
public void setLayer(Component c, int layer) 指定されたコンポーネントのレイヤ属性を設定する。
public void setLayer(Component c, int layer, int position) 指定されたコンポーネントのレイヤ属性と相対位置を設定する。
public int getLayer(Component c) 指定された Component のレイヤ属性を返す。
public void setPosition(Component c, int position) コンポーネントを指定した相対位置に移動する。
public int getPosition(Component c) レイヤ内でのコンポーネントの相対位置を返す。
public int highestLayer() 現在のすべての子から最大レイヤ値を返す。子が存在しない場合には 0。
public int lowestLayer() 現在のすべての子から最小レイヤ値を返す。子が存在しない場合には 0。
public Component[] getComponentsInLayer(int layer) 指定されたレイヤのコンポーネントの配列を返す。
public int getComponentCountInLayer(int layer) 指定されたレイヤの現在の子の数を返す。
public void moveToFront(Component c) コンポーネントを現在のレイヤで一番上 (位置 0) に移動する。
public void moveToBack(Component c) コンポーネントを現在のレイヤで一番下 (位置 -1) に移動する。

表1に示す JLayeredPane クラスのメソッドを見ると、このクラスがコンポーネントを表示する順番を決定するためのプロパティが 3 つ存在することがわかります。1 つは、setLayer() メソッドなどで設定できるコンポーネントを配置するレイヤー、すなわち層を管理しています。層の値が高いコンポーネントほど優先順位が高いことを意味しています。このコンテナで最も重要なプロパティであると考えられます。

一方、setPosition() メソッドから相対位置というプロパティを設定することができます。これは、同じ層のコンポーネントの優先順位を表すためのプロパティで、例えば 100 番目の層に属するコンポーネントが複数存在する場合、さらにその中から相対的に優先順位を決定するのがこの値です。相対位置は、層とはことなり値が小さいほど優先され 0 が最も優先されるコンポーネントとなります。相対位置に -1 を指定すると、そのコンポーネントの優先順位は最下位となります。

層も相対位置も同じ値である場合は、従来の AWT コンポーネント同様に Z オーダーによって表示される順番が決定されます。しかし、通常は層と相対位置でコンポーネントの優先度を決定することができるでしょう。

JLayeredPane クラスは、フィールド定数に層を決定する参考基準値となる数値オブジェクトを公開しています。最下層から、通常のコンポーネントを配置する DEFAULT_LAYER、他のコンポーネントより優先されるツールコンポーネントなどを表示する PALETTE_LAYER、ダイアログを表示する MODAL_LAYER、ツールヒントなどの情報やコンボボックスを表示する POPUP_LAYER、そして、すべてのコンポーネントの上に表示する DRAG_LAYER という順番になります。コンポーネントの配置は、これらの数値を参考にすると良いでしょう。

コード1
import javax.swing.*;

public class Test extends JFrame {
	public static void main(String args[]) {
		JLayeredPane pane = new JLayeredPane();
		for(int i = 0 , x = 0 , y = 0 ; i < 5 ; i++ , x += 20 , y += 40) {
			int layer = (int)(Math.random() * (400));
			JButton button = new JButton("レイヤ : " + layer);
			button.setBounds(x , y , 200 , 50);
			pane.add(button);
			pane.setLayer(button , layer);
		}

		JFrame win = new Test();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.getContentPane().add(pane);
		win.show();
	}
}
実行結果
コード1 実行結果

コード1は、新しくレイヤーペインを作成し、これに 5 つのボタンを追加しています。レイヤーペインのレイアウトマネージャは意図的に null に設定し、それぞれのボタンは重なり合うような位置とサイズに設定されています。これらのボタンに割り当てる層は 0 ~ 400 の範囲で乱数を使って決定しています。このプログラムでは各ボタンに割り当てられる層の値が実行するたびに変化します。ボタンのテキストには層の番号が表示されているので、より大きな番号を持つボタンのほうが優先して表示されることを確認できるでしょう。