I try to build my personal JFrame from a PNG picture. But there is different behavior between Mac OSX 10.8 and Windows 7. (I have to use JDK 6)
Here is my code :
[...]
public Fenetre()
{
this.setLocationRelativeTo(null);
this.setUndecorated(true);
this.setBackground(new Color(0,0,0,0));
try {
image = ImageIO.read(this.getClass().getResource("/Images/frame.png"));
} catch (IOException e) {
e.printStackTrace();
}
this.setSize(image.getWidth(),image.getHeight());
this.setLayout(null);
panel = new JPanel();
JButton quit = new JButton("Quitter");
panel.add(quit);
Dimension size = panel.getPreferredSize();
panel.setBounds(67, 45, size.width, size.height);
this.add(panel);
this.addMouseListener(this);
this.addMouseMotionListener(this);
this.setVisible(true);
}
public void paint(Graphics g) {
Graphics2D g2 =(Graphics2D) g;
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC)); // SRC_ATOP > Windows
g2.drawImage(image, 0, 0, this);
panel.update(panel.getGraphics());
}
[...]
The result on Mac OSX 10.8 (AlphaComposite = SRC) :
http://imageshack.us/photo/my-images/15/maczr.png/
Then, on Windows 7 (AlphaComposite = SRC_ATOP), at start up and when I move it, I can see :
http://imageshack.us/photo/my-images/16/windowsqu.jpg/
How do it ?
Your code is incomplete, but it seems to me like you are overriding the paint() method of a JFrame. You should never do this (unless you know what you doing and you invoke super.paint(..))!
If you want to display an image in the frame then either:
a) add a JLabel with an image to the frame
b) or do custom painting on a JPanel by overriding the paintComponent() method and then add the panel to the frame.
You are not honoring the paint chain
public void paint(Graphics g) {
// You must call super.paint somewhere here...
Graphics2D g2 =(Graphics2D) g;
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC)); // SRC_ATOP > Windows
g2.drawImage(image, 0, 0, this);
panel.update(panel.getGraphics());
}
You should NEVER call Component#update.
panel.update(panel.getGraphics());
This is called on you behalf by the repaint manager, which calls paint.
paint calls paintComponent, paintBorder and paintChildren failing to honor the paint chain means that not of these methods are been called and they are very important
As camickr has pointed out, you should never need to overrride the paint method of a top level container, like JFrame.
Instead, create a custom component that is capable of performing you painting for you and set it as the frames content pane...
this.setLocationRelativeTo(null);
this.setUndecorated(true);
this.setBackground(new Color(0,0,0,0));
setContentPane(new FancyPaintPane());
pack();
And the FancyPaintPane
public class FancyPaintPane extends JPanel {
private BufferedImage image;
public FancyPaintPane() {
try {
image = ImageIO.read(this.getClass().getResource("/Images/frame.png"));
} catch (IOException e) {
e.printStackTrace();
}
setOpaque(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(image.getWidth(), image.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
Graphics2D g2 = (Graphics2D) g.create();
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC)); // SRC_ATOP > Windows
g2.drawImage(image, 0, 0, this);
g2.dispose();
}
}
You should also never modify a Graphics context without reverting it. These are shared resources across all components within the same top level container. A better approach is to create a copy which you can manipulate and dispose of when you're done...
You are also trying to show a component with transparent elements without first marking the component as transparent. This can produce nasty paint artifacts. You should call JComponent#setOpaque passing it a false value.
I would also strongly discourage you from using null layouts. They have a nasty habit of coming back and biting you.
Updated with simple example
public class CirclePaneTest {
public static void main(String[] args) {
new CirclePaneTest();
}
public CirclePaneTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
CirclePane circlePane = new CirclePane();
circlePane.setLayout(new BorderLayout());
circlePane.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
System.exit(0);
}
}
});
JLabel label = new JLabel("Look Ma, I'm a circle");
label.setHorizontalAlignment(JLabel.CENTER);
label.setVerticalAlignment(JLabel.CENTER);
JFrame frame = new JFrame("Test");
frame.setUndecorated(true);
frame.setBackground(new Color(0, 0, 0, 0));
frame.setContentPane(circlePane);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class CirclePane extends JPanel {
public CirclePane() {
setOpaque(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected int getRadius() {
return Math.min(getWidth(), getHeight()) - 1;
}
#Override
public Insets getInsets() {
int radius = getRadius();
int xOffset = (getWidth() - radius) / 2;
int yOffset = (getHeight() - radius) / 2;
Insets insets = new Insets(
radius / 6,
radius / 6,
radius / 6,
radius / 6);
return insets;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int radius = getRadius();
int xOffset = (getWidth() - radius) / 2;
int yOffset = (getHeight() - radius) / 2;
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(getBackground());
g2d.fillOval(xOffset, yOffset, radius, radius);
g2d.setColor(Color.GRAY);
g2d.drawOval(xOffset, yOffset, radius, radius);
g2d.dispose();
}
}
}
Updated with code for Java 7
The following code should be used to set up the frame to be transparent under Java 6.
JFrame frame = new JFrame("Test");
frame.setAlwaysOnTop(true);
frame.setUndecorated(true);
try {
Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
if (awtUtilsClass != null) {
Method method = awtUtilsClass.getMethod("setWindowOpaque", Window.class, boolean.class);
method.invoke(null, frame, false);
}
} catch (Exception exp) {
}
//frame.setBackground(new Color(0, 0, 0, 0));
frame.setContentPane(circlePane);
Related
I found a decorator pattern example with Swing component. The following code is drawing three buttons on a JFrame. One of the button has slash on it and implemented with Decorator pattern. The original one has paint() method, but I replaced paint() with paintComponent(Graphics g), then it fails to draw lines on the button. Is it impossible to use paintComponent() instead of paint()? If possible how to do that? What is the problem of this trial? What I am missing?
Original code link is Decorator pattern in Java.
public class DecoWindow extends JFrame implements ActionListener {
JButton Quit;
public DecoWindow() {
super("Deco Button");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel jp = new JPanel();
getContentPane().add(jp);
jp.add(new CoolDDecorator(new JButton("Cbutton")));
jp.add(new SlashDDecorator(new CoolDDecorator(new JButton("Dbutton"))));
jp.add(Quit = new JButton("Quit"));
Quit.addActionListener(this);
setSize(new Dimension(200, 100));
setVisible(true);
Quit.requestFocus();
}
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
public static void main(String argv[]) {
new DecoWindow();
}
}
class DDecorator extends JComponent {
public DDecorator(JComponent c) {
setLayout(new BorderLayout());
add("Center", c);
}
}
class SlashDDecorator extends DDecorator {
int x1, y1, w1, h1;
public SlashDDecorator(JComponent c) {
super(c);
}
public void setBounds(int x, int y, int w, int h) {
x1 = x; y1 = y;
w1 = w; h1 = h;
super.setBounds(x, y, w, h);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.red);
g2d.drawLine(0, 0, w1, h1);
}
}
class CoolDDecorator extends DDecorator {
boolean mouse_over; //true when mose over button
JComponent thisComp;
public CoolDDecorator(JComponent c) {
super(c);
mouse_over = false;
thisComp = this; //save this component
//catch mouse movements in inner class
c.addMouseListener(new MouseAdapter() {
public void mouseEntered(MouseEvent e) {
mouse_over = true; //set flag when mouse over
thisComp.repaint();
}
public void mouseExited(MouseEvent e) {
mouse_over = false; //clear flag when mouse not over
thisComp.repaint();
}
});
}
//paint the button
public void paintComponent(Graphics g) {
super.paintComponent(g); //first draw the parent button
Graphics2D g2d = (Graphics2D) g;
//if the mouse is not over the button
//erase the borders
if (!mouse_over) {
Dimension size = super.getSize();
g2d.setColor(Color.lightGray);
g2d.drawRect(0, 0, size.width - 1, size.height - 1);
g2d.drawLine(size.width - 2, 0, size.width - 2, size.height - 1);
g2d.drawLine(0, size.height - 2, size.width - 2, size.height - 2);
}
}
}
paint() calls paintComponent() then paintChildren(). Your component is painting the slash and returning from paintComponent(). Then the default paint() implementation moves on, eventually painting the children, which are those buttons, which then just paint right ontop of your slash.
Your IDE should let you place a breakpoint in your paint code. You can check the callstack to see what's going on. If you aren't using an IDE, you can look at what Swing is doing by looking at JComponent.java in src.zip within your JDK.
Why do you want to use paintComponent() anyway?
I am getting input==null while trying to load an Image. I can't seem to figure out why, even after browsing a good number of StackOverflow questions. It looks right to me.
Here is the code:
private BufferedImage image;
public practice(){
setLayout(new BorderLayout());
timeBall.start();
JPanel panelNorth = makePanel();
panelNorth.setBackground(Color.CYAN);
add(panelNorth, BorderLayout.NORTH);
try{
image = ImageIO.read(practice.class.getResource("/pics/flying.png"));
}catch(IOException e){
e.printStackTrace();
}
}
public Dimension getPreferredSize() {
return image == null ? new Dimension(400, 300): new Dimension(image.getWidth(), image.getHeight());
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLUE);
ellipse = new Ellipse2D.Double(Ox, Oy+Speedy, height, height);
Graphics2D graphics = (Graphics2D)g;
g.drawImage(image, 0, 0, this);
graphics.fill(ellipse);
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
Oy = Oy + Speedy / 10;
Ox = Ox + Speedx / 10;
}
How do I draw that semi-transparent rectangle on the screen?
That cannot be a JFrame because JFrames have the usual close, minimize, maximize options in top right.
if it is indeed a swing competent, How is it drawn in thin air? Without inserting it in a JFrame whatsoever?
Please tell me what it is and how I can implement it...
The immediate idea that comes to mind is to use java.awt.Robot to capture a screen shot, paint that to frameless window. From there you can simply draw a rectangle on it
Updated with example
... Took some time ...
public class SelectionRectangle {
public static void main(String[] args) {
new SelectionRectangle();
}
public SelectionRectangle() {
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.setUndecorated(true);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new BackgroundPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BackgroundPane extends JPanel {
private BufferedImage background;
private Point mouseAnchor;
private Point dragPoint;
private SelectionPane selectionPane;
public BackgroundPane() {
selectionPane = new SelectionPane();
try {
Robot bot = new Robot();
background = bot.createScreenCapture(getScreenViewableBounds());
} catch (AWTException ex) {
Logger.getLogger(SelectionRectangle.class.getName()).log(Level.SEVERE, null, ex);
}
selectionPane = new SelectionPane();
setLayout(null);
add(selectionPane);
MouseAdapter adapter = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
mouseAnchor = e.getPoint();
dragPoint = null;
selectionPane.setLocation(mouseAnchor);
selectionPane.setSize(0, 0);
}
#Override
public void mouseDragged(MouseEvent e) {
dragPoint = e.getPoint();
int width = dragPoint.x - mouseAnchor.x;
int height = dragPoint.y - mouseAnchor.y;
int x = mouseAnchor.x;
int y = mouseAnchor.y;
if (width < 0) {
x = dragPoint.x;
width *= -1;
}
if (height < 0) {
y = dragPoint.y;
height *= -1;
}
selectionPane.setBounds(x, y, width, height);
selectionPane.revalidate();
repaint();
}
};
addMouseListener(adapter);
addMouseMotionListener(adapter);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(background, 0, 0, this);
g2d.dispose();
}
}
public class SelectionPane extends JPanel {
private JButton button;
private JLabel label;
public SelectionPane() {
button = new JButton("Close");
setOpaque(false);
label = new JLabel("Rectangle");
label.setOpaque(true);
label.setBorder(new EmptyBorder(4, 4, 4, 4));
label.setBackground(Color.GRAY);
label.setForeground(Color.WHITE);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
add(label, gbc);
gbc.gridy++;
add(button, gbc);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.getWindowAncestor(SelectionPane.this).dispose();
}
});
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
label.setText("Rectangle " + getX() + "x" + getY() + "x" + getWidth() + "x" + getHeight());
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(128, 128, 128, 64));
g2d.fillRect(0, 0, getWidth(), getHeight());
float dash1[] = {10.0f};
BasicStroke dashed =
new BasicStroke(3.0f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER,
10.0f, dash1, 0.0f);
g2d.setColor(Color.BLACK);
g2d.setStroke(dashed);
g2d.drawRect(0, 0, getWidth() - 3, getHeight() - 3);
g2d.dispose();
}
}
public static Rectangle getScreenViewableBounds() {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
return getScreenViewableBounds(gd);
}
public static Rectangle getScreenViewableBounds(GraphicsDevice gd) {
Rectangle bounds = new Rectangle(0, 0, 0, 0);
if (gd != null) {
GraphicsConfiguration gc = gd.getDefaultConfiguration();
bounds = gc.getBounds();
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
bounds.x += insets.left;
bounds.y += insets.top;
bounds.width -= (insets.left + insets.right);
bounds.height -= (insets.top + insets.bottom);
}
return bounds;
}
}
Update with SnipIt Example
Some people have suggested using a transparent window laid over the top of the screen, this actually won't work, as transparent windows don't actually respond to mouse clicks UNLESS they have something to be painted on them that will allow the mouse event to be trapped.
It's also been suggested that you use a Window as the selection mechanism, this is a valid answer, however, I would (personally) find that to be an unsuitable solution, as you want the user to simply click and drag the selection rectangle (IMHO).
Another approach is use something like SnipIt.
public class SnipIt {
public static void main(String[] args) {
new SnipIt();
}
public SnipIt() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setUndecorated(true);
// This works differently under Java 6
frame.setBackground(new Color(0, 0, 0, 0));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new SnipItPane());
frame.setBounds(getVirtualBounds());
frame.setVisible(true);
}
});
}
public class SnipItPane extends JPanel {
private Point mouseAnchor;
private Point dragPoint;
private SelectionPane selectionPane;
public SnipItPane() {
setOpaque(false);
setLayout(null);
selectionPane = new SelectionPane();
add(selectionPane);
MouseAdapter adapter = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
mouseAnchor = e.getPoint();
dragPoint = null;
selectionPane.setLocation(mouseAnchor);
selectionPane.setSize(0, 0);
}
#Override
public void mouseDragged(MouseEvent e) {
dragPoint = e.getPoint();
int width = dragPoint.x - mouseAnchor.x;
int height = dragPoint.y - mouseAnchor.y;
int x = mouseAnchor.x;
int y = mouseAnchor.y;
if (width < 0) {
x = dragPoint.x;
width *= -1;
}
if (height < 0) {
y = dragPoint.y;
height *= -1;
}
selectionPane.setBounds(x, y, width, height);
selectionPane.revalidate();
repaint();
}
};
addMouseListener(adapter);
addMouseMotionListener(adapter);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Rectangle bounds = new Rectangle(0, 0, getWidth(), getHeight());
Area area = new Area(bounds);
area.subtract(new Area(selectionPane.getBounds()));
g2d.setColor(new Color(192, 192, 192, 64));
g2d.fill(area);
}
}
public class SelectionPane extends JPanel {
private JButton button;
private JLabel label;
public SelectionPane() {
button = new JButton("Close");
setOpaque(false);
label = new JLabel("Rectangle");
label.setOpaque(true);
label.setBorder(new EmptyBorder(4, 4, 4, 4));
label.setBackground(Color.GRAY);
label.setForeground(Color.WHITE);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
add(label, gbc);
gbc.gridy++;
add(button, gbc);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.getWindowAncestor(SelectionPane.this).dispose();
}
});
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
label.setText("Rectangle " + getX() + "x" + getY() + "x" + getWidth() + "x" + getHeight());
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
// I've chosen NOT to fill this selection rectangle, so that
// it now appears as if you're "cutting" away the selection
// g2d.setColor(new Color(128, 128, 128, 64));
// g2d.fillRect(0, 0, getWidth(), getHeight());
float dash1[] = {10.0f};
BasicStroke dashed =
new BasicStroke(3.0f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER,
10.0f, dash1, 0.0f);
g2d.setColor(Color.BLACK);
g2d.setStroke(dashed);
g2d.drawRect(0, 0, getWidth() - 3, getHeight() - 3);
g2d.dispose();
}
}
public static Rectangle getVirtualBounds() {
Rectangle bounds = new Rectangle(0, 0, 0, 0);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice lstGDs[] = ge.getScreenDevices();
for (GraphicsDevice gd : lstGDs) {
bounds.add(gd.getDefaultConfiguration().getBounds());
}
return bounds;
}
}
Update Multi Monitor Support to the Example Answer from #MadProgrammer.
Without ExtendedState(JFrame.MAXIMIZED_BOTH) and pack()
public SelectionRectangle() {
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.setUndecorated(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new BackgroundPane());
frame.setResizable( false );
frame.setBounds( getScreenViewableBounds() );
frame.setVisible(true);
}
});
}
public static Rectangle getScreenViewableBounds() {
GraphicsDevice[] devices = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
int minx = Integer.MAX_VALUE;
int miny = Integer.MAX_VALUE;
int maxx = Integer.MIN_VALUE;
int maxy = Integer.MIN_VALUE;
for( GraphicsDevice device : devices ) {
for( GraphicsConfiguration config : device.getConfigurations() ) {
Rectangle bounds = config.getBounds();
minx = Math.min( minx, bounds.x );
miny = Math.min( miny, bounds.y );
maxx = Math.max( maxx, bounds.x + bounds.width );
maxy = Math.max( maxy, bounds.y + bounds.height );
}
}
return new Rectangle( new Point(minx, miny), new Dimension(maxx - minx, maxy - miny) );
}
You could use a transparent, undecorated frame in order to create a basic border.
public class ScreenRectangle extends JFrame {
public ScreenRectangle() {
this.setUndecorated(true);
this.setBackground(new Color(0, 0, 0, 0.25F));
// opacity ranges 0.0-1.0 and is the fourth paramater
this.add(new DrawPanel());
}
private class DrawPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(0, 0, this.getWidth(), this.getHeight());
// any other drawing
}
}
}
The frame may also need to be setOpaque, or the panel size may need to be handled, but this is the general idea of it.
I've just recently extended JPanel for use in a project which we want to appear to be more "3D". That's my bosses' way of requiring shadowing and rounded corners on components. That's been accomplished as shown on many online examples. I did it like this:
public class RoundedPanel extends JPanel
{
protected int _strokeSize = 1;
protected Color _shadowColor = Color.BLACK;
protected boolean _shadowed = true;
protected boolean _highQuality = true;
protected Dimension _arcs = new Dimension(30, 30);
protected int _shadowGap = 5;
protected int _shadowOffset = 4;
protected int _shadowAlpha = 150;
protected Color _backgroundColor = Color.LIGHT_GRAY;
public RoundedPanel()
{
super();
setOpaque(false);
}
#Override
public void setBackground(Color c)
{
_backgroundColor = c;
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
int shadowGap = this._shadowGap;
Color shadowColorA = new Color(_shadowColor.getRed(), _shadowColor.getGreen(), _shadowColor.getBlue(), _shadowAlpha);
Graphics2D graphics = (Graphics2D) g;
if(_highQuality)
{
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
if(_shadowed)
{
graphics.setColor(shadowColorA);
graphics.fillRoundRect(_shadowOffset, _shadowOffset, width - _strokeSize - _shadowOffset,
height - _strokeSize - _shadowOffset, _arcs.width, _arcs.height);
}
else
{
_shadowGap = 1;
}
graphics.setColor(_backgroundColor);
graphics.fillRoundRect(0, 0, width - shadowGap, height - shadowGap, _arcs.width, _arcs.height);
graphics.setStroke(new BasicStroke(_strokeSize));
graphics.setColor(getForeground());
graphics.drawRoundRect(0, 0, width - shadowGap, height - shadowGap, _arcs.width, _arcs.height);
graphics.setStroke(new BasicStroke());
}
}
I am creating a test frame with the following code:
public class UITest
{
private static JFrame mainFrame;
private static ImagePanel mainPanel;
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
mainFrame = new JFrame();
mainFrame.setVisible(true);
try
{
mainPanel = new ImagePanel(ImageIO.read(this.getClass().getResource("/content/diamondPlate_Light.jpg")));
//mainPanel.setBounds(0, 0, 800, 600);
}
catch(IOException e)
{
}
mainPanel.setLayout(null);
RoundedPanel rPanel = new RoundedPanel();
rPanel.setBounds(10, 10, 200, 200);
rPanel.setBackground(new Color(168, 181, 224));
mainPanel.add(rPanel);
rPanel = new RoundedPanel();
rPanel.setBounds(220, 10, 560, 200);
rPanel.setBackground(new Color(168, 224, 168));
mainPanel.add(rPanel);
rPanel = new RoundedPanel();
rPanel.setBounds(10, 220, 770, 300);
rPanel.setBackground(new Color(224, 168, 168));
mainPanel.add(rPanel);
mainFrame.setSize(800, 600);
mainFrame.getContentPane().add(mainPanel);
}
});
}
}
And it results in this (sans the background image of the JFrame's contentPane:
What I would really like to do is generate the red, green, and blue panels with the rounded corners, but filled by a different image instead of the Color. I still want the properly rounded corners, but I'm unsure of how to do this.
If I've got a large texture, can I simply "clip" a piece of it out in the size and shape of the RoundedPanel? I need to evaluate this, since it just occurred to me as I typed, but if I can create a piece of geometry like what is used in graphics.fillRoundRect(...) and then clip the image, this could work.
Are there any other ways of doing this that I'm missing? I'd appreciate any feedback you might be able to offer. Thanks.
Edit:
Based upon the idea in the selected solution below, I've got the following results:
It needs to be whipped into shape for production and the background images are poorly chosen, but as a demo, the following RoundedPanel code gets us to the above results:
public class RoundedPanel extends JPanel
{
protected int strokeSize = 1;
protected Color _shadowColor = Color.BLACK;
protected boolean shadowed = true;
protected boolean _highQuality = true;
protected Dimension _arcs = new Dimension(30, 30);
protected int _shadowGap = 5;
protected int _shadowOffset = 4;
protected int _shadowAlpha = 150;
protected Color _backgroundColor = Color.LIGHT_GRAY;
protected BufferedImage image = null;
public RoundedPanel(BufferedImage img)
{
super();
setOpaque(false);
if(img != null)
{
image = img;
}
}
#Override
public void setBackground(Color c)
{
_backgroundColor = c;
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
int shadowGap = this._shadowGap;
Color shadowColorA = new Color(_shadowColor.getRed(), _shadowColor.getGreen(), _shadowColor.getBlue(), _shadowAlpha);
Graphics2D graphics = (Graphics2D) g;
if(_highQuality)
{
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
if(shadowed)
{
graphics.setColor(shadowColorA);
graphics.fillRoundRect(_shadowOffset, _shadowOffset, width - strokeSize - _shadowOffset,
height - strokeSize - _shadowOffset, _arcs.width, _arcs.height);
}
else
{
_shadowGap = 1;
}
RoundRectangle2D.Float rr = new RoundRectangle2D.Float(0, 0, (width - shadowGap), (height - shadowGap), _arcs.width, _arcs.height);
Shape clipShape = graphics.getClip();
if(image == null)
{
graphics.setColor(_backgroundColor);
graphics.fill(rr);
}
else
{
RoundRectangle2D.Float rr2 = new RoundRectangle2D.Float(0, 0, (width - strokeSize - shadowGap), (height - strokeSize - shadowGap), _arcs.width, _arcs.height);
graphics.setClip(rr2);
graphics.drawImage(this.image, 0, 0, null);
graphics.setClip(clipShape);
}
graphics.setColor(getForeground());
graphics.setStroke(new BasicStroke(strokeSize));
graphics.draw(rr);
graphics.setStroke(new BasicStroke());
}
}
Thanks for the help.
Try "clipping area" (see the g.setClip() call):
public static void main(String[] args) {
JFrame f = new JFrame();
f.setSize(new Dimension(600, 400));
f.getContentPane().setLayout(null);
RoundPanel rp = new RoundPanel();
rp.setBounds(100, 50, 400, 300);
f.getContentPane().add(rp);
f.setVisible(true);
}
static class RoundPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
// Prepare a red rectangle
BufferedImage bi = new BufferedImage(400, 300, BufferedImage.TYPE_INT_ARGB);
Graphics2D gb = bi.createGraphics();
gb.setPaint(Color.RED);
gb.fillRect(0, 0, 400, 300);
gb.dispose();
// Set a rounded clipping region:
RoundRectangle2D r = new RoundRectangle2D.Float(0, 0, 400, 300, 20, 20);
g.setClip(r);
// Draw the rectangle (and see whether it has round corners)
g.drawImage(bi, 0, 0, null);
}
}
Beware of the restrictions mentioned in the API doc for Graphics.setClip:
Sets the current clipping area to an arbitrary clip shape. Not all objects that implement the Shape interface can be used to set the clip. The only Shape objects that are guaranteed to be supported are Shape objects that are obtained via the getClip method and via Rectangle objects.
Just out of intellectual interest can you make a Canvas not flicker when doing a manual resize.
public class FlickerAWT extends Canvas {
public static void main(String[] args) {
Frame f = new Frame(str);
//this line change nothing
//JFrame f = new JFrame(str);
f.add(new FlickerAWT());
f.pack();
int frameWidth = f.getWidth();
int frameHeight = f.getHeight();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
f.setLocation(screenSize.width / 2 - frameWidth / 2, screenSize.height / 2 - frameHeight / 2);
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
public void windowDeiconified(WindowEvent e) {
}
public void windowIconified(WindowEvent e) {
}
});
}
private Color bgColor; private Color contentColor;
Font f = new Font("Georgia", Font.BOLD, 16);
static String str = "AWT Canvas Resize Flickering";
public FlickerAWT() {
Random r = new Random();
bgColor = new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256));
contentColor = new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256));
}
public Dimension getPreferredSize() {
FontMetrics fm = getFontMetrics(f);
return new Dimension(fm.stringWidth(str) + 20, fm.getHeight() + 10);
}
public void paint(java.awt.Graphics g) {
g.setColor(bgColor);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(contentColor);
g.setFont(f);
FontMetrics fm = g.getFontMetrics(f);
int dx = getWidth() / 2 - (fm.stringWidth(str) / 2);
int dy = getHeight() / 2 + (fm.getHeight() / 2);
g.drawString(str, dx, dy);
}
}
You can copy paste in a Java editor and run the example.
You can add this to the beginning of your main method to avoid the background flicker:
System.setProperty("sun.awt.noerasebackground", "true");
I know this question is ancient, but it came up during my search and I meanwhile found a solution:
There are two problems:
On the one hand, the update(...) method of java.awt.Container looks like the following:
public void update(Graphics g) {
if (isShowing()) {
if (! (peer instanceof LightweightPeer)) {
g.clearRect(0, 0, width, height);
}
paint(g);
}
}
I.e. it calls g.clearRect(...) to erase the current content before painting its children.
Therefore, you need to override update(...) in every descendant of java.awt.Container in your view-stack, that doesn't already do so, with something like:
public void update(Graphics g) {
if (isShowing()) paint(g);
}
Also, it seems that AWT or the JVM or whoever (haven't figured this out yet) also clears the background of the main window, independent of any Container's update-methods. To prevent this, follow #WhiteFang34's suggestion and add the following line to your code somewhere:
System.setProperty("sun.awt.noerasebackground", "true");
Only doing both of these things finally solved my flicker issues...
The key I believe is to use double buffering, and one way to possibly solve this is to use Swing which double buffers by default:
import java.awt.*;
import java.util.Random;
import javax.swing.*;
public class FlickerSwing extends JPanel {
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
private static void createAndShowGui() {
JFrame f = new JFrame(str);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new FlickerSwing());
f.setLocationRelativeTo(null);
f.pack();
f.setVisible(true);
}
private Color bgColor;
private Color contentColor;
Font f = new Font("Georgia", Font.BOLD, 16);
static String str = "Swing Resize Flickering";
public FlickerSwing() {
Random r = new Random();
bgColor = new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256));
contentColor = new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256));
setBackground(bgColor);
}
public Dimension getPreferredSize() {
FontMetrics fm = getFontMetrics(f);
return new Dimension(fm.stringWidth(str) + 20, fm.getHeight() + 10);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(contentColor);
g.setFont(f);
FontMetrics fm = g.getFontMetrics(f);
int dx = getWidth() / 2 - (fm.stringWidth(str) / 2);
int dy = getHeight() / 2 + (fm.getHeight() / 2);
g.drawString(str, dx, dy);
}
}