JPanel 投影

Posted

技术标签:

【中文标题】JPanel 投影【英文标题】:JPanel Drop Shadow 【发布时间】:2012-11-02 07:35:33 【问题描述】:

我有一个JPanel 元素,我想给它添加一个阴影,如何给元素添加一个漂亮的褪色阴影?我需要使用外部库还是可以使用内置的东西?

例子:

【问题讨论】:

这里也有类似的问题:***.com/questions/3232675/… 如果你想做这种事情,我会考虑使用 JavaFX。我相信它内置了对做这种事情的支持,希望有硬件加速。 【参考方案1】:

所以我查看了swingx,它扩展了 JPanel,并能够使用以下代码实现我正在寻找的结果:

public class Canvas extends JXPanel

    public Canvas()
        DropShadowBorder shadow = new DropShadowBorder();
        shadow.setShadowColor(Color.BLACK);
        shadow.setShowLeftShadow(true);
        shadow.setShowRightShadow(true);
        shadow.setShowBottomShadow(true);
        shadow.setShowTopShadow(true);
        this.setBorder(shadow);
    

结果:

【讨论】:

【参考方案2】:

这是一个完整的HACK

这将要求您拥有 JH-Labs Filters 的副本以用于模糊实现

这是一个昂贵的操作,因为它使用了模糊操作,我使用它的原因是要考虑到它被遮蔽的组件的形状。

您遇到的主要问题是边框本身不是透明的,没有办法真正拥有不透明的组件和透明的边框。解决这个问题

public class TestDropShadowBorder 

    public static void main(String[] args) 
        new TestDropShadowBorder();
    

    public TestDropShadowBorder() 
        EventQueue.invokeLater(new Runnable() 
            @Override
            public void run() 
                try 
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                 catch (ClassNotFoundException ex) 
                 catch (InstantiationException ex) 
                 catch (IllegalAccessException ex) 
                 catch (UnsupportedLookAndFeelException ex) 
                

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            
        );
    

    public class TestPane extends JPanel 

        public TestPane() 
            setBackground(Color.RED);
            setBorder(new EmptyBorder(20, 20, 20, 20));

            setLayout(new BorderLayout());
            JPanel drop = new JPanel();
            drop.setOpaque(false);
            DropShadowBorder border = new DropShadowBorder();
            border.setFillContentArea(true);
            drop.setBorder(new CompoundBorder(border, new LineBorder(Color.BLACK)));

            add(drop);

        
    

    public static class DropShadowBorder implements Border 

        protected static final int SHADOW_SIZE = 4;
        protected static final Map<Component, DropShadowBorder.CachedBorder> BORDER_CACHE = new WeakHashMap<Component, CachedBorder>(5);
        private boolean fillContentArea;
        private int shadowSize;
        private float shadowOpacity;
        private Color shadowColor;

        public DropShadowBorder() 

            this(SHADOW_SIZE, Color.BLACK, 0.5f, true);

        

        public DropShadowBorder(boolean paintContentArea) 

            this(SHADOW_SIZE, Color.BLACK, 0.5f, paintContentArea);

        

        public DropShadowBorder(int shadowSize) 

            this(shadowSize, Color.BLACK, 0.5f, true);

        

        public DropShadowBorder(Color shadowColor) 

            this(SHADOW_SIZE, shadowColor, 0.5f, true);

        

        public DropShadowBorder(int shadowSize, Color showColor) 

            this(shadowSize, showColor, 0.5f, true);

        

        public DropShadowBorder(int shadowSize, float opacity) 

            this(shadowSize, Color.BLACK, opacity, true);

        

        public DropShadowBorder(Color shadowColor, float opacity) 

            this(SHADOW_SIZE, shadowColor, opacity, true);

        

        public DropShadowBorder(int shadowSize, Color shadowColor, float opacity) 

            this(shadowSize, shadowColor, opacity, true);

        

        public DropShadowBorder(int shadowSize, boolean paintContentArea) 

            this(shadowSize, Color.BLACK, 0.5f, paintContentArea);

        

        public DropShadowBorder(Color shadowColor, boolean paintContentArea) 

            this(SHADOW_SIZE, shadowColor, 0.5f, paintContentArea);

        

        public DropShadowBorder(int shadowSize, Color showColor, boolean paintContentArea) 

            this(shadowSize, showColor, 0.5f, paintContentArea);

        

        public DropShadowBorder(int shadowSize, float opacity, boolean paintContentArea) 

            this(shadowSize, Color.BLACK, opacity, paintContentArea);

        

        public DropShadowBorder(Color shadowColor, float opacity, boolean paintContentArea) 

            this(SHADOW_SIZE, shadowColor, opacity, paintContentArea);

        

        public DropShadowBorder(int shadowSize, Color showColor, float opacity, boolean paintContentArea) 

            setShadowSize(shadowSize);
            setShadowColor(showColor);
            setShadowOpacity(opacity);
            setFillContentArea(paintContentArea);

        

        public void setShadowColor(Color shadowColor) 
            this.shadowColor = shadowColor;
        

        public void setShadowOpacity(float shadowOpacity) 
            this.shadowOpacity = shadowOpacity;
        

        public Color getShadowColor() 
            return shadowColor;
        

        public float getShadowOpacity() 
            return shadowOpacity;
        

        public void setShadowSize(int size) 

            shadowSize = size;

        

        public int getShadowSize() 

            return shadowSize;

        

        public static GraphicsConfiguration getGraphicsConfiguration() 

            return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();

        

        public static BufferedImage createCompatibleImage(int width, int height) 

            return createCompatibleImage(width, height, Transparency.TRANSLUCENT);

        

        public static BufferedImage createCompatibleImage(int width, int height, int transparency) 

            BufferedImage image = getGraphicsConfiguration().createCompatibleImage(width, height, transparency);
            image.coerceData(true);
            return image;

        

        public static BufferedImage generateShadow(BufferedImage imgSource, int size, Color color, float alpha) 

            int imgWidth = imgSource.getWidth() + (size * 2);
            int imgHeight = imgSource.getHeight() + (size * 2);

            BufferedImage imgMask = createCompatibleImage(imgWidth, imgHeight);
            Graphics2D g2 = imgMask.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
            g2.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
            g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);

            int x = Math.round((imgWidth - imgSource.getWidth()) / 2f);
            int y = Math.round((imgHeight - imgSource.getHeight()) / 2f);
            g2.drawImage(imgSource, x, y, null);
            g2.dispose();

            // ---- Blur here ---

            BufferedImage imgGlow = generateBlur(imgMask, size, color, alpha);
//
//        BufferedImage imgGlow = ImageUtilities.createCompatibleImage(imgWidth, imgHeight);
//        g2 = imgGlow.createGraphics();
//
//        g2.drawImage(imgMask, 0, 0, null);
//        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, alpha));
//        g2.setColor(color);
//
//        g2.fillRect(x, y, imgSource.getWidth(), imgSource.getHeight());
//        g2.dispose();
//
//        imgGlow = filter.filter(imgGlow, null);

            // ---- Blur here ----

//        imgGlow = ImageUtilities.applyMask(imgGlow, imgMask, AlphaComposite.DST_OUT);

            return imgGlow;

        

        public static BufferedImage generateBlur(BufferedImage imgSource, int size, Color color, float alpha) 

            GaussianFilter filter = new GaussianFilter(size);

            int imgWidth = imgSource.getWidth();
            int imgHeight = imgSource.getHeight();

            BufferedImage imgBlur = createCompatibleImage(imgWidth, imgHeight);
            Graphics2D g2d = imgBlur.createGraphics();
            g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
            g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);

            g2d.drawImage(imgSource, 0, 0, null);
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, alpha));
            g2d.setColor(color);

            g2d.fillRect(0, 0, imgSource.getWidth(), imgSource.getHeight());
            g2d.dispose();

            imgBlur = filter.filter(imgBlur, null);

            return imgBlur;

        

        public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) 

            /*
             * Because of the amount of time it can take to render the drop shadow,
             * we cache the results in a static cache, based on the component
             * and the components size.
             * 
             * This allows the shadows to repainted quickly so long as the component
             * hasn't changed in size.
             */

            BufferedImage dropShadow = null;

            DropShadowBorder.CachedBorder cached = BORDER_CACHE.get(c);
            if (cached != null) 

                dropShadow = cached.getImage(c);

            

            if (dropShadow == null) 

                int shadowSize = getShadowSize();
                float opacity = getShadowOpacity();
                Color color = getShadowColor();

                // Create a blank canvas, from which the actually border can be generated
                // from...
                // The ahadow routine can actually generate a non-rectangular border, but
                // because we don't have a suitable template to run from, we need to 
                // set this up our selves...
                // It would be nice to be able to user the component itself, but this will
                // have to
                BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2d = img.createGraphics();
                g2d.fillRect(0, 0, width - (shadowSize * 2), height - (shadowSize * 2));
                g2d.dispose();

                // Generate the shadow
                BufferedImage shadow = generateShadow(img, shadowSize, getShadowColor(), getShadowOpacity());

                // We need to produce a clipping result, cause the border is painted ontop
                // of the base component...
                BufferedImage clipedShadow = createCompatibleImage(width, height, Transparency.TRANSLUCENT);
                g2d = clipedShadow.createGraphics();
                Shape clip = g2d.getClip();

                // First we create a "area" filling the avaliable space...
                Area area = new Area(new Rectangle(width, height));
                // Then we subtract the space left over for the component
                area.subtract(new Area(new Rectangle(width - (shadowSize * 2), height - (shadowSize * 2))));
                // And then apply the clip
                g2d.setClip(area);
                // Then draw the shadow image
//        g2d.drawImage(shadow, -(shadowSize / 2), -(shadowSize / 2), c);
                g2d.drawImage(shadow, 0, 0, c);
                g2d.setClip(clip);

            if (!c.isOpaque() && isFillContentArea()) 

                area = new Area(new Rectangle(width - (shadowSize * 2), height - (shadowSize * 2)));
                g2d.setColor(c.getBackground());
                g2d.fill(area);

            

//            g2d.setColor(Color.RED);
//            g2d.drawRect(x, y, width - 1, height - 1);
//
//            g2d.setColor(Color.GREEN);
//            g2d.drawRect(x, y, width - (shadowSize * 2), height - (shadowSize * 2));

                g2d.dispose();

                dropShadow = clipedShadow;
                BORDER_CACHE.put(c, new CachedBorder(dropShadow, c.getSize()));

            

            g.drawImage(dropShadow, x, y, c);

//        if (!c.isOpaque() && isFillContentArea()) 
//
//            Graphics2D g2d = (Graphics2D) g;
//            
//            Area area = new Area(new Rectangle(width - (shadowSize * 2), height - (shadowSize * 2)));
//            g2d.setColor(c.getBackground());
//            g2d.fill(area);
//
//        

//        g.setColor(Color.MAGENTA);
//        g.drawRect(x + 1, y + 1, width - (shadowSize * 2) - 1, height - (shadowSize * 2) - 1);

        

        public Insets getBorderInsets(Component cmpnt) 
            return new Insets(0, 0, getShadowSize() * 2, getShadowSize() * 2);
        

        public boolean isBorderOpaque() 
            return false;
        

        /**
         * Returns if the content area should be painted by this border when the
         * parent component is opaque...
         *
         * The problem is, the paintComponent method will paint the WHOLE component
         * background, including the border area. This is a reasonable assumption to
         * make, but it makes the shadow border really show up when the parent
         * component is a different color.
         *
         * This allows the border to take control of that fact.
         *
         * When using it, you will need to try and make this the first border to get
         * painted though :P
         *
         * @return
         */
        public boolean isFillContentArea() 
            return fillContentArea;
        

        public void setFillContentArea(boolean fill) 

            fillContentArea = fill;

        

        protected class CachedBorder 

            private BufferedImage image;
            private Dimension size;

            public CachedBorder(BufferedImage border, Dimension size) 

                this.image = border;
                this.size = size;

            

            public BufferedImage getImage(Component comp) 

                BufferedImage dropShadow = null;

                if (comp.getSize().equals(size)) 

                    dropShadow = image;

                

                return dropShadow;

            
        
    

更新了更多示例

阴影边框有局限性,它不能考虑组件的形状,因为绘制边框的时候,组件还没有开始,所以我们没有参考点。

为了能够生成考虑到组件形状的阴影,我们需要创建一个自定义组件并将我们的边框直接注入到绘制过程中。

public class TestDropShadowBorder 

    public static void main(String[] args) 
        new TestDropShadowBorder();
    

    public TestDropShadowBorder() 
        EventQueue.invokeLater(new Runnable() 
            @Override
            public void run() 
                try 
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                 catch (ClassNotFoundException ex) 
                 catch (InstantiationException ex) 
                 catch (IllegalAccessException ex) 
                 catch (UnsupportedLookAndFeelException ex) 
                

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            
        );
    

    public class TestPane extends JPanel 

        public TestPane() 
            setBackground(Color.RED);
            setBorder(new EmptyBorder(20, 20, 20, 20));
            setLayout(new BorderLayout());
            add(new RoundedPane());
        
    

    public class RoundedPane extends JPanel 

        private int shadowSize = 5;

        public RoundedPane() 
            // This is very important, as part of the panel is going to be transparent
            setOpaque(false);
        

        @Override
        public Insets getInsets() 
            return new Insets(0, 0, 10, 10);
        

        @Override
        public Dimension getPreferredSize() 
            return new Dimension(200, 200);
        

        @Override
        protected void paintComponent(Graphics g) 
            int width = getWidth() - 1;
            int height = getHeight() - 1;

            Graphics2D g2d = (Graphics2D) g.create();
            applyQualityProperties(g2d);
            Insets insets = getInsets();
            Rectangle bounds = getBounds();
            bounds.x = insets.left;
            bounds.y = insets.top;
            bounds.width = width - (insets.left + insets.right);
            bounds.height = height - (insets.top + insets.bottom);

            RoundRectangle2D shape = new RoundRectangle2D.Float(bounds.x, bounds.y, bounds.width, bounds.height, 20, 20);

            /**
             * * THIS SHOULD BE CAHCED AND ONLY UPDATED WHEN THE SIZE OF THE
             * COMPONENT CHANGES **
             */
            BufferedImage img = createCompatibleImage(bounds.width, bounds.height);
            Graphics2D tg2d = img.createGraphics();
            applyQualityProperties(g2d);
            tg2d.setColor(Color.BLACK);
            tg2d.translate(-bounds.x, -bounds.y);
            tg2d.fill(shape);
            tg2d.dispose();
            BufferedImage shadow = generateShadow(img, shadowSize, Color.BLACK, 0.5f);

            g2d.drawImage(shadow, shadowSize, shadowSize, this);

            g2d.setColor(getBackground());
            g2d.fill(shape);

            /**
             * THIS ONE OF THE ONLY OCCASIONS THAT I WOULDN'T CALL
             * super.paintComponent *
             */
            getUI().paint(g2d, this);

            g2d.setColor(Color.GRAY);
            g2d.draw(shape);
            g2d.dispose();
        
    

    public static GraphicsConfiguration getGraphicsConfiguration() 

        return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();

    

    public static BufferedImage createCompatibleImage(int width, int height) 

        return createCompatibleImage(width, height, Transparency.TRANSLUCENT);

    

    public static void applyQualityProperties(Graphics2D g2) 
        g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
        g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
    

    public static BufferedImage createCompatibleImage(int width, int height, int transparency) 

        BufferedImage image = getGraphicsConfiguration().createCompatibleImage(width, height, transparency);
        image.coerceData(true);
        return image;

    

    public static BufferedImage generateShadow(BufferedImage imgSource, int size, Color color, float alpha) 

        int imgWidth = imgSource.getWidth() + (size * 2);
        int imgHeight = imgSource.getHeight() + (size * 2);

        BufferedImage imgMask = createCompatibleImage(imgWidth, imgHeight);
        Graphics2D g2 = imgMask.createGraphics();
        applyQualityProperties(g2);

        int x = Math.round((imgWidth - imgSource.getWidth()) / 2f);
        int y = Math.round((imgHeight - imgSource.getHeight()) / 2f);
//            g2.drawImage(imgSource, x, y, null);
        g2.drawImage(imgSource, 0, 0, null);
        g2.dispose();

        // ---- Blur here ---

        BufferedImage imgShadow = generateBlur(imgMask, size, color, alpha);

        return imgShadow;

    

    public static BufferedImage generateBlur(BufferedImage imgSource, int size, Color color, float alpha) 

        GaussianFilter filter = new GaussianFilter(size);

        int imgWidth = imgSource.getWidth();
        int imgHeight = imgSource.getHeight();

        BufferedImage imgBlur = createCompatibleImage(imgWidth, imgHeight);
        Graphics2D g2d = imgBlur.createGraphics();
        applyQualityProperties(g2d);

        g2d.drawImage(imgSource, 0, 0, null);
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, alpha));
        g2d.setColor(color);

        g2d.fillRect(0, 0, imgSource.getWidth(), imgSource.getHeight());
        g2d.dispose();

        imgBlur = filter.filter(imgBlur, null);

        return imgBlur;

    

【讨论】:

我想知道这是否是 OP 想要的,我最初的答案是相似的,除了圆形边框(通过将弧尺寸设置为 0 来更改)。似乎如果我的眼睛没有欺骗我,边框在角落的底部末端显示得很大(没有弧形或角落突出),并且围绕图像其余部分的边框更薄,也没有从图像角落突出的角落或弧形?还是 OP 所追求的模糊? +1 虽然很棒的 sn-p :) 原始窗格周围有一个“黑色”线条边框。通过拍摄组件的快照并模糊结果以生成简单的阴影来开始生成阴影。当前的阴影固定在东南方,但移动它不需要太多时间;) @DavidKroukamp 添加了其他示例:P【参考方案3】:

您可以处理的简单阴影。您可以在这些 JPanel 上看到实现。

public class DropShadowPanel extends JPanel 

    private static final long serialVersionUID = 1L;

    public int pixels;

    public DropShadowPanel(int pix) 
        this.pixels = pix;
        Border border = BorderFactory.createEmptyBorder(pixels, pixels, pixels, pixels);
        this.setBorder(BorderFactory.createCompoundBorder(this.getBorder(), border));
        this.setLayout(new BorderLayout());
    

    @Override
    protected void paintComponent(Graphics g) 
        int shade = 0;
        int topOpacity = 80;
        for (int i = 0; i < pixels; i++) 
            g.setColor(new Color(shade, shade, shade, ((topOpacity / pixels) * i)));
            g.drawRect(i, i, this.getWidth() - ((i * 2) + 1), this.getHeight() - ((i * 2) + 1));
        
    

【讨论】:

这绝对是美丽的。 +1。 在我这一代(2020 年)中仍然更简单、更干净+1 在每次迭代中使用Rectangle grow(1, 1) 可能比围绕i 的计算更容易理解。但这并没有改变这个解决方案的简单性。【参考方案4】:

你的意思是这样的:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class ShadowTest 

    private JFrame frame;

    public ShadowTest() 
        initComponents();
    

    public static void main(String[] args) 
        SwingUtilities.invokeLater(new Runnable() 
            @Override
            public void run() 
                new ShadowTest();
            
        );
    

    private void initComponents() 
        frame = new JFrame();
        frame.setTitle("Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//app exited when frame closes
        frame.setResizable(false);
        frame.setLayout(new GridBagLayout());
        GridBagConstraints gc = new GridBagConstraints();
        gc.fill = GridBagConstraints.HORIZONTAL;
        gc.insets = new Insets(10, 10, 10, 10);
        frame.add(new RoundedPanel(), gc);

        //pack frame (size components to preferred size)
        frame.pack();
        frame.setVisible(true);//make frame visible
    


class RoundedPanel extends JPanel 

    /**
     * Stroke size. it is recommended to set it to 1 for better view
     */
    protected int strokeSize = 1;
    /**
     * Color of shadow
     */
    protected Color shadowColor = Color.black;
    /**
     * Sets if it drops shadow
     */
    protected boolean shady = true;
    /**
     * Sets if it has an High Quality view
     */
    protected boolean highQuality = true;
    /**
     * Double values for Horizontal and Vertical radius of corner arcs
     */
    protected Dimension arcs = new Dimension(0, 0);
    //protected Dimension arcs = new Dimension(20, 20);//creates curved borders and panel
    /**
     * Distance between shadow border and opaque panel border
     */
    protected int shadowGap = 10;
    /**
     * The offset of shadow.
     */
    protected int shadowOffset = 4;
    /**
     * The transparency value of shadow. ( 0 - 255)
     */
    protected int shadowAlpha = 150;
    int width = 300, height = 300;

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

    @Override
    protected void paintComponent(Graphics g) 
        super.paintComponent(g);
        Color shadowColorA = new Color(shadowColor.getRed(),
                shadowColor.getGreen(), shadowColor.getBlue(), shadowAlpha);
        Graphics2D graphics = (Graphics2D) g;

        //Sets antialiasing if HQ.
        if (highQuality) 
            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
        

        //Draws shadow borders if any.
        if (shady) 
            graphics.setColor(shadowColorA);
            graphics.fillRoundRect(
                    shadowOffset,// X position
                    shadowOffset,// Y position
                    width - strokeSize - shadowOffset, // width
                    height - strokeSize - shadowOffset, // height
                    arcs.width, arcs.height);// arc Dimension
         else 
            shadowGap = 1;
        

        //Draws the rounded opaque panel with borders.
        graphics.setColor(getBackground());
        graphics.fillRoundRect(0, 0, width - shadowGap,
                height - shadowGap, arcs.width, arcs.height);
        graphics.setColor(getForeground());
        graphics.setStroke(new BasicStroke(strokeSize));
        graphics.drawRoundRect(0, 0, width - shadowGap,
                height - shadowGap, arcs.width, arcs.height);

        //Sets strokes to default, is better.
    

    @Override
    public Dimension getPreferredSize() 
        return new Dimension(width, height);
    

参考:

Rounded Border JPanel

【讨论】:

我希望它在边缘淡出。 @RyanNaddy 我不确定我是否理解...请发布某种样本以显示整个预期效果

以上是关于JPanel 投影的主要内容,如果未能解决你的问题,请参考以下文章

将JPanel从其他类添加到JPanel

如何对齐 JPanel 在另一个 JPanel 下方居中?

JAVA--JPanel

JPanel与JFrame的区别

交换JPanel,第二个JPanel不显示

在 JPanel 上的任何位置检测鼠标进入/退出事件