【问题标题】:Java Swing - Applying transparancy to Components on transparent JPanelJava Swing - 将透明度应用于透明 JPanel 上的组件
【发布时间】:2016-12-26 22:30:13
【问题描述】:

我正在创建一个支持透明度的JPanel,但遇到的问题是我不确定如何对添加到此面板的所有Components 应用相同级别的透明度。到目前为止我的代码:

package de.uebertreiberman.project.swing;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;

import javax.swing.JPanel;

@SuppressWarnings("serial")
public class JTransparancyPanel extends JPanel {

    float opacity = 1.0f;

    /**Constructor for JTransparentPanel
     */
    public JTransparancyPanel()
    {
        super();
        this.setOpaque(false);
    }

    /**Getter for opacity value of panel
     * 
     * @return float containing opacity value of frame (0-1)
     */
    public float getOpacity()
    {
        return opacity;
    }

    /**Setter for opacity value of panel
     * 
     * @param value as float for opacity of frame (0-1)
     */
    public void setOpacity(float value)
    {
        opacity = value;
        repaint();
    }

    /**Converts opacity value (0-1) to opacity color (0-255)
     * 
     * @param opacity as float opacity value (0-1)
     * @return integer containing converted opacity value (0-255)
     */
    public static int getOpacityColor(float opacity)
    {
        return (int)(opacity * 255);
    }

    /**Converts opacity color (0-255) to opacity value (0-1)
     * 
     * @param opacity as integer value (0-255)
     * @return float containing converted opacity value (0-1)
     */
    public static float getOpacityValue(int opacity)
    {
        //Returns more or less the correct, capped value
        //Just ignore it, it works, leave it :D
        return capFloat((3.9216f*opacity)/1000f, 0.0f, 1.0f);
    }

    /**Returns float capped between minimum and maximum value
     * 
     * @param value as original value
     * @param min as minimum cap value
     * @param max as maximum cap value
     * @return float containing capped value
     */
    public static float capFloat(float value, float min, float max)
    {
        if(value < min) value = min;
            else if(value > max) value = max;

        return value;
    }

    /**Merges color and opacity to new color
     * 
     * @param bg as color for old color, only RGB will be used from that
     * @return color with RGB from bg and A from opacity of frame
     */
    Color getTransparencyColor(Color bg)
    {
        return new Color(getOpacityValue(bg.getRed()), getOpacityValue(bg.getGreen()), getOpacityValue(bg.getBlue()), opacity);
    }

    @Override
    public void paintComponent(Graphics g)
    {
        //Draw transparent background before painting other Components
        g.setColor(getTransparencyColor(getBackground()));
        Rectangle r = g.getClipBounds();
        g.fillRect(r.x, r.y, r.width, r.height);

        //Paint other components
        super.paintComponent(g);
    }
}

重要的部分基本上在我覆盖paintComponent(Graphics g)方法的最后。

我只取backgroundcolor,对其应用透明度并设置它。

现在我想让这个面板的所有子组件也变得透明,我不太确定这样做最有效的方法是什么。

你有什么建议?

【问题讨论】:

  • 如果不覆盖各个组件的 paint/paintComponent 方法,这很困难。没有什么能阻止子组件公然用纯色、不透明的颜色填充其区域。可以尝试一些技巧,将子组件绘制成图像,然后用适当的不透明度绘制该图像。我会尝试一下,但可能有点繁琐。
  • 我今天没试过也没有时间,但是你能不能不只是获取组件(Component[] components = jpanel.getComponents();),遍历它们并使用getGraphics().setBackgroundColour()设置它们的背景?

标签: java swing jpanel transparency


【解决方案1】:

我找到了一个解决方案——基本上就是我在 cmets 中已经提到的:面板并没有真正绘制在屏幕上。相反,它被绘制成一个图像,并且这个图像被绘制了适当的AlphaComposite。

(滑块可用于设置面板的不透明度)

虽然这个解决方案简单、通用并且基本上应该适用于所有子组件,但我不喜欢它的一件事:调整RepaintManager 的必要性。为了确保应用面板的不透明度(并且子组件不是独立绘制的,并且具有完全不透明度),重绘管理器必须在重绘子组件时触发整个组件的重绘。我认为应该有没有这种解决方法的解决方案(例如,这可能是在面板上覆盖getGaphics 可能合适的罕见情况之一),但无济于事。也许通常的嫌疑人(camickr、Hovercraft 等)会找到一个更优雅、更简洁的解决方案。

在此之前,这里是作为 MCVE 的代码:

import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTree;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;

public class TransparencyPanelTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().setLayout(new BorderLayout());

        OpacityPanel opacityPanel = new OpacityPanel();

        opacityPanel.setLayout(new BorderLayout());
        opacityPanel.add(new JLabel("Label"), BorderLayout.NORTH);
        opacityPanel.add(new JTree(), BorderLayout.CENTER);
        opacityPanel.add(new JButton("Button"), BorderLayout.SOUTH);

        f.getContentPane().add(opacityPanel, BorderLayout.CENTER);

        JSlider slider = new JSlider(0, 100, 50);
        slider.addChangeListener(e -> 
            opacityPanel.setOpacity(slider.getValue() / 100.0f));
        f.getContentPane().add(slider, BorderLayout.SOUTH);

        f.setSize(500,500);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

}

class OpacityPanel extends JPanel
{
    static
    {
        RepaintManager.setCurrentManager(new RepaintManager()
        {
            @Override
            public void addDirtyRegion(
                JComponent c, int x, int y, int w, int h)
            {
                Component cc = c;
                while (cc != null)
                {
                    if (cc instanceof OpacityPanel)
                    {
                        OpacityPanel p = (OpacityPanel)cc;
                        super.addDirtyRegion(
                            p, 0, 0, p.getWidth(), p.getHeight());
                    }
                    cc = cc.getParent();
                }
                super.addDirtyRegion(c, x, y, w, h);
            }
        });
    }
    private float opacity = 0.5f;
    private BufferedImage image;

    public OpacityPanel()
    {
        setOpaque(false);
    }

    public void setOpacity(float opacity)
    {
        this.opacity = Math.max(0.0f, Math.min(1.0f, opacity));
        repaint();
    }

    private void updateImage()
    {
        int w = Math.min(1, getWidth());
        int h = Math.min(1, getHeight());
        if (image == null || image.getWidth() != w || image.getHeight() != h)
        {
            image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        }
        Graphics2D g = image.createGraphics();
        g.setColor(new Color(0,0,0,0));
        g.setComposite(AlphaComposite.SrcOver);
        g.fillRect(0, 0, w, h);
        g.dispose();
    }

    @Override
    protected void paintComponent(Graphics gr)
    {
        updateImage();
        Graphics2D imageGraphics = image.createGraphics();
        super.paintComponent(imageGraphics);
        imageGraphics.dispose();

        Graphics2D g = (Graphics2D) gr;
        g.setComposite(AlphaComposite.getInstance(
            AlphaComposite.SRC_OVER, opacity));
        g.drawImage(image, 0, 0, null);
    }

}

【讨论】:

  • 酷,感谢您的快速回复。我认为完全掌握这个解决方案对我来说为时已晚,但我想我明白你在做什么。明天我将尝试将其实现到我的代码中。我还将详细了解 RepaintManager,因为我现在对它的工作原理很感兴趣。但是,再次感谢,你帮了我很多!
  • @Uebertreiberman 不要 übertreiben 接受答案的速度太快了 ;-) 您可能需要等待一段时间,也许其他人会提出更好的解决方案。我仍然认为应该有一个没有重绘管理器的更简单的方法,但是 Swing 绘制机制有时在底层相当复杂,这看起来是实现预期目标的简单方法。
猜你喜欢
  • 2011-06-26
  • 1970-01-01
  • 2017-06-14
  • 1970-01-01
  • 1970-01-01
  • 2013-06-05
  • 1970-01-01
  • 2012-04-20
  • 1970-01-01
相关资源
最近更新 更多