このセクションは、近く予定されているJava SE 7リリースの機能と表記規則を反映するために更新されています。 最新のJDK7スナップショットはjava.net
からダウンロードできます。
JLayer
クラスは、柔軟でありながら強力なSwingコンポーネント向けのデコレータです。 このクラスを使用すると、基本となるコンポーネントを直接変更することなく、コンポーネント上に描画したり、コンポーネント・イベントに応答したりすることができます。
Java SE 7でのJLayer
クラスは、java.net
のJXLayerプロジェクトに似た性質を持ちます。 もともと、JLayer
クラスはJXLayer
プロジェクトをベースにしていましたが、それぞれのAPIは別々に展開されました。
このドキュメントでは、JLayer
クラスの機能を示す例について説明します。 また、完全なソース・コードも提供されています。
JLayer
クラスの使用javax.swing.JLayer
クラスが果たす役割は半分に過ぎず、 残りの半分はjavax.swing.plaf.LayerUI
クラスによって実行されます。 ここでは、JButton
オブジェクトの上に何らかのカスタム描画を表示する(JButton
のデコレート)とします。 その場合、デコレートの対象となるコンポーネントはターゲットです。
LayerUI
サブクラスのインスタンスを作成します。LayerUI
オブジェクトをラップするJLayer
オブジェクトを作成します。JLayer
オブジェクトをユーザー・インタフェースで使用します。たとえば、JPanel
サブクラスのインスタンスをJFrame
オブジェクトに追加する場合、通常、次のようなコードを使用します。
JFrame f = new JFrame(); JPanel panel = createPanel(); f.add (panel);
JPanel
オブジェクトをデコレートする場合は、代わりに次のようなコードを使用します。
JFrame f = new JFrame(); JPanel panel = createPanel(); LayerUI<JPanel> layerUI = new MyLayerUISubclass(); JLayer<JPanel> jlayer = new JLayer<JPanel>(panel, layerUI); f.add (jlayer);
JPanel
オブジェクトとLayerUI
オブジェクトの型に互換性を持たせるため、総称を使用します。 前の例では、JLayer
オブジェクトとLayerUI
オブジェクトの両方がJPanel
クラスに対して使用されています。
JLayer
クラスは通常、そのビュー・コンポーネントの正確な型を使用して総称化されますが、LayerUI
クラスは、その汎用パラメータまたは祖先のJLayer
クラスとともに使用するよう設計されています。
たとえば、LayerUI<JComponent>
オブジェクトをJLayer<AbstractButton>
オブジェクトと一緒に使用できます。
LayerUI
オブジェクトは、JLayer
オブジェクトのカスタム・デコレートとイベント処理を担当します。 LayerUI
サブクラスのインスタンスを作成すると、適切な総称型を持つすべてのJLayer
オブジェクトに対して、カスタムの動作が適用されます。 これは、JLayer
クラスがfinal
であるためであり、すべてのカスタム動作はLayerUI
サブクラスにカプセル化されているため、JLayer
のサブクラスを作成する必要はありません。
LayerUI
クラスの使用LayerUI
クラスは、その動作のほとんどをComponentUI
クラスから継承します。 次に、もっともよくオーバーライドされるメソッドを示します。
paint(Graphics g, JComponent c)
メソッドが呼び出されます。 Swingのレンダリングと同じ方法でコンポーネントをレンダリングするには、super.paint(g, c)
メソッドを呼び出します。LayerUI
のサブクラス・インスタンスがコンポーネントに関連付けられると、installUI(JComponent c)
メソッドが呼び出されます。 ここで、必要なすべての初期化処理を実行します。 渡されるコンポーネントは、対応するJLayer
オブジェクトになります。 JLayer
クラスのgetView()
メソッドを使用して、ターゲット・コンポーネントを取得します。LayerUI
のサブクラス・インスタンスとコンポーネントとの関連付けがなくなると、uninstallUI(JComponent c)
メソッドが呼び出されます。 必要に応じて、ここでクリーンアップ処理を実行します。JLayer
クラスを使用するには、適切なLayerUI
のサブクラスが必要です。 もっとも簡単な種類のLayerUI
クラスは、コンポーネントの描画方法を変更するものです。 たとえば、コンポーネント上に薄いカラー・グラデーションを描く例を次に挙げます。
class WallpaperLayerUI extends LayerUI<JComponent> { @Override public void paint(Graphics g, JComponent c) { super.paint(g, c); Graphics2D g2 = (Graphics2D) g.create(); int w = c.getWidth(); int h = c.getHeight(); g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, .5f)); g2.setPaint(new GradientPaint(0, 0, Color.yellow, 0, h, Color.red)); g2.fillRect(0, 0, w, h); g2.dispose(); } }
paint()
メソッドで、カスタムの描画が実行されます。 super.paint()
メソッドを呼び出すと、JPanel
オブジェクトのコンテンツが描画されます。 50%透明なコンポジットが設定されてから、カラー・グラデーションが描画されます。
LayerUI
のサブクラスを定義したら、その使用は簡単です。 次に、WallpaperLayerUI
クラスを使用するソース・コードを示します。
import java.awt.*; import javax.swing.*; import javax.swing.plaf.LayerUI; public class Wallpaper { public static void main(String[] args) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createUI(); } }); } public static void createUI() { JFrame f = new JFrame("Wallpaper"); JPanel panel = createPanel(); LayerUI<JComponent> layerUI = new WallpaperLayerUI(); JLayer<JComponent> jlayer = new JLayer<JComponent>(panel, layerUI); f.add (jlayer); f.setSize(300, 200); f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); f.setLocationRelativeTo (null); f.setVisible (true); } private static JPanel createPanel() { JPanel p = new JPanel(); ButtonGroup entreeGroup = new ButtonGroup(); JRadioButton radioButton; p.add(radioButton = new JRadioButton("Beef", true)); entreeGroup.add(radioButton); p.add(radioButton = new JRadioButton("Chicken")); entreeGroup.add(radioButton); p.add(radioButton = new JRadioButton("Vegetable")); entreeGroup.add(radioButton); p.add(new JCheckBox("Ketchup")); p.add(new JCheckBox("Mustard")); p.add(new JCheckBox("Pickles")); p.add(new JLabel("Special requests:")); p.add(new JTextField(20)); JButton orderButton = new JButton("Place Order"); p.add(orderButton); return p; } }
結果は次のとおりです。
Java Web Startを使用して実行
LayerUI
クラスのpaint()
メソッドを使用すると、コンポーネントの描画方法を完全にコントロールできます。 次に挙げたLayerUI
のもう1つのサブクラスでは、Java 2Dイメージ処理を使用してパネルのコンテンツ全体を変更する方法を示します。
class BlurLayerUI extends LayerUI<JComponent> { private BufferedImage mOffscreenImage; private BufferedImageOp mOperation; public BlurLayerUI() { float ninth = 1.0f / 9.0f; float[] blurKernel = { ninth, ninth, ninth, ninth, ninth, ninth, ninth, ninth, ninth }; mOperation = new ConvolveOp( new Kernel(3, 3, blurKernel), ConvolveOp.EDGE_NO_OP, null); } @Override public void paint (Graphics g, JComponent c) { int w = c.getWidth(); int h = c.getHeight(); if (w == 0 || h == 0) { return; } // Only create the offscreen image if the one we have // is the wrong size. if (mOffscreenImage == null || mOffscreenImage.getWidth() != w || mOffscreenImage.getHeight() != h) { mOffscreenImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); } Graphics2D ig2 = mOffscreenImage.createGraphics(); ig2.setClip(g.getClip()); super.paint(ig2, c); ig2.dispose(); Graphics2D g2 = (Graphics2D)g; g2.drawImage(mOffscreenImage, mOperation, 0, 0); } }
paint()
メソッド内で、パネルはオフスクリーン・イメージにレンダリングされます。 オフスクリーン・イメージは畳み込み演算子を使用して処理された後で、画面に描画されます。
ユーザー・インタフェース全体は変わらず使用できる状態にありますが、表示がぼやけています。
Java Web Startを使用して実行
LayerUI
のサブクラスでも、対応するコンポーネントのすべてのイベントを受け取ることができます。 しかし、JLayer
インスタンスは特定のイベント・タイプに対する関心を登録する必要があります。 これを実行するには、JLayer
クラスのsetLayerEventMask()
メソッドを使用します。 ただし、通常このメソッドは、LayerUI
クラスのinstallUI()
メソッド内で実行される初期化処理によって呼び出されます。
たとえば、次に抜粋したLayerUI
のサブクラスでは、マウス・イベントとマウス・モーション・イベントを取得するよう登録されています。
public void installUI(JComponent c) { super.installUI(c); JLayer jlayer = (JLayer)c; jlayer.setLayerEventMask( AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK ); }
JLayer
のサブクラスが受け取ったすべてのイベントは、イベント・タイプに名前の一致するイベント・ハンドラ・メソッドにルーティングされます。 たとえば、次の該当メソッドをオーバーライドすることで、マウス・イベントとマウス・モーション・イベントに応答できるようになります。
protected void processMouseEvent(MouseEvent e, JLayer l) { // ... } protected void processMouseMotionEvent(MouseEvent e, JLayer l) { // ... }
次のLayerUI
サブクラスは、パネル内でマウスが動くたびに半透明な円を描画します。
class SpotlightLayerUI extends LayerUI<JPanel> { private boolean mActive; private int mX, mY; @Override public void installUI(JComponent c) { super.installUI(c); JLayer jlayer = (JLayer)c; jlayer.setLayerEventMask( AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK ); } @Override public void uninstallUI(JComponent c) { JLayer jlayer = (JLayer)c; jlayer.setLayerEventMask(0); super.uninstallUI(c); } @Override public void paint (Graphics g, JComponent c) { Graphics2D g2 = (Graphics2D)g.create(); // Paint the view. super.paint (g2, c); if (mActive) { // Create a radial gradient, transparent in the middle. java.awt.geom.Point2D center = new java.awt.geom.Point2D.Float(mX, mY); float radius = 72; float[] dist = {0.0f, 1.0f}; Color[] colors = {new Color(0.0f, 0.0f, 0.0f, 0.0f), Color.BLACK}; RadialGradientPaint p = new RadialGradientPaint(center, radius, dist, colors); g2.setPaint(p); g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, .6f)); g2.fillRect(0, 0, c.getWidth(), c.getHeight()); } g2.dispose(); } @Override protected void processMouseEvent(MouseEvent e, JLayer l) { if (e.getID() == MouseEvent.MOUSE_ENTERED) mActive = true; if (e.getID() == MouseEvent.MOUSE_EXITED) mActive = false; l.repaint(); } @Override protected void processMouseMotionEvent(MouseEvent e, JLayer l) { Point p = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), l); mX = p.x; mY = p.y; l.repaint(); } }
mActive
変数は、マウスがパネルの座標内にあるかどうかを示します。 LayerUI
サブクラスがマウス・イベントとマウス・モーション・イベントの取得に関心があることを示すため、installUI()
でsetLayerEventMask()
が呼ばれています。
processMouseEvent()
メソッドでは、マウスの位置に応じてmActive
フラグが設定されます。 processMouseMotionEvent()
メソッドでは、マウス移動の座標がmX
およびmY
のメンバー変数に保管されるため、後からpaint()
メソッドで使用できます。
paint()
メソッドはパネルのデフォルトの外観を表示した後で、スポットライト効果として放射状のグラデーションを重ねて表示します。
Java Web Startを使用して実行
この例では、ビジー・インジケータを動画として表示します。 LayerUI
サブクラス内の動画を表示するものであり、フェードイン機能とフェードアウト機能を備えています。 この例はここまでの例より複雑ですが、カスタム描画のためにpaint()
メソッドを定義するという基本方針に基づく点に変わりはありません。
Place Orderボタンをクリックすると、ビジー・インジケータが4秒間表示されます。 パネルがグレーで表示され、インジケータが回転することに注目してください。 インジケータの各要素には、それぞれ異なる透明度が設定されています。
LayerUI
のサブクラスであるWaitLayerUI
クラスでは、コンポーネントを更新するためにプロパティ変更イベントを発生させる方法が示されています。 WaitLayerUI
クラスはTimer
オブジェクトを使用して、1秒間に24回、状態を更新しています。 この処理は、タイマーのターゲット・メソッドであるactionPerformed()
メソッド内で実行されています。
actionPerformed()
メソッドはfirePropertyChange()
メソッドを使用して、内部状態の更新を通知します。 これによってapplyPropertyChange()
メソッドが呼び出され、JLayer
オブジェクトが再描画されます。
Java Web Startを使用して実行
このドキュメントの最後の例では、JLayer
クラスを使用して、テキスト・フィールドに有効なデータが含まれているかどうかを示すデコレーションを付与する方法を示します。 その他の例ではJLayer
クラスを使用してパネルや汎用コンポーネントがラップされていましたが、この例では具体的にJFormattedTextField
コンポーネントのラップ方法を示します。 また、複数のJLayer
インスタンスに対して、1つのLayerUI
サブクラス実装を使用できるということも実証します。
JLayer
クラスは、無効なデータを含むフィールドを視覚的に表すために使用されます。 ValidationLayerUI
クラスによってテキスト・フィールドが描画される際、フィールド・コンテンツが解析できない場合は、赤い×印が表示されます。 次にその例を示します。
Java Web Startを使用して実行