JPanel投影

我有一个JPanel元素,我想添加一个投影,我怎么能添加一个漂亮的褪色阴影到元素? 我是否需要使用外部库或者是否有可以使用的内置库?

例:

在此处输入图像描述

所以我研究了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); } } 

结果如下:

阴影效果

这是一个完整的HACK

这将要求您获得用于模糊实现的JH-Labsfilter的副本

这是一个昂贵的操作,因为它使用模糊操作,我使用它的原因是将考虑它正在阴影的组件的形状。

在此处输入图像描述

你遇到的主要问题是边框不是自己的,透明的,没有办法真正拥有不透明的组件和透明的边框。 Hench the hack

 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 BORDER_CACHE = new WeakHashMap(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; } } 

简单的投影你可以解决。 您可以在这些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)); } } } 

你的意思是这样的:

在此处输入图像描述

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

参考:

  • 圆形边框JPanel