I have an image that is to be drawn on a JFrame. The dimensions of the image are dependent on the dimensions of the JFrame.
The JFrame is drawn significantly more often then the JFrame is actually re-sized. Thus I had the image re-sized then stored in the component re-size event and only drew the re-sized image in the draw method.
//called on componentResized
private void scaleImage(){
if((this.getHeight() * this.getWidth()) != 0)
scalledBackGroundImage = backGroundImage.getScaledInstance(this.getWidth(), this.getHeight(), Image.SCALE_FAST);
else
scalledBackGroundImage = null;
}
#Override
public void paint(Graphics g){
if(scalledBackGroundImage != null)
g.drawImage(scalledBackGroundImage, 0, 0, this);
super.paint(g);
}
However I would seem that the re-size event is called after paint when a component is redrawn. Thus the image displayed is the image for the previous frame size. This really becomes a problem with actions like maximize or minimize.
I am looking for a way to detect a JFrame re-size before paint is called.
(I know I could call repaint() on re-size but it seem a bit rude to ask for the component to be drawn twice when re-sizing)
Thanks for any help.
See The Perils of Image.getScaledInstance(). It really is not a very good choice.
Not only do you have the problem you described above, but I get a lot of flickering:
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.*;
import javax.swing.text.*;
import java.io.*;
public class ResizeSSCCE extends JPanel
{
Image original;
Image scaled;
public ResizeSSCCE()
{
original = new ImageIcon( "mong.jpg" ).getImage();
scaled = original;
scaleImage();
ComponentListener cl = new ComponentAdapter()
{
#Override
public void componentResized(ComponentEvent e)
{
scaleImage();
}
};
addComponentListener(cl);
}
#Override
public void paintComponent(Graphics g)
{
if (scaled != null)
g.drawImage(scaled, 0, 0, this);
// g.drawImage(original, 0, 0, getWidth(), getHeight(), this);
}
private void scaleImage()
{
if (getHeight() * getWidth() != 0)
scaled = original.getScaledInstance(getWidth(), getHeight(), Image.SCALE_FAST);
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("ResizeSSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new ResizeSSCCE() );
frame.setSize(200, 200);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Change the code to use your image and then run using the scaled version to see the flickering. Then change the code to use the original image that is scale of the fly to see the difference. As the article suggests scaling on the fly is the better approach.
Add a component listener to JFrame as
jFrame.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
//Your resize method here
}
});
Related
I need help to add an image to the separate JFrame, but I can't seem to get it to work. Like I want the image to open in a separate frame like when I run this code it opens a blank JFrame. d help to add an image to the separate JFrame, but I can't seem to get it to work. Like I want the image to open in a separate frame like when I run this code it opens a blank JFrame.
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
import javax.swing.JFrame;
class PPJJ extends JFrame implements ActionListener, KeyListener
{
public void paint(Graphics g) {
Toolkit t=Toolkit.getDefaultToolkit();
Image i=t.getImage("tenor.gif");
g.drawImage(i, 120,100,this);
}
public static void main(String[] args)
{
JFrame frame = new JFrame("VOLUNTEER FOR THING");
PPJJ obj = new PPJJ();
JPanel panel = new JPanel();
JLabel lname = new JLabel("Enter your name here");
JTextField tname = new JTextField(21);
JButton btn = new JButton("Click");
btn.addActionListener(obj);
tname.addKeyListener(obj);
panel.add(lname);
panel.add(tname);
panel.add(btn);
frame.add(panel);
frame.setSize(300, 130);
frame.show();
frame.setLocationRelativeTo(null);
PPJJ m = new PPJJ();
JFrame f =new JFrame();
//f.add(m);
f.setSize(500,500);
f.setVisible(true);
frame.add(new JLabel(new ImageIcon("volunteer.jpeg")));
}
public void actionPerformed(ActionEvent e)
{
String s = e.getActionCommand();
if(s.equals("Click here")){
JOptionPane.showMessageDialog(null , "THANKS FOR SIGNING UP");
}
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode()==KeyEvent.VK_ENTER){
JOptionPane.showMessageDialog(null , "THANKS FOR SIGNING UP");
}
}
#Override
public void keyReleased(KeyEvent arg) {}
#Override
public void keyTyped(KeyEvent arg) {}
}
Oh, animated GIFs 😓.
Image handling isn't simple in most cases, but animated GIFs are whole other level of pain ... I mean fun.
Normally, I prefer to use ImageIO.read, but ImageIO returns a BufferedImage and it's not (easily) possible to then render animated GIFs through it.
The "easy" route of displaying animated GIFs is by using Image or ImageIcon.
The first step is get your image "embedded" within your application context (assuming that you're not allowing the user to select the image). How this is done will depend on your IDE and build system (Eclipse and Netbeans allow you to simply include them in the src folder, when you're not using Maven).
Next, you want to use Class#getResource to obtain a URL reference to the embedded resource. In this case, you can use ImageIO.read, ImageIcon(URL) or Toolkit#getImage(URL) to load the image. But, as I've said, ImageIO.read isn't going to help you.
Next, you need a component which can render the image, lucky for us, Swing can do this pretty much auto magically for use, all we need to do is make sure the component is passed as the ImageObserver reference, for example...
public class BackgroundPane extends JPanel {
private Image image;
public BackgroundPane(Image image) {
this.image = image;
}
#Override
public Dimension getPreferredSize() {
Image image = getBackgroundImage();
return image == null ? super.getPreferredSize() : new Dimension(image.getWidth(this), image.getHeight(this));
}
public Image getBackgroundImage() {
return image;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Image image = getBackgroundImage();
if (image == null) {
return;
}
int x = (getWidth() - image.getWidth(this)) / 2;
int y = (getHeight() - image.getHeight(this)) / 2;
g.drawImage(image, x, y, this);
}
}
Also, note, JLabel supports animated GIFs via it's icon property as well, but look at How to set a background picture in JPanel for reasons why you shouldn't use a JLabel as a background container.
Now, all we need to do is load the image, pass it to the background, add what ever content we need to the component and show it, easy, or at least it should be. ImageIcon and Toolkit#getImage both off load the reading of the image to a background thread, so inspecting the images dimensions before the image is loaded will return 0x0 😓, so, we need to wait for it to load (this is why I prefer ImageIO.read as it won't return until the image is loaded or an error occurs).
Something like...
Image image = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/images/kitty.gif"));
BackgroundPane backgroundPane = new BackgroundPane(image);
// Did I mention I had this workflow, but ImageIO doesn't
// support animated images, without a lot of work
MediaTracker mt = new MediaTracker(backgroundPane);
mt.addImage(image, 0);
mt.waitForAll();
// The image is now loaded, hooray for us
Runnable example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
Image image = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/images/kitty.gif"));
BackgroundPane backgroundPane = new BackgroundPane(image);
// Did I mention I had this workflow, but ImageIO doesn't
// support animated images, without a lot of work
MediaTracker mt = new MediaTracker(backgroundPane);
mt.addImage(image, 0);
mt.waitForAll();
backgroundPane.setLayout(new GridBagLayout());
JLabel label = new JLabel("All your kitty is belong to us");
label.setForeground(Color.WHITE);
backgroundPane.add(label);
JFrame frame = new JFrame();
frame.add(backgroundPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (InterruptedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class BackgroundPane extends JPanel {
private Image image;
public BackgroundPane(Image image) {
this.image = image;
}
#Override
public Dimension getPreferredSize() {
Image image = getBackgroundImage();
return image == null ? super.getPreferredSize() : new Dimension(image.getWidth(this), image.getHeight(this));
}
public Image getBackgroundImage() {
return image;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Image image = getBackgroundImage();
if (image == null) {
return;
}
int x = (getWidth() - image.getWidth(this)) / 2;
int y = (getHeight() - image.getHeight(this)) / 2;
g.drawImage(image, x, y, this);
}
}
}
Please note...
If you're not using an animated GIF, then you can just use ImageIO.read instead of Toolkit#getImage and you won't need to wait (as ImageIO.read works in the current thread), in which case the code would look more like...
try {
BackgroundPane backgroundPane = new BackgroundPane(ImageIO.read(getClass().getResource("/images/kitty.gif")));
backgroundPane.setLayout(new GridBagLayout());
JLabel label = new JLabel("All your kitty is belong to us");
label.setForeground(Color.WHITE);
backgroundPane.add(label);
JFrame frame = new JFrame();
frame.add(backgroundPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
(the BackgroundPane code doesn't change)
This is a really general question, but how should I add an animated background for a JPanel. I want the background to be behind all the panel's components and graphics. Right now, I have two separate classes (one for the main panel and the other for the background). The background class uses repaint() to animate a grid moving across the screen. I've tried to make the main panel background transparent, but that hasn't gotten me anywhere.
Even more info:
My main panel is part of a CardLayout and it has many different classes in it. So when I'm adding my main panel to my main frame, I'm doing frame.getContentPane().add(cards, BorderLayout.CENTER)
cards is a JPanel which acts as a container for the mainpanel and all the panels inside main panel.
Can anybody help me out in getting a panel animated background?
You can use Toolkit.getImage() to load animated image and then draw the image in container's paintComponent. Make sure the ImageObserver is set (not null) in order to update animation frames properly. For details how image is loaded, observed and updated see How Images are Loaded appendix in Java AWT Reference.
Here is a simple example:
import java.awt.*;
import javax.swing.*;
import java.net.URL;
class AnimatedPanelDemo {
static class ImagePanel extends JPanel {
private Image image;
ImagePanel(Image image) {
this.image = image;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image,0,0,getWidth(),getHeight(),this);
}
}
private static void createAndShowUI() {
try {
JFrame frame = new JFrame("Image");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
Image image = Toolkit.getDefaultToolkit().getImage(new URL(
"http://duke.kenai.com/iconSized/duke.running.gif"));
ImagePanel imagePanel = new ImagePanel(image);
imagePanel.add(new JLabel("Some label"));
frame.add(imagePanel);
frame.setSize(100, 100);
frame.setVisible(true);
}
catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
Well, this is my first answer on stackoverflow.
Will try to help with my learning curve with this complex AWT and Swift API.
Below there's the contructor that extends JFrame
package xpto;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.event.WindowStateListener;
import java.awt.image.ImageObserver;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import sun.java2d.SunGraphicsEnvironment;
import java.awt.Graphics;
import java.awt.Graphics2D;
public class FrameLuckyRaffle extends JFrame {
/**
*
*/
private JLabel backgroundLabel;
private ImageIcon imageIcon;
private Image bgImage;
/**
* Constructor of this frame.
*/
public FrameLuckyRaffle(String background, final String dbname) {
try {
setTitle("Lucky Raffle of "+ dbname);
GraphicsConfiguration config = this.getGraphicsConfiguration();
Rectangle usableBounds = SunGraphicsEnvironment.
getUsableBounds(config.getDevice());
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setBounds(100, 100, (int)(usableBounds.getWidth()*0.8),
(int)(usableBounds.getHeight()*0.8));
setMinimumSize(new Dimension(1024, 700));
setResizable(true);
setDefaultLookAndFeelDecorated(true);
backgroundLabel = new JLabel() {
public void paintComponent(Graphics g) {
// alternative --> g.drawImage(bgImage, 0, 0, null);
// I prefer to control the new ImageObserver parameter as bellow
g.drawImage(bgImage, 0, 0, new ImageObserver() {
#Override
public boolean imageUpdate(Image img, int infoflags,
int x, int y, int width, int height) {
img.getScaledInstance(getWidth(),getHeight(),
Image.SCALE_FAST);
return true;
}
});
// this is used to have easier control on
// image manipulation on my application
Graphics2D g2d = (Graphics2D)g;
super.paintComponent(g2d);
revalidate();
repaint();
}
};
backgroundLabel.setBounds(0, 0, 0, 0);
// this is necessary if you want more child
// components to be visible on the JFrame afterwards
backgroundLabel.setOpaque(false);
setContentPane(backgroundLabel);
addWindowListener(new WindowListener() {
#Override
public void windowOpened(WindowEvent e) {
// Set Frame Background
imageIcon = new ImageIcon(Toolkit.getDefaultToolkit().
createImage(FrameBusinessPure.class.getResource(background)));
bgImage = imageIcon.getImage().
getScaledInstance(getWidth(),getHeight(), Image.SCALE_FAST);
}
// Even after closing the window, JVM didn't Garbage Collected the instanced
// objects, for some reason. Forcing the objects to null helped on that.
#Override
public void windowClosed(WindowEvent e) {
backgroundLabel = null;
imageIcon = null;
bgImage = null;
System.gc();
}
});
addWindowStateListener(new WindowStateListener() {
#Override
public void windowStateChanged(WindowEvent e) {
// if you flush the object on runtime you will surpass the
// memory leak on using GIFs and most complex graphics
bgImage.flush();
bgImage = imageIcon.getImage().
getScaledInstance(getWidth(),getHeight(), Image.SCALE_FAST);
}
});
addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e) {
// if you flush the object on runtime you will surpass the
// memory leak on using GIFs and most complex graphics
bgImage.flush();
bgImage = imageIcon.getImage().
getScaledInstance(getWidth(),getHeight(), Image.SCALE_FAST);
});
}catch (Exception e) {
e.printStackTrace();
}
}
}
Feel free to learn more on below link
https://www.oracle.com/java/technologies/painting.html
import javax.swing.JFrame;
import javax.swing.JTextField;
import com.sun.awt.AWTUtilities;
#SuppressWarnings("serial")
public class TranslucentIssueTest extends JFrame
{
public static void main(String[] args)
{
(new TranslucentIssueTest()).setVisible(true);
}
public TranslucentIssueTest()
{
super();
setUndecorated(true);
setLayout(null);
setSize(300, 300);
AWTUtilities.setWindowOpaque(this, false);
JTextField box = new JTextField();
box.setBounds(30, 150, 100, 25);
add(box);
}
}
The code above creates a textfield on transparent frame.
But when I typed some Chinese characters into the box using an input method, transparent effect was removed automatically. Is there anything wrong with my code?
Thanks in advance.
I ran into what appears to be the same issue and thought I'd post my solution for anyone who finds this question while searching. The problem seems to be due to a bug in the DirectDraw pipeline in Swing. I'm using an older version of Java 7 (update 5), so it's possible that this has been fixed in one of the later releases.
The problem doesn't seem to be with the IME specifically, but rather with the rendering calls that get made by the text field while the IME is active.
The simplest way to work around the problem is to just disable DirectDraw rendering by making the following call before any GUI code is run:
System.setProperty("sun.java2d.noddraw", "true");
If you'd rather not disable DirectDraw rendering entirely, you can work around the specific JTextField issue by overriding its paintComponent method to write to a buffer as in the example below.
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
public class Test {
public static class JTextField2 extends JTextField {
private static final long serialVersionUID = 1L;
private BufferedImage buffer = null;
#Override public void paintComponent(Graphics g) {
Component window = this.getTopLevelAncestor();
if (window instanceof Window && !((Window)window).isOpaque()) {
// This is a translucent window, so we need to draw to a buffer
// first to work around a bug in the DirectDraw rendering in Swing.
int w = this.getWidth();
int h = this.getHeight();
if (buffer == null || buffer.getWidth() != w || buffer.getHeight() != h) {
// Create a new buffer based on the current size.
GraphicsConfiguration gc = this.getGraphicsConfiguration();
buffer = gc.createCompatibleImage(w, h, BufferedImage.TRANSLUCENT);
}
// Use the super class's paintComponent implementation to draw to
// the buffer, then write that buffer to the original Graphics object.
Graphics bufferGraphics = buffer.createGraphics();
try {
super.paintComponent(bufferGraphics);
} finally {
bufferGraphics.dispose();
}
g.drawImage(buffer, 0, 0, w, h, 0, 0, w, h, null);
} else {
// This is not a translucent window, so we can call the super class
// implementation directly.
super.paintComponent(g);
}
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
final JFrame frame = new JFrame();
frame.setUndecorated(true);
frame.setBackground(new Color(96, 128, 160, 192));
JTextField textField = new JTextField2();
JButton exitButton = new JButton("Exit");
exitButton.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
frame.dispose();
}
});
frame.add(exitButton, BorderLayout.PAGE_START);
frame.add(textField, BorderLayout.PAGE_END);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
this is my first post here and I have a question that seems really nooby, but this has been troubling me for the past hour or so.
I'm making a simple JFrame with a JPanel in it, but the Windows 7 border frame appears to be blocking my view of parts of the panel. For instance, if I draw a little square at coordinate 0,0, it will not appear and I suspect it's behind the window frame.
I've tried messing around with pack, setsize, setpreferred size, setresizable, and different layouts, but I can't get it to show the top 20 pixels or so!
This is what I have:
public RedSunGame() {
super("Red Sun");
rs = new JPanel(new BorderLayout(), true);
rs.setPreferredSize(new Dimension(WIDTH, HEIGHT));
add(rs, "Center");
setPreferredSize(new Dimension(WIDTH, HEIGHT));
pack();
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
EDIT:
Thanks for all of your replies, sorry for the lack of info :)
I'm using a double buffer strategy I saw in a book. gameRender and paintScreen are in a standard game loop. My RedSunGame class extends JFrame. All the relevant code you could ask for in addition to above:
private static final int WIDTH = 500;
private static final int HEIGHT = 500;
private JPanel rs;
private Graphics2D g2d;
private Image dbImage;
private void gameRender() {
//create buffer
if (dbImage == null){
dbImage = createImage(WIDTH, HEIGHT);
g2d = (Graphics2D)dbImage.getGraphics();
}
//clear screen
g2d.setColor(Color.white);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
g2d.setColor(Color.blue);
g2d.setFont(font);
g2d.drawString("FPS: " + FPS, 0, HEIGHT);
g2d.fillRect(30, 30, 10, 10);
}
private void paintScreen() {
Graphics g;
try {
g = getGraphics();
if ((g != null) && (dbImage != null))
g.drawImage(dbImage, 0, 0, null);
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
catch (Exception e)
{ System.out.println("Graphics context error: " + e); }
}
With my current settings it looks like this.
http://i.imgur.com/qaabC.png
This is what happens if I have g2d.fillRect(30, 30, 10, 10), the only change being the coordinates 30,30 instead of 0,0. It's definitely hiding behind the border up top.
http://i.imgur.com/uzfFe.png
Also, setting it to BorderLayout.CENTER doesn't seem to make a difference in any of these cases.
(sorry it won't let new users post images)
EDIT:
I figured it out. I was drawing directly to the JFrame. #Guillaume Polet I see why you shouldn't override the paint method of JFrames as it draws to the frame and not the panel that should actually display content!! Thanks
Here is a sample code that shows how your goal can be achieved. Try to spot the differences with your code to find what is wrong:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class RedSunGame {
private static final int SQUARE_SIZE = 20;
private JPanel rs;
private JFrame frame;
private void initUI() {
frame = new JFrame("Red Sun");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
rs = new JPanel(new BorderLayout()) {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.YELLOW);
g.fillRect(0, 0, SQUARE_SIZE, SQUARE_SIZE);
}
#Override
public Dimension getPreferredSize() {
Dimension preferredSize = super.getPreferredSize();
// Let's make sure that we have at least our little square size.
preferredSize.width = Math.max(preferredSize.width, SQUARE_SIZE);
preferredSize.height = Math.max(preferredSize.height, SQUARE_SIZE);
return preferredSize;
}
};
frame.add(rs);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
RedSunGame redSunGame = new RedSunGame();
redSunGame.initUI();
}
});
}
}
Verify that WIDTH and HEIGHT are > 0.
Try this:
//add(rs, "center");
add(rs, BorderLayout.CENTER);
you may got your answer but for a newbie to java swing i suggest that you should the Net-beans IDE. it graphically adds and lays out the GUI and you dont need to write any hand-written code. It's a great place to start, as stated here:
http://docs.oracle.com/javase/tutorial/uiswing/learn/index.html
What are the principle differences between using an AWT Frame and a Swing JFrame when implementing your own rendering and not using standard Java GUI components?
This is a follow on from a previous question:
AWT custom rendering - capture smooth resizes and eliminate resize flicker
The typical talking points on Swing vs AWT don't seem to apply because we're only using frames. Heavyweight vs Lightweight goes out the window (and JFrame extends Frame), for example.
So which is best, JFrame or Frame for this situation? Does it make any meaningful difference?
Note: this scenario is one where rendering in the EDT is not desirable. There is an application workflow which is not linked to the EDT and rendering is done on an as-needs basis outside of the EDT. To synchronize rendering with the EDT would add latency to the rendering. We are not rendering any Swing or AWT components other than the Frame or JFrame (or an enclosed JPanel/Component/etc if it is best).
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.image.BufferStrategy;
import java.awt.Frame;
public class SmoothResize extends Frame {
public static void main(String[] args) {
Toolkit.getDefaultToolkit().setDynamicLayout(true);
System.setProperty("sun.awt.noerasebackground", "true");
SmoothResize srtest = new SmoothResize();
//srtest.setIgnoreRepaint(true);
srtest.setSize(100, 100);
srtest.setVisible(true);
}
public SmoothResize() {
render();
}
private Dimension old_size = new Dimension(0, 0);
private Dimension new_size = new Dimension(0, 0);
public void validate() {
super.validate();
new_size.width = getWidth();
new_size.height = getHeight();
if (old_size.equals(new_size)) {
return;
} else {
render();
}
}
public void paint(Graphics g) {
validate();
}
public void update(Graphics g) {
paint(g);
}
public void addNotify() {
super.addNotify();
createBufferStrategy(2);
}
protected synchronized void render() {
BufferStrategy strategy = getBufferStrategy();
if (strategy == null) {
return;
}
// Render single frame
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
Graphics draw = strategy.getDrawGraphics();
Insets i = getInsets();
int w = (int)(((double)(getWidth() - i.left - i.right))/2+0.5);
int h = (int)(((double)(getHeight() - i.top - i.bottom))/2+0.5);
draw.setColor(Color.YELLOW);
draw.fillRect(i.left, i.top + h, w,h);
draw.fillRect(i.left + w, i.top, w,h);
draw.setColor(Color.BLACK);
draw.fillRect(i.left, i.top, w, h);
draw.fillRect(i.left + w, i.top + h, w,h);
draw.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
}
Expanding on #camickr's answer, the "missing detail" is JRootPane, which manages the contentPane. Note that for JFrame "add and its variants, remove and setLayout have been overridden to forward to the contentPane as necessary." JRootPane#createContentPane() "creates a new JComponent a[n]d sets a BorderLayout as its LayoutManager." As an implementation detail, that JComponent happens to be a new JPanel(). This has several consequences for the contentPane of JFrame:
The contentPane is double buffered by default.
The contentPane has a BorderLayout, although JPanel ordinarily defaults to FlowLayout.
The contentPane has a L&F specific UI delegate, typically derived from PanelUI, that may affect appearance and geometry.
Swing is double buffered by default so generally you only need to concentrate on your painting.
Here is the Swing version:
import java.awt.*;
import javax.swing.*;
public class SwingResize extends JPanel
{
protected void paintComponent(Graphics g)
{
int w = (int)(((double)(getWidth()))/2+0.5);
int h = (int)(((double)(getHeight()))/2+0.5);
g.setColor(Color.YELLOW);
g.fillRect(0, h, w,h);
g.fillRect(w, 0, w,h);
g.setColor(Color.BLACK);
g.fillRect(0, 0, w, h);
g.fillRect(w, h, w,h);
}
public static void main(String[] args)
{
// Toolkit.getDefaultToolkit().setDynamicLayout(true);
// System.setProperty("sun.awt.noerasebackground", "true");
JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.add( new SwingResize() );
frame.setSize(100, 100);
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}