web-dev-qa-db-ja.com

JPanelドロップシャドウ

JPanel要素があり、それにドロップシャドウを追加したいのですが、要素に素敵なフェードドロップシャドウを追加するにはどうすればよいですか?外部ライブラリを使用する必要がありますか、それとも使用できる組み込みのものがありますか?

例:

enter image description here

19
Get Off My Lawn

そこで、JPanelを拡張し、次のコードで探していた結果を達成することができた swingx を調べました。

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);
    }
}

そして結果:

Dropshadow

26
Get Off My Lawn

これは完全です[〜#〜]ハック[〜#〜]

これには、ブラーの実装のために JH-Labs Filters のコピーが必要になります

ぼかし操作を使用するため、これは負荷の高い操作です。私が使用する理由は、シャドウしているコンポーネントの形状を考慮するためです。

enter image description here

あなたが持っている主な問題は、ボーダー自体が透明ではなく、実際には不透明なコンポーネントと透明なボーダーを持つ方法がないということです。ハックをヘンチ

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;

            }
        }
    }
}

追加の例で更新されました

ドロップシャドウの境界線には制限があり、コンポーネントの形状を考慮することができません。境界線がペイントされた時点ではコンポーネントが開始されていないため、参照ポイントがありません。

enter image description hereenter image description here

コンポーネントの形状を考慮したドロップシャドウを生成できるようにするには、カスタムコンポーネントを作成し、境界線をペイントプロセスに直接挿入する必要があります。

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;

    }
}
11
MadProgrammer

あなたが取り除くことができる単純なドロップシャドウ。これらのJPanelsに実装されていることがわかります。

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));
        }
    }
}
4
Yen

次のような意味ですか?

enter image description here

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);
    }
}

参照:

3
David Kroukamp