Has anyone ever run into this issue before? Sometimes the string displays, sometimes half of it, sometimes none of it. The issue is worsened when using VolatileImage instead of BufferedImage as a backbuffer.
public class Game3D {
public static void main(String[] args) {
Game3D game = new Game3D();
game.start();
}
public Game3D() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
canv = new GameCanvas();
canv.setPreferredSize(new Dimension(800, 600));
frame.add(canv);
frame.pack();
frame.setVisible(true);
}
private JFrame frame;
private GameCanvas canv;
public void start() {
canv.createBuffer(canv.getPreferredSize());
loadingScreen("Loading", 10);
}
public void loadingScreen(String msg, int done) {
Graphics2D g = canv.img.createGraphics();
try {
g.setColor(Color.BLACK);
g.fillRect(0, 0, canv.getWidth(), canv.getHeight());
int sizeX = 400, sizeY = 50;
int loadX = canv.getWidth() / 2 - sizeX / 2;
int loadY = canv.getHeight() / 2 - sizeY / 2;
g.setColor(Color.RED);
g.drawRect(loadX, loadY, sizeX, sizeY);
g.fillRect(loadX + 2, loadY + 2, (int) (sizeX / 100F * done), sizeY - 3);
int textX = canv.getWidth() / 2 - g.getFontMetrics().stringWidth(msg) / 2;
int textY = canv.getHeight() / 2 - g.getFontMetrics().getHeight() / 2;
g.setColor(Color.WHITE);
g.setFont(canv.font);
g.drawString(msg, textX, textY);
} finally {
g.dispose();
}
}
}
class GameCanvas extends Canvas {
GameCanvas() {
}
BufferedImage img;
Font font;
void createBuffer(Dimension dim) {
img = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB);
font = new Font(Font.MONOSPACED, Font.PLAIN, 16);
}
#Override
public void paint(Graphics g) {
g.drawImage(img, 0, 0, null);
}
}
I think you may need to call frame.repaint(); in order to have what you draw show up properly though I'm not entirely certain. I just remember this being the cause of a few of my own problems when using Java GUIs.
The code needed to be ran in the SWING thread!
Related
I am creating a music player program.
I have created the seek bar using JSlider
Code:
JSlider seek = new JSlider(JProgressBar.HORIZONTAL);
seek.setOpaque(true);
seek.setMajorTickSpacing(0);
seek.setMinorTickSpacing(0);
seek.setBackground(Color.DARK_GRAY);
seek.setSize(100, 13);
seek.setLocation(6, 30);
Currently, it looks like this :
I can only change the background of JSlider using setBackground() method.
I don't have any idea about how to change the thumb colour, thumb shape, track colour, etc.
I want my seek bar to look something like this :
How can I achieve this?
If not possible with JSlider, is it possible to create a JProgressBar which has a slidable thumb?
As was already mentioned in the comments there is no way for you to change the appearance of the slider without extending an existing implementation of SliderUI. Here is an example implementation of how one could achieve the visuals from your demo picture.
Note that hard coding the sizes and colours isn't the best approach and for a real implementation should be handled by setting and using values available by the UIManager.
class Scratch {
public static void main(final String[] args) {
SwingUtilities.invokeLater(() -> {
JPanel content = new JPanel(new BorderLayout());
content.setPreferredSize(new Dimension(300, 100));
JSlider slider = new JSlider() {
#Override
public void updateUI() {
setUI(new CustomSliderUI(this));
}
};
content.add(slider);
JFrame frame = new JFrame();
frame.setContentPane(content);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
private static class CustomSliderUI extends BasicSliderUI {
private static final int TRACK_HEIGHT = 8;
private static final int TRACK_WIDTH = 8;
private static final int TRACK_ARC = 5;
private static final Dimension THUMB_SIZE = new Dimension(20, 20);
private final RoundRectangle2D.Float trackShape = new RoundRectangle2D.Float();
public CustomSliderUI(final JSlider b) {
super(b);
}
#Override
protected void calculateTrackRect() {
super.calculateTrackRect();
if (isHorizontal()) {
trackRect.y = trackRect.y + (trackRect.height - TRACK_HEIGHT) / 2;
trackRect.height = TRACK_HEIGHT;
} else {
trackRect.x = trackRect.x + (trackRect.width - TRACK_WIDTH) / 2;
trackRect.width = TRACK_WIDTH;
}
trackShape.setRoundRect(trackRect.x, trackRect.y, trackRect.width, trackRect.height, TRACK_ARC, TRACK_ARC);
}
#Override
protected void calculateThumbLocation() {
super.calculateThumbLocation();
if (isHorizontal()) {
thumbRect.y = trackRect.y + (trackRect.height - thumbRect.height) / 2;
} else {
thumbRect.x = trackRect.x + (trackRect.width - thumbRect.width) / 2;
}
}
#Override
protected Dimension getThumbSize() {
return THUMB_SIZE;
}
private boolean isHorizontal() {
return slider.getOrientation() == JSlider.HORIZONTAL;
}
#Override
public void paint(final Graphics g, final JComponent c) {
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
super.paint(g, c);
}
#Override
public void paintTrack(final Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Shape clip = g2.getClip();
boolean horizontal = isHorizontal();
boolean inverted = slider.getInverted();
// Paint shadow.
g2.setColor(new Color(170, 170 ,170));
g2.fill(trackShape);
// Paint track background.
g2.setColor(new Color(200, 200 ,200));
g2.setClip(trackShape);
trackShape.y += 1;
g2.fill(trackShape);
trackShape.y = trackRect.y;
g2.setClip(clip);
// Paint selected track.
if (horizontal) {
boolean ltr = slider.getComponentOrientation().isLeftToRight();
if (ltr) inverted = !inverted;
int thumbPos = thumbRect.x + thumbRect.width / 2;
if (inverted) {
g2.clipRect(0, 0, thumbPos, slider.getHeight());
} else {
g2.clipRect(thumbPos, 0, slider.getWidth() - thumbPos, slider.getHeight());
}
} else {
int thumbPos = thumbRect.y + thumbRect.height / 2;
if (inverted) {
g2.clipRect(0, 0, slider.getHeight(), thumbPos);
} else {
g2.clipRect(0, thumbPos, slider.getWidth(), slider.getHeight() - thumbPos);
}
}
g2.setColor(Color.ORANGE);
g2.fill(trackShape);
g2.setClip(clip);
}
#Override
public void paintThumb(final Graphics g) {
g.setColor(new Color(246, 146, 36));
g.fillOval(thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height);
}
#Override
public void paintFocus(final Graphics g) {}
}
}
Result:
I'm trying to create a simple JFrame window in OS X and render a simple black square on it like so using Graphics2d:
public Start() {
running = true;
window = new JFrame("Finest Hour");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setPreferredSize(new Dimension(500, 500));
window.setSize(window.getPreferredSize());
FullScreenUtilities.setWindowCanFullScreen(window, true);
window.setIgnoreRepaint(true);
window.setVisible(true);
window.createBufferStrategy(2);
strategy = window.getBufferStrategy();
}
public static void main(String[] args) {
Start s = new Start();
s.loop();
}
public void loop() {
Random random = new Random();
while(running) {
Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
g.setColor(Color.black);
g.fillRect(20, 20, 40, 40);
strategy.show();
}
}
However the window doesyn't seem to have rounded corners at the bottom when I use strategy.show(); :
Rendering it without the buffer strategy, i.e: Graphics2D = (Graphics2D) window.getGraphics(); produces a window with rounded corners:
I know it's a really small problem but it's annoying none the less. Any way to fix this?
Works fine for me.
public class TestMacFrame extends JFrame {
public TestMacFrame() throws HeadlessException {
super("Testing");
setSize(200, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
add(new PaintPane());
setVisible(true);
}
protected class PaintPane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int width = getWidth() - 1;
int height = getHeight() - 1;
g.setColor(Color.BLACK);
int blockWidth = width / 2;
int blockHeight = height / 2;
int x = (width - blockWidth) / 2;
int y = (height - blockHeight) / 2;
g.fillRect(x, y, blockWidth, blockHeight);
}
}
public static void main(String[] args) {
new TestMacFrame();
}
}
Now, I have no idea what it is you're trying to, but I can tell you this:
public void loop() {
Random random = new Random();
while(running) {
Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
g.setColor(Color.black);
g.fillRect(20, 20, 40, 40);
strategy.show();
}
}
Is a bad idea. Firstly as SoboLAN pointed out, you're trying to update a UI component out side the Event Dispatching Thread. Swing components are not Thread safe.
Secondly, this loop will eventually chew up your CPU cycles, making you application unusable as well as you overall system.
UPDATED with Animation
Here, try this. This is a really BASIC example ;)
public class TestMacFrame extends JFrame {
private float angel = 0;
private Timer timer;
public TestMacFrame() throws HeadlessException {
super("Testing");
setSize(200, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
add(new PaintPane());
setVisible(true);
timer = new Timer(25, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
angel += 1;
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.setInitialDelay(0);
timer.start();
}
protected class PaintPane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth() - 1;
int height = getHeight() - 1;
g.setColor(Color.BLACK);
int blockWidth = width / 2;
int blockHeight = height / 2;
int x = (width - blockWidth) / 2;
int y = (height - blockHeight) / 2;
g2d.setTransform(AffineTransform.getRotateInstance(Math.toRadians(angel), width / 2, height / 2));
g2d.fillRect(x, y, blockWidth, blockHeight);
g2d.dispose();
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
new TestMacFrame();
}
}
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.
I was asking question about Translucent JFrame border (see here) and I got very good answers, but unfortunatelly, given answers work perfectly only on JDK 6, but not 7. Any ideas how to make it work with JDK 7?
In JDK 6 it looks like this:
And JDK 7:
And my code looks like this:
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.border.AbstractBorder;
public class ShadowBorder extends AbstractBorder {
private static final int RADIUS = 30;
private static BufferedImage shadowTop;
private static BufferedImage shadowRight;
private static BufferedImage shadowBottom;
private static BufferedImage shadowLeft;
private static BufferedImage shadowTopLeft;
private static BufferedImage shadowTopRight;
private static BufferedImage shadowBottomLeft;
private static BufferedImage shadowBottomRight;
private static boolean shadowsLoaded = false;
public ShadowBorder() {
if (!shadowsLoaded) {
try {
shadowTop = ImageIO.read(getClass().getResource("/cz/vutbr/fit/assets/shadow-top.png"));
shadowRight = ImageIO.read(getClass().getResource("/cz/vutbr/fit/assets/shadow-right.png"));
shadowBottom = ImageIO.read(getClass().getResource("/cz/vutbr/fit/assets/shadow-bottom.png"));
shadowLeft = ImageIO.read(getClass().getResource("/cz/vutbr/fit/assets/shadow-left.png"));
shadowTopLeft = ImageIO.read(getClass().getResource("/cz/vutbr/fit/assets/shadow-top-left.png"));
shadowTopRight = ImageIO.read(getClass().getResource("/cz/vutbr/fit/assets/shadow-top-right.png"));
shadowBottomLeft = ImageIO.read(getClass().getResource("/cz/vutbr/fit/assets/shadow-bottom-left.png"));
shadowBottomRight = ImageIO.read(getClass().getResource("/cz/vutbr/fit/assets/shadow-bottom-right.png"));
shadowsLoaded = true;
} catch (IOException ex) {
Logger.getLogger(ShadowBorder.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
#Override
public boolean isBorderOpaque() {
return false;
}
#Override
public Insets getBorderInsets(Component c) {
return new Insets(RADIUS, RADIUS, RADIUS, RADIUS);
}
#Override
public Insets getBorderInsets(Component c, Insets insets) {
insets.top = RADIUS;
insets.left = RADIUS;
insets.bottom = RADIUS;
insets.right = RADIUS;
return insets;
}
#Override
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, 1f));
int recWidth = width - (2 * RADIUS);
int recHeight = height - (2 * RADIUS);
int recX = width - RADIUS;
int recY = height - RADIUS;
//edges
g2d.drawImage(shadowTop.getScaledInstance(recWidth, RADIUS, Image.SCALE_REPLICATE), RADIUS, 0, null);
g2d.drawImage(shadowRight.getScaledInstance(RADIUS, recHeight, Image.SCALE_REPLICATE), recX, RADIUS, null);
g2d.drawImage(shadowBottom.getScaledInstance(recWidth, RADIUS, Image.SCALE_REPLICATE), RADIUS, recY, null);
g2d.drawImage(shadowLeft.getScaledInstance(RADIUS, recHeight, Image.SCALE_REPLICATE), 0, RADIUS, null);
//corners
g2d.drawImage(shadowTopLeft, 0, 0, null);
g2d.drawImage(shadowTopRight, recX, 0, null);
g2d.drawImage(shadowBottomLeft, 0, recY, null);
g2d.drawImage(shadowBottomRight, recX, recY, null);
}
}
Thanks a lot!
I've just solved my problem. The problem was, that JDK 7 implements AWTUtilities.setWindowOpaque() method from JDK6 in setBackground() method and I was (NetBeans did :-)) setting default background for JFrame in different place, so setting background to new Color(0, 0, 0, 0); makes JFrame transparent and all goes well now.
For whoever stumbles upon this thread and wants his own transparent window, I devised this example. With how little information is available on the web, I almost had to break a leg to come up with something just works, and doesn't use image files or anything. (Combined from different examples on this site)
public class GradientTranslucentWindowDemo
{
public static void main(String[] args)
{
// Create the GUI on the event-dispatching thread
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
final JFrame f = new JFrame("Per-pixel translucent window");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setUndecorated(true);
f.setBackground(new Color(0, 0, 0, 0));
final BufferedImage backrgoundImage = makeBackrgoundImage(400, 400);
JPanel panel = new JPanel()
{
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (g instanceof Graphics2D)
{
g.drawImage(backrgoundImage, 0, 0, null);
}
}
};
panel.setOpaque(false);
f.setContentPane(panel);
f.setLayout(new GridBagLayout()); // Centers the button
f.add(new JButton(new AbstractAction("Close")
{
#Override
public void actionPerformed(ActionEvent e)
{
f.dispose();
}
}));
f.setBounds(100, 100, 400, 400);
f.setVisible(true);
}
});
}
static BufferedImage makeBackrgoundImage(int w, int h)
{
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
// Draw something transparent
Graphics2D g = img.createGraphics();
g.setPaint(new RadialGradientPaint(new Point2D.Float(w / 2, h / 2), (w + h) / 4, new float[]{0, 1}, new Color[]{Color.RED, new Color(1f, 0, 0, 0)}));
g.fillRect(0, 0, w, h);
g.setPaint(Color.RED);
g.drawRect(0, 0, w - 1, h - 1);
g.dispose();
return img;
}
}
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);
}
}