Was ist eine Glass Pane?

Eine Glass Pane ist eine transparente "Schicht", die als oberste alle Top Level Container abdeckt und auch zur Steuerung der Funktionalität eines GUI verwendet werden kann.

Screenshot Glass Pane Demo

Ein Top Level Container wie JWindow, JFrame oder JDialog besteht aus mehreren Schichten, die jeweils eingene Aufgaben besitzen und gesondert angesprochen werden können. Am bekanntesten dürfte die Content Pane sein, die zur Aufnahme der auf dem Container sichtbaren Komponenten dient.
Das Beispiel zeigt, wie ein Glass Pane gesetzt wird und wie es verwendet werden kann.

Auf einem JFrame wird ein Bild dargestellt, das in der Mitte von einem JButton überlagert wird. Eine zur Hälfte voll und zur Hälfte halb transparente Glass Pane überlagert das Ganze. Sie ist bei einem MouseListener angemeldet, der die Mausposition registriert und der MouseEvents über der Hälfte des Buttons an diesen weiterleitet.

In init() wird ein JPanel erzeugt, das opaque gesetzt und mit einer Hintergrundfarbe versehen wird. Ein JLabel wird dem JPanel hinzugefügt. Nach dem Laden einer Bilddatei wird diese in ein ImageIcon konvertiert und dieses dann auf das JLabel gesetzt, das anschließend an die Größe des ImageIcons angepasst wird.
Ein neu gebildetes JButton-Objekt wird dem JPanel hinzugefügt, und die Größe des Fensters so eingestellt, dass sie etwas größer als das Label ist, um einen grauen Rand zu zeigen.

Da dem JPanel kein LayoutManager zugewiesen wurde, muss die Methode paint() des Fensters überschrieben werden, um das Label und den Button zu zentrieren. Hierzu wird deren Position über die Maße des Labels und Panels berechnet und mittels setBounds() schließlich absolut gesetzt.

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.RootPaneContainer;
import javax.swing.SwingUtilities;

public class GlassPaneDemo {

    private static final long serialVersionUID = 1L;
    private JFrame frame;
    private JPanel panel;
    private JLabel label;
    private JButton butt;
    private BufferedImage img;
    private String imgName = "src/img/wien.jpg";

    public GlassPaneDemo() {
        init();
    }

    public void init() {
        frame = new JFrame("GlassPane-Demo") {
            public void paint(Graphics g) {
                int x = (panel.getWidth() - label.getSize().width) / 2;
                int y = (panel.getHeight() - label.getSize().height) / 2;
                label.setBounds(x, y, img.getWidth(), img.getHeight());
                butt.setBounds(panel.getWidth() / 2 - butt.getWidth() / 2, 200,
                        butt.getWidth(), butt.getHeight());
            }
        };
        label = new JLabel();
        panel = new JPanel();
        panel.setOpaque(true);
        panel.setBackground(Color.DARK_GRAY);
        panel.add(label);
        loadImage();
        ImageIcon icon = new ImageIcon(img);
        label.setIcon(icon);
        label.setSize(new Dimension(icon.getIconWidth(), icon.getIconHeight()));
        butt = new JButton("Klick");
        panel.add(butt, 0);
        frame.setSize(label.getWidth() + 40, label.getHeight() + 60);
        Glass glass = new Glass(butt, label, frame);
        butt.addMouseListener(new ButtonMouseListener(glass, frame.getContentPane(),
                butt) {
            public void mouseReleased(MouseEvent e) {
                System.out.println("Button angeklickt");
            }
        });
        frame.getContentPane().add(panel);
        frame.setGlassPane(glass);
        glass.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public void loadImage() {
        File source = new File(imgName);
        if (source.canRead() && source.isFile()) {
            try {
                img = ImageIO.read(source);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
        if (img == null) {
            System.err.println("Kein Bild geladen.");
            System.exit(1);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new GlassPaneDemo());
    }
}

Um eine Glass Pane zu setzen wird eine Klasse Glass von JComponent abgeleitet. In der Klasse wird deren Methode paintComponent() überschrieben, um ein teiltransparentes Rechteck zeichnen zu können. Dessen Größe wird aus der Größe des Labels berechnet, das zu diesem Zweck an den Konstruktor der Klasse Glass übergeben wird.
Dem Glass Pane wird im Konstruktor noch ein MouseListener hinzugefügt. Er soll die Möglichkeit demonstrieren, ein Mausereignis an den darunter liegenden JButton weiterzureichen.

class Glass extends JComponent {
    JLabel label;

    public Glass(JButton butt, JLabel label, RootPaneContainer parent) {
        this.label = label;
        this.addMouseListener(new ButtonMouseListener(this,
                parent.getContentPane(), butt));
    }

    public void paintComponent(Graphics g) {
        Rectangle rect = label.getBounds();
        int x = rect.x + rect.width / 2;
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(new Color(0.5f, 0.5f, 0.5f, 0.8f));
        g2d.fillRect(x, rect.y, rect.width / 2, rect.height);
    }
}

Der MouseListener ist zu Demonstrationszwecken von MouseAdapter abgeleitet und überschreibt lediglich mouseReleased(). In der Methode werden die Koordinaten der Maus ausgegeben und eine Methode redispatchMouseEvent() aufgerufen, in der das MouseEvent an den unter dem Glass Pane gelagerten JButton weitergereicht wird. Die Methode entstammt in veränderter Form Oracles Java Tutorial How to use Root Panes.
In ihr wird zunächst der aktuelle Punkt der Maus ermittelt. Durch die statische Methode convertPoint() der Klasse SwingUtilities wird der Punkt auf die Content Pane des Fensters transpositioniert. Die Methode getDeepestComponentAt() ermittelt dann die dort liegende Komponente, den Button.
Befindet sich der ermittelte Punkt über dem Button und auf der rechten Hälfte der Glass Pane, so wird die Punktlage von dieser auf den Button übertragen, der daraufhin das an ihm registrierte Ereignis verschickt.

class ButtonMouseListener extends MouseAdapter {
    Component glass;
    Container contentPane;
    JButton butt;

    public ButtonMouseListener(Component glass, Container contentPane, JButton butt) {
        this.glass = glass;
        this.contentPane = contentPane;
        this.butt = butt;
    }

    public void mouseReleased(MouseEvent e) {
        redispatchMouseEvent(e);
        System.out.println("mouse released at " + e.getX() + "/" + e.getY());
    }

    private void redispatchMouseEvent(MouseEvent e) {

        Point gpPoint = e.getPoint();
        Point cpPoint = SwingUtilities.convertPoint(glass,
                gpPoint, contentPane);
        Component button = SwingUtilities.getDeepestComponentAt(contentPane,
                cpPoint.x, cpPoint.y);
        if ((button != null) && (button.equals(butt))
                && gpPoint.x >= glass.getWidth() / 2) {
            Point buttPoint = SwingUtilities.convertPoint(glass,
                    gpPoint, button);
            button.dispatchEvent(new MouseEvent(button, e.getID(), e
                    .getWhen(), e.getModifiers(), buttPoint.x,
                    buttPoint.y, e.getClickCount(), e.isPopupTrigger()));
        }
    }
}

Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.