在JPanel中添加一个带有圆角的背景图片

5

我最近扩展了JPanel用于一个需要呈现更多"3D"效果的项目中。这是我的老板要求我们在组件上使用阴影和圆角的方式。这可以通过许多在线示例中所显示的方法来实现。我是这样做的:

public class RoundedPanel extends JPanel
{
    protected int _strokeSize = 1;
    protected Color _shadowColor = Color.BLACK;
    protected boolean _shadowed = true;
    protected boolean _highQuality = true;
    protected Dimension _arcs = new Dimension(30, 30);
    protected int _shadowGap = 5;
    protected int _shadowOffset = 4;
    protected int _shadowAlpha = 150;

    protected Color _backgroundColor = Color.LIGHT_GRAY;

    public RoundedPanel()
    {
        super();
        setOpaque(false);
    }

    @Override
    public void setBackground(Color c)
    {
        _backgroundColor = c;
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);

        int width = getWidth();
        int height = getHeight();
        int shadowGap = this._shadowGap;
        Color shadowColorA = new Color(_shadowColor.getRed(), _shadowColor.getGreen(), _shadowColor.getBlue(), _shadowAlpha);
        Graphics2D graphics = (Graphics2D) g;

        if(_highQuality)
        {
            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        }

        if(_shadowed)
        {
            graphics.setColor(shadowColorA);
            graphics.fillRoundRect(_shadowOffset, _shadowOffset, width - _strokeSize - _shadowOffset,
                    height - _strokeSize - _shadowOffset, _arcs.width, _arcs.height);
        }
        else
        {
            _shadowGap = 1;
        }

        graphics.setColor(_backgroundColor);
        graphics.fillRoundRect(0,  0, width - shadowGap, height - shadowGap, _arcs.width, _arcs.height);
        graphics.setStroke(new BasicStroke(_strokeSize));
        graphics.setColor(getForeground());
        graphics.drawRoundRect(0,  0, width - shadowGap, height - shadowGap, _arcs.width, _arcs.height);
        graphics.setStroke(new BasicStroke());
    }
}

我正在使用以下代码创建一个测试框架:

public class UITest
{
    private static JFrame mainFrame;
    private static ImagePanel mainPanel;

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                mainFrame = new JFrame();
                mainFrame.setVisible(true);

                try
                {
                    mainPanel = new ImagePanel(ImageIO.read(this.getClass().getResource("/content/diamondPlate_Light.jpg")));
                    //mainPanel.setBounds(0, 0, 800, 600);
                }
                catch(IOException e)
                {

                }
                mainPanel.setLayout(null);

                RoundedPanel rPanel = new RoundedPanel();
                rPanel.setBounds(10, 10, 200, 200);
                rPanel.setBackground(new Color(168, 181, 224));

                mainPanel.add(rPanel);

                rPanel = new RoundedPanel();
                rPanel.setBounds(220, 10, 560, 200);
                rPanel.setBackground(new Color(168, 224, 168));

                mainPanel.add(rPanel);

                rPanel = new RoundedPanel();
                rPanel.setBounds(10, 220, 770, 300);
                rPanel.setBackground(new Color(224, 168, 168));

                mainPanel.add(rPanel);

                mainFrame.setSize(800, 600);
                mainFrame.getContentPane().add(mainPanel);
            }
        });
    }
}

并且它会产生这个结果(不包括JFrame的contentPane的背景图像): Panels 我真正想做的是用填充不同图像而不是颜色生成红色、绿色和蓝色面板。我仍然希望正确地生成圆角,但我不确定如何做到这一点。
如果我有一个大的纹理,我可以简单地在RoundedPanel的大小和形状上“剪辑”出一块吗?我需要评估一下,因为我打字时突然想到,但如果我可以创建一个类似于graphics.fillRoundRect(...)中使用的几何形状,然后剪裁图像,这可能会起作用。
还有其他我错过的方法吗?我很感激您能提供的任何反馈。谢谢。
编辑:
基于下面所选解决方案中的想法,我得到了以下结果: Panels 它需要整理成产品,并且背景图像选择不当,但作为演示,以下RoundedPanel代码使我们得到了上述结果:
public class RoundedPanel extends JPanel
{
    protected int strokeSize = 1;
    protected Color _shadowColor = Color.BLACK;
    protected boolean shadowed = true;
    protected boolean _highQuality = true;
    protected Dimension _arcs = new Dimension(30, 30);
    protected int _shadowGap = 5;
    protected int _shadowOffset = 4;
    protected int _shadowAlpha = 150;

    protected Color _backgroundColor = Color.LIGHT_GRAY;
    protected BufferedImage image = null;

    public RoundedPanel(BufferedImage img)
    {
        super();
        setOpaque(false);

        if(img != null)
        {
            image = img;
        }
    }

    @Override
    public void setBackground(Color c)
    {
        _backgroundColor = c;
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);

        int width = getWidth();
        int height = getHeight();
        int shadowGap = this._shadowGap;
        Color shadowColorA = new Color(_shadowColor.getRed(), _shadowColor.getGreen(), _shadowColor.getBlue(), _shadowAlpha);
        Graphics2D graphics = (Graphics2D) g;

        if(_highQuality)
        {
            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        }

        if(shadowed)
        {
            graphics.setColor(shadowColorA);
            graphics.fillRoundRect(_shadowOffset, _shadowOffset, width - strokeSize - _shadowOffset,
                    height - strokeSize - _shadowOffset, _arcs.width, _arcs.height);
        }
        else
        {
            _shadowGap = 1;
        }

        RoundRectangle2D.Float rr = new RoundRectangle2D.Float(0, 0, (width - shadowGap), (height - shadowGap), _arcs.width, _arcs.height);

        Shape clipShape = graphics.getClip();

        if(image == null)
        {
            graphics.setColor(_backgroundColor);
            graphics.fill(rr);
        }
        else
        {
            RoundRectangle2D.Float rr2 =  new RoundRectangle2D.Float(0, 0, (width - strokeSize - shadowGap), (height - strokeSize - shadowGap), _arcs.width, _arcs.height);

            graphics.setClip(rr2);
            graphics.drawImage(this.image, 0, 0, null);
            graphics.setClip(clipShape);
        }

        graphics.setColor(getForeground());
        graphics.setStroke(new BasicStroke(strokeSize));
        graphics.draw(rr);
        graphics.setStroke(new BasicStroke());
    }
}

感谢您的帮助。

1
也许可以参考这个链接:https://dev59.com/S17Va4cB1Zd3GeqPGAsq 或者这个链接:https://dev59.com/SWDVa4cB1Zd3GeqPfrHm - mKorbel
@mKorbel:第一个链接很不错。显然我之前用的术语是错误的。谢谢。 - Rich Hoffman
1个回答

3
尝试使用“裁剪区域”(参见g.setClip()调用):
public static void main(String[] args) {
    JFrame f = new JFrame();
    f.setSize(new Dimension(600, 400));
    f.getContentPane().setLayout(null);
    RoundPanel rp = new RoundPanel();
    rp.setBounds(100, 50, 400, 300);
    f.getContentPane().add(rp);
    f.setVisible(true);
}

static class RoundPanel extends JPanel {
    @Override
    protected void paintComponent(Graphics g) {
        // Prepare a red rectangle
        BufferedImage bi = new BufferedImage(400, 300, BufferedImage.TYPE_INT_ARGB);
        Graphics2D gb = bi.createGraphics();
        gb.setPaint(Color.RED);
        gb.fillRect(0, 0, 400, 300);
        gb.dispose();

        // Set a rounded clipping region:
        RoundRectangle2D r = new RoundRectangle2D.Float(0, 0, 400, 300, 20, 20);
        g.setClip(r);

        // Draw the rectangle (and see whether it has round corners)
        g.drawImage(bi, 0, 0, null);
    }
}

注意API文档中Graphics.setClip的限制:

将当前剪切区域设置为任意剪切形状。并非所有实现Shape接口的对象都可以用于设置剪切。确保受支持的Shape对象仅限于通过getClip方法和Rectangle对象获得的Shape对象。


已编辑原帖并解决了问题。谢谢! - Rich Hoffman
为什么圆形不够光滑? - user1708134

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接