JPanel Drop Shadow
我有一个元素,我想添加一个投影到它,我怎么能添加一个漂亮的褪色的投影到元素?我是否需要使用外部库,或者是否有内置的、我可以使用的内容?JPanel
例:
我有一个元素,我想添加一个投影到它,我怎么能添加一个漂亮的褪色的投影到元素?我是否需要使用外部库,或者是否有内置的、我可以使用的内容?JPanel
例:
因此,我研究了扩展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);
}
}
结果:
这是一个完整的黑客
这将要求您拥有用于模糊实现的 JH-Labs 滤镜副本
这是一个昂贵的操作,因为它使用模糊操作,我使用它的原因是将考虑它所阴影的组件的形状。
你遇到的主要问题是,边框不是它们本身,而是透明的,没有办法真正拥有不透明的组件和透明的边框。斩钉截铁
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;
}
}