首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java SE 6 Update N 透明與非矩形視窗實戰:AWTUtilities API 深度解析

     編輯:Java 相關知識

背景:Consumer JRE 帶來的視覺革命

Java SE 6 Update N(通稱 Consumer JRE)在 com.sun.awt.AWTUtilities 中引入三大核心 API,讓 Swing/AWT 終於原生支援:

  • setWindowOpacity(Window, float):整體不透明度(0.0f~1.0f)
  • setWindowShape(Window, Shape):非矩形視窗剪裁
  • setWindowOpaque(Window, boolean):啟用逐像素透明度(per-pixel translucency)

⚠️ 注意:此類位於 com.sun.* 非標準包,Java 7+ 可能遷移至 java.awt,升級時需適配。

核心 API 速查

方法參數效果備註
setWindowOpacityWindow, float (0~1)整體半透明不支援逐像素 Alpha
setWindowShapeWindow, Shape任意幾何形狀剪裁邊緣易鋸齒,需配合 setWindowOpaque(false) 抗鋸齒
setWindowOpaqueWindow, booleantrue=不透明(預設), false=支援逐像素 Alpha必須搭配自訂繪製背景

實戰案例一:半透明 ToolTip 與淡入淡出動畫

自訂 PopupFactory

public class TranslucentPopupFactory extends PopupFactory {
    @Override
    public Popup getPopup(Component owner, Component contents, int x, int y) {
        return new TranslucentPopup(owner, contents, x, y);
    }
}

// 安裝:PopupFactory.setSharedInstance(new TranslucentPopupFactory());

TranslucentPopup 關鍵實作

class TranslucentPopup extends Popup {
    private final JWindow popupWindow;
    private final boolean toFade;
    private int currOpacity = 0;
    private Timer fadeInTimer, fadeOutTimer;

    TranslucentPopup(Component owner, Component contents, int x, int y) {
        popupWindow = new JWindow();
        // ToolTip 0.8f,其他 0.95f
        float opacity = (contents instanceof JToolTip) ? 0.8f : 0.95f;
        AWTUtilities.setWindowOpacity(popupWindow, opacity);
        popupWindow.setLocation(x, y);
        popupWindow.getContentPane().add(contents, BorderLayout.CENTER);
        // 陰影邊框需逐像素透明
        AWTUtilities.setWindowOpaque(popupWindow, false);
        ((JComponent)contents.getParent()).setBorder(new ShadowPopupBorder());
        toFade = contents instanceof JToolTip;
    }

    @Override public void show() {
        popupWindow.setVisible(true);
        popupWindow.pack();
        if (toFade) fadeIn();
    }

    private void fadeIn() {
        AWTUtilities.setWindowOpacity(popupWindow, 0f);
        fadeInTimer = new Timer(50, e -> {
            currOpacity += 20;
            if (currOpacity <= 100) {
                AWTUtilities.setWindowOpacity(popupWindow, currOpacity / 100f);
                popupWindow.getContentPane().repaint(); // 修正 6670649
            } else {
                fadeInTimer.stop();
            }
        });
        fadeInTimer.start();
    }

    @Override public void hide() {
        if (toFade) fadeOut();
        else disposeWindow();
    }

    private void fadeOut() {
        if (fadeInTimer != null && fadeInTimer.isRunning()) fadeInTimer.stop();
        fadeOutTimer = new Timer(50, e -> {
            currOpacity -= 10;
            if (currOpacity >= 0) {
                AWTUtilities.setWindowOpacity(popupWindow, currOpacity / 100f);
                popupWindow.getContentPane().repaint();
            } else {
                fadeOutTimer.stop();
                disposeWindow();
            }
        });
        fadeOutTimer.start();
    }

    private void disposeWindow() {
        popupWindow.setVisible(false);
        popupWindow.removeAll();
        popupWindow.dispose();
    }
}

實戰案例二:視頻即時反射效果(跨視窗)

結合 RepaintManager、ComponentListener、WindowListener 同步主視窗與反射視窗:

public class JReflectionFrame extends JFrame {
    private final JWindow reflection;
    private final JPanel reflectionPanel;

    public JReflectionFrame(String title) {
        super(title);
        reflection = new JWindow();
        reflectionPanel = new JPanel() {
            @Override protected void paintComponent(Graphics g) {
                paintReflection(g); // 繪製鏡像+漸變遮罩
            }
        };
        reflectionPanel.setDoubleBuffered(false); // 關鍵:雙緩衝會阻擋逐像素透明
        reflectionPanel.setOpaque(false);
        reflection.add(reflectionPanel);
        reflection.setSize(getSize());
        reflection.setLocation(getX(), getY() + getHeight());
        reflection.setVisible(true);

        // 同步位置/大小/可見性
        addComponentListener(new ComponentAdapter() {
            @Override public void componentMoved(ComponentEvent e) { syncReflection(); }
            @Override public void componentResized(ComponentEvent e) { syncReflection(); }
            @Override public void componentShown(ComponentEvent e) { 
                reflection.setVisible(true); 
                if (AWTUtilities.isWindowOpaque(reflection)) 
                    AWTUtilities.setWindowOpaque(reflection, false);
            }
            @Override public void componentHidden(ComponentEvent e) { reflection.setVisible(false); }
        });
        addWindowListener(new WindowAdapter() {
            @Override public void windowActivated(WindowEvent e) {
                reflection.setAlwaysOnTop(true);
                reflection.setAlwaysOnTop(false); // 強制置頂閃爍技巧
            }
        });

        // 強制重繪同步
        RepaintManager.setCurrentManager(new RepaintManager() {
            @Override public void addDirtyRegion(JComponent c, int x, int y, int w, int h) {
                Window win = SwingUtilities.getWindowAncestor(c);
                if (win instanceof JReflectionFrame) {
                    JRootPane rp = ((JReflectionFrame)win).getRootPane();
                    super.addDirtyRegion(rp, 0, 0, rp.getWidth(), rp.getHeight());
                    reflectionPanel.repaint();
                } else super.addDirtyRegion(c, x, y, w, h);
            }
        });
    }

    private void syncReflection() {
        reflection.setSize(getSize());
        reflection.setLocation(getX(), getY() + getHeight());
    }
    // paintReflection 參考 Romain Guy 教學:離屏緩衝 -> 翻轉 -> Alpha 遮罩漸變
}

常見坑點與解決方案

問題原因解決
非矩形視窗邊緣鋸齒嚴重Shape 裁剪無抗鋸齒setWindowOpaque(false) + 自訂背景繪製時開啟 Graphics2D.RENDERING_HINT_ANTIALIASING
逐像素透明視窗背景全黑未關閉雙緩衝或未設 opaque=falseJPanel.setDoubleBuffered(false); setOpaque(false); AWTUtilities.setWindowOpaque(window, false)
ToolTip 動畫閃爍repaint() 不觸發重繪針對 Bug 6670649:呼叫 contentPane.repaint() 而非 window.repaint()
Linux/X11 無效視窗管理器不支援 _NET_WM_WINDOW_TYPE_DOCK需啟用 Compositing Manager (Compiz, Mutter, KWin) 並確認 AWT Toolkit 為 XToolkit

遷移指南:Java 7+ 標準化 API

Java 7 將功能移至 java.awt.Window 實例方法:

// Java 7+
window.setOpacity(0.7f);                    // 替代 setWindowOpacity
window.setShape(new Ellipse2D.Double(...)); // 替代 setWindowShape
window.setBackground(new Color(0,0,0,0));   // 替代 setWindowOpaque(false) + 透明背景色
// 判斷支援:GraphicsDevice.isWindowTranslucencySupported(TRANSLUCENT/PERPIXEL_TRANSPARENT/UNIFORM_TRANSLUCENCY)

結語

AWTUtilities 雖為非標準 API,卻在 Java 7 標準化前填補了桌面應用視覺表現的長期空白。掌握「不透明度、形狀剪裁、逐像素透明」三大核心能力,配合 RepaintManager、離屏緩衝、抗鋸齒繪製,即可構建媲美原生應用的炫酷 UI。現代專案建議直接使用 Java 7+ 標準 API,並做好跨平台 Compositing 環境相容性測試。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved