Drawing in JPanel disappears when scrolling or ressizing the main frame - java

I draw many shapes in panel and it works, but when I scroll the panel or when I resize the frame the drawings disappears.
I looked at other questions on this topic, but did not find a solution to the problem.
The screenshots:
The code:
public class ZoomPane {
public static void main(String[] args) {
new ZoomPane();
}
public ZoomPane() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
. . .
}
});
}
public class TestPane extends JPanel {
private float scale = 1;
public TestPane() {
addMouseWheelListener(new MouseAdapter() {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
double delta = 0.05f * e.getPreciseWheelRotation();
scale += delta;
revalidate();
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
Dimension size = new Dimension(200, 200);
size.width = Math.round(size.width * scale);
size.height = Math.round(size.height * scale);
return size;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
AffineTransform at = new AffineTransform();
at.scale(scale, scale);
g2d.setTransform(at);
g2d.setColor(Color.RED);
gr.draw(new Line2D.Double((int)this.getWidth() / 2, 0, (int)this.getWidth() / 2, this.getHeight()));
gr.draw(new Line2D.Double(0, (int)this.getHeight() / 2, this.getWidth(), (int)this.getHeight() / 2));
g2d.dispose();
}
}
}
How can I fix this bug?

You are clobbering the Graphics2D transform put in place by Swing. JScrollPane relies on it.
Instead of replacing the transform, you need to append to it. Replace this:
AffineTransform at = new AffineTransform();
at.scale(scale, scale);
g2d.setTransform(at);
with this:
AffineTransform at = new AffineTransform();
at.scale(scale, scale);
g2d.transform(at);
The setTransform replaces the transform; the transform method appends to it.
A cleaner solution is to replace all three lines with this:
g2d.scale(scale, scale);

Related

Simple Crosshair in java [duplicate]

This question already has an answer here:
Java: mouseDragged and moving around in a graphical interface
(1 answer)
Closed 5 years ago.
I have to draw a simple crosshair. All I am seeing is a blank panel.
class ChartPanel extends JPanel implements MouseMotionListener{
Graphics2D g;
Dimension dimFrame;
ChartPanel() {
addMouseMotionListener(this);
}
public void mouseMoved(MouseEvent e) {
drawCrosshair(e.getX(),e.getY());
}
public void mouseDragged(MouseEvent e) {}
protected void paintComponent(Graphics g2) {
g = (Graphics2D)g2;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
dimFrame = getSize();
setBackground(Color.WHITE);
}
public Dimension getPreferredSize() {
return new Dimension(700, 500);
}
void drawCrosshair(double x, double y) {
double maxx = dimFrame.getWidth();
double maxy = dimFrame.getHeight();
g.setPaint(Color.BLACK);
g.draw(new Line2D.Double(0, y, maxx, y));
g.draw(new Line2D.Double(x, 0, x, maxy));
}
}
public class pra {
public static void main(String[] args) {
JFrame jFrame = new JFrame();
ChartPanel chartPanel = new ChartPanel();
jFrame.add(chartPanel);
jFrame.pack();
jFrame.setVisible(true);
jFrame.setExtendedState(Frame.MAXIMIZED_BOTH);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
And it is getting into drawCrosshair() method with correct value. I have no clue what I am doing wrong.
You can just dispose of drawCrosshair(), and draw the crosshair in the paint method, which would replace the paintComponent method (Actually I think that you shouldn't ever override paintComponent):
Graphics2D g;
Dimension dimFrame;
int x, y;
ChartPanel() {
addMouseMotionListener(this);
setPreferredSize(new Dimension(700, 500));
}
public void mouseMoved(MouseEvent e) {
x = e.getX();
y = e.getY();
repaint();
}
public void mouseDragged(MouseEvent e) {
}
public void paint(Graphics g2) {
super.paint(g2);
g = (Graphics2D) g2;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
dimFrame = getSize();
g.clearRect(0, 0, dimFrame.width, dimFrame.height);//clears previous drawings
g.setColor(Color.BLACK);
g.drawLine(x - 10, y, x + 10, y);
g.drawLine(x, y - 10, x, y + 10);
}
And this should do it (actually it does, as I've tested it ;) )

Drawing lines on top of image without using AlphaComposite

I am currently having lots of difficulty in one of my assignments. The task for this assignment is to create an aviation radar, VOR, with java's GUI features. When the user presses either the left/right arrow on their keyboard, the circular radar should rotate, and a needle in the middle of the radar should move left/right.
I am very close to finishing, but I am stuck on one part. I drew an image of the radar, and I attempted to draw a line on top of it. However, I was only successful when I was able to make all JPanels transparent. This causes a problem because it is difficult to see what I drew on top of it.
So, my question is, how would I draw it on top of the radar image without making everything transparent? Below is my code
public class finalVORGUI extends JPanel{
private JPanel rotationPanel;
private JPanel needle;
private JPanel attributes;
private int degrees;
private String CurrentRadial;
private int x;
private int y1;
private int y2;
final int WIDTH = 600;
final int HEIGHT = 600;
private ImageIcon radar = new ImageIcon("image/vor1.png");
/**
* The constructor for the class
* It's going to set the dimension of the program to 600x600, the
* background is going to be white (in order to blend in with the
* vor image), and it is going to add in the VOR radar and a radial
* indicator that will let the user know which radial he/she is on
*/
public finalVORGUI(){
JLayeredPane lp = new JLayeredPane();
lp.setPreferredSize(new Dimension(WIDTH, HEIGHT));
setBackground(Color.white);
lp.setLayout(null);
lp.setFocusable(true);
lp.addKeyListener(new KeyboardListener());
rotationPanel = new JPanel();
rotationPanel = new TurningCanvas();
needle = new JPanel();
needle = new DrawNeedle();
attributes = new JPanel();
attributes = new DrawAttributes();
lp.add(rotationPanel, Integer.valueOf(1));
lp.add(needle, Integer.valueOf(2));
lp.add(attributes, Integer.valueOf(3));
needle.setBounds(100,0, needle.getPreferredSize().width, needle.getPreferredSize().height);
rotationPanel.setBounds(100, 100, rotationPanel.getPreferredSize().width, rotationPanel.getPreferredSize().height);
attributes.setBounds(100, 100, rotationPanel.getPreferredSize().width, rotationPanel.getPreferredSize().height);
add(lp);
degrees = 360; //to edit: this is going to be the radial the radar is currently facing
x = 172; //x is the location of the needle
y1 = 155;
y2 = 330;
CurrentRadial = "Radial: " + degrees; //A string that is always going to be above the radar. it's going to let the user know the current radial
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawString(CurrentRadial, 250, 100);
}
public class DrawAttributes extends JPanel{
public DrawAttributes(){
setOpaque(false);
add(new Attributes());
}
public class Attributes extends JPanel{
int w = 500;
int h = 400;
public Attributes(){
setPreferredSize(new Dimension(w,h));
setBackground(Color.white);
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.drawString("To",300,400);
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 0.3f));
g2.setStroke(new BasicStroke(3));
super.paintComponent (g);
g2.dispose();
}
}
}
public class DrawNeedle extends JPanel{ //todo: make sure this works and adds correctly to the LayeredPane
public DrawNeedle(){
setOpaque(false);
add(new Needle());
}
public class Needle extends JPanel{
int w = 500;
int h = 400;
public Needle(){
setPreferredSize(new Dimension(w,h));
setBackground(Color.white);
}
private void doDrawing(Graphics g){
Graphics2D g4 = (Graphics2D) g;
g4.drawString("TO", 190, 200);
g4.drawString("FROM",190, 300);
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
RenderingHints hints = new RenderingHints(null);
hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
hints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHints(hints);
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 0.3f));
g2.setStroke(new BasicStroke(3));
doDrawing(g);
g2.drawLine(x,y1,x,y2);
super.paintComponent (g);
g2.dispose();
}
}
}
public class TurningCanvas extends JPanel{
public TurningCanvas(){
setOpaque(false);
add(new TurningImage());
}
public class TurningImage extends JPanel{
int w = radar.getIconWidth()- 20;
int h = radar.getIconHeight() -20;
public TurningImage(){
setPreferredSize(new Dimension(w,h));
setBackground(Color.white);
}
public void paintComponent(Graphics g){
super.paintComponent (g);
Graphics2D g2 = (Graphics2D) g;
RenderingHints hints = new RenderingHints(null);
hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
hints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHints(hints);
g2.rotate (Math.toRadians(degrees),w/2, h/2);
g2.drawImage(radar.getImage(), 0, 0, this);
//g2.drawLine(171,0,x,300);
g2.dispose();
}
}
}
/**
* This is the keyboard listener that this program will be using
* Depending on what the user wishes, or what type of keyboard they have,
* they will be able to find their desired radial by using the arrow keys
* or the "a" and "d" buttons. Left arrow to minus radial, right arrow to plus radial, etc etc...
*
*/
public class KeyboardListener implements KeyListener{
public void keyPressed (KeyEvent event) {
if(event.getKeyCode() == KeyEvent.VK_LEFT){
degrees--;
x--;
if(degrees <= 0){
degrees = 360;
}
if(x <= 89){
x = 89;
}
CurrentRadial = "Radial: " + degrees;
repaint();
}
if(event.getKeyCode() == KeyEvent.VK_RIGHT){
degrees++;
x++;
if(degrees >= 360){
degrees = 1;
}
if(x >= 250){
x = 250;
}
CurrentRadial = "Radial: " + degrees;
repaint();
}
}
public void keyTyped (KeyEvent event) {}
public void keyReleased (KeyEvent event) {}
}
/**
* The main method of this class
* This is going to make a new JFrame, which will hold the new
* VOR radar
* #param args
*/
public static void main(String[] args){
finalVORGUI test = new finalVORGUI();
JFrame frame = new JFrame("VOR Radar");
frame.setContentPane(test);
frame.pack();
frame.setVisible(true);
}
}

draw ellipse relative to panel java

Basically I want to draw an ellipse which its size is relative to JPanel in Java.
for example: Ellipse2D e = new Ellipse2D.Double(0, 0, w, h)
Which w and h is the size of the panel. So by doing this, the ellipse will automatically resize when the panel is changing its size.
I have tried this but actually it doesn't work, I wrote this code for testing only.
public class Help extends JFrame{
public static void main(String [] agrs){
Help h = new Help();
h.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
h.init();
}
public void init(){
this.setLayout(new FlowLayout());
this.setSize(2000, 1000);
JPanel a = new JPanel();
a.setPreferredSize(new Dimension(1000, 500));
a.setBorder(BorderFactory.createLineBorder(Color.yellow, 3));
Help_Option k = new Help_Option(a.getPreferredSize().width/2, a.getPreferredSize().height/4);
k.setPreferredSize(new Dimension(1000, 400));
a.add(k);
this.add(a);
this.validate();
this.setVisible(true);
}
}
class Help_Option extends JComponent implements MouseMotionListener{
private static int x, y;
private Ellipse2D ellipse = new Ellipse2D.Double(0, 0, x, y);
private Color c = Color.MAGENTA;
public Help_Option(int x, int y){
Help_Option.x = x;
Help_Option.y = y;
this.addMouseMotionListener(this);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLUE);
g2d.draw(ellipse);
g2d.setColor(c);
g2d.fill(ellipse);
g2d.setColor(Color.BLACK);
g2d.setFont(new Font("TimesRoman", Font.BOLD, 20));
g2d.drawString("Here I am", 250, 100);
}
public void setColor(Color c){
this.c = c;
}
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
if(ellipse.contains(e.getX(), e.getY())){
setColor(Color.GREEN);
repaint();
}else{
setColor(Color.MAGENTA);
repaint();
}
}
}
Thank you for your code snippet. In order to make it work, you need to initalize the ellipse in the Help_Option-Constructor otherwise it is draw at 0, 0 with the widht and height of 0 and 0.
...
private Ellipse2D ellipse = null; //also works w/o this assignment.
private Color c = Color.MAGENTA;
public Help_Option(int x, int y){
Help_Option.x = x;
Help_Option.y = y;
ellipse = new Ellipse2D.Double(0, 0, x, y);
this.addMouseMotionListener(this);
}
...
If I change the code accordingly, the ellipse stays at its size even though I resize the window.
Please give me a hint (screenshot) what you mean.
I want to draw an ellipse which its size is relative to JPanel in Java.
Then you need to create the Ellipse object in the paintComponent() method. You can use the getWidth() and getHeight() methods to get the current size of the panel.

Rotating text in Java2D on Mac

Here is the problem I am currently facing: I want to draw a String on a JPanel using Java2D. The String has to be rotated of a user-defined angle.
Under that String, I also paint the background in a given color to facilitate reading (plenty of other things are drawn on my JPanel).
What I did, in the overridden paint method of my JPanel, is the following:
final Graphics2D g2 = (Graphics2D) g.create();
final int textWidth = g.getFontMetrics().stringWidth(textToDraw);
final int textHeight = g.getFontMetrics().getHeight();
g2.translate(pointToDraw.x, pointToDraw.y);
g2.rotate(angle);
g2.setColor(textBackground);
g2.fillRect(deltaX, -textHeight, textWidth, textHeight);
g2.setColor(drawColor);
g2.setFont(font);
g2.drawString(textToDraw, deltaX, deltaY);
g2.dispose();
This works very well on linux, but on Mac OS X (with Java 1.6), the text is not displayed properly: the text is correctly rotated but after each character, there is a line break.
How can I make it work on both platforms?
I don't think this is the solution you will want, but from everything I've been able to read, there doesn't seem to be a better solution...
The problem seems to be that the Mac is rotating each character, not just the String
Basically, I've cheated. This renders the text to a BufferedImage (you should create the image only when the properties change, unlike me, which I've done it within the paint method) and then rotates the image...
public class RotateText {
public static void main(String[] args) {
new RotateText();
}
public RotateText() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | 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 {
private String textToDraw = "Stack Overflow";
private double angle = 90;
private Color drawColor = Color.BLACK;
public TestPane() {
Timer timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
angle += 2;
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g.create();
FontMetrics fm = g2.getFontMetrics();
int textWidth = fm.stringWidth(textToDraw);
int textHeight = fm.getHeight();
BufferedImage img = new BufferedImage(textWidth, textHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D ig = img.createGraphics();
ig.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
ig.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
ig.setColor(drawColor);
ig.drawString(textToDraw, 0, fm.getAscent());
ig.dispose();
int x = (getWidth() - textWidth) / 2;
int y = (getHeight() - textHeight) / 2;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setTransform(AffineTransform.getRotateInstance(Math.toRadians(angle), getWidth() / 2, getHeight() / 2));
g2.drawImage(img, x, y, this);
g2.dispose();
}
}
}

JPanel Drop Shadow

I have a JPanel element and I would like added a drop shadow to it, how can I add a nice faded drop shadow to the element? Do I need to use external libraries or is there something that is built in that I can use?
Example:
So I looked into swingx which extends JPanel and was able to achieve the results I was looking for with the following code:
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);
}
}
And the result:
This is a complete HACK
This will require you to have a copy of JH-Labs Filters for the blur implementation
This is an expensive operation as it uses a blur operation, the reason I use it is that will take into account the the shape of the component it is shadowing.
The main problem you have is that borders aren't them selves, transparent, there's no way to really have an opaque component and a transparent border. 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<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;
}
}
}
}
UPDATED with additional Example
The drop shadow border has limitations, it can't take into consideration the shape of the component, as the time the border is painted, the component hasn't begin, so we have no reference point.
In order to be able to generate a drop shadow which takes into consideration the shape of the component, we need to create a custom component and inject our border directly into the paint process.
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;
}
}
Simple drop shadow you can work off. You can see implemented on these 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));
}
}
}
Do you mean something like this:
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);
}
}
Reference:
Rounded Border JPanel

Categories