Memory Leak(?) when displaying images in java - java

today I kinda fiddled around with image opening/scaling/displaying in Java and wrote a bit of code to open an Image File, scale it randomly and display it for a short time.
The problem is: After displaying it for like 100-1000 times, the used memory of my "javaw.exe" grows and grows, it even reached 1 GB of memory space.
I dont know where the memory leak in my code is since the only memory eating things are my picures and there are only 2 (the original image and the one who is getting scaled, which is always assigned to the same variable(temp) so the "older" ones should be picked off by the GC), maybe you guys could have a look over it, its pretty simple.
1) You choose an image from your hard drive
2) It gets scaled randomly
3) Its displayed for a short amount of time and then disappears
4) go to 2)
To scale the image I used this library: http://www.thebuzzmedia.com/software/imgscalr-java-image-scaling-library/
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import org.imgscalr.Scalr;
public static void main(String[] args) throws IOException, InterruptedException {
JFileChooser chooser = new JFileChooser();
chooser.showOpenDialog(null);
BufferedImage originalImage = ImageIO.read(chooser.getSelectedFile());
BufferedImage temp;
while(true){
int width = (int) ((Math.random()*1000)+1);
int height = (int) ((Math.random()*1000)+1);
Thread.sleep(1000);
temp = Scalr.resize(originalImage,Scalr.Mode.FIT_EXACT, width, height);
showImage(temp, 800);
}
}
static void showImage(BufferedImage v,long length) throws InterruptedException {
JFrame frame = new JFrame();
frame.add(new JLabel(new ImageIcon(v)));
frame.setSize(v.getWidth(), v.getHeight());
frame.setVisible(true);
Thread.sleep(length);
frame.setVisible(false);
}
This is my first time posting here, so please ask questions if I am unclear
thanks in advance!
EDIT: I monitored the memory javaw.exe is needing
1 picture displayed: 75M
100 pictures displayed: 330M
1000 pictures displayed: 2,4G
EDIT 2:
I now have applied your helpful advice but I still have a growing amount of memory and my Images arent displayed anymore.. The JFrames are empty.
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import org.imgscalr.Scalr;
public class App {
public static void main(String[] args) throws IOException, InterruptedException {
JFileChooser chooser = new JFileChooser();
chooser.showOpenDialog(null);
BufferedImage originalImage = ImageIO.read(chooser.getSelectedFile());
BufferedImage temp;
JFrame frame = new JFrame();
while(true){
int width = (int) ((Math.random()*1000)+1);
int height = (int) ((Math.random()*1000)+1);
Thread.sleep(1000);
temp = Scalr.resize(originalImage,Scalr.Mode.FIT_EXACT, width, height);
showImage(temp, 500, frame);
}
}
static void showImage(BufferedImage v,long length, JFrame frame) throws InterruptedException {
SwingUtilities.invokeLater(
() -> {
frame.removeAll();
frame.revalidate();
frame.repaint();
frame.add(new JLabel(new ImageIcon(v)));
frame.setSize(v.getWidth(), v.getHeight());
frame.setVisible(true);
try {
Thread.sleep(length);
} catch (Exception e) {}
frame.setVisible(false);
frame.dispose();
});
}
}
Maybe I put your advice in the wrong places in my code.

The code below should do what you want. I used a Timer instead of Thread.sleep. You're tying up the EDT. I also just draw the image in the container. You should probably use a JPanel instead (add it to the JFrame and override its paintComponent method). I also cleaned up the methods a little.
import java.awt.image.BufferedImage;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Graphics;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import org.imgscalr.Scalr;
public class App extends JFrame implements ActionListener{
BufferedImage originalImage = null;
BufferedImage temp = null;
JFileChooser chooser = null;
public App(){
setVisible(true);
}
public static void main(String[] args) throws IOException, InterruptedException {
SwingUtilities.invokeLater(
() -> {
App app = new App();
Timer timer = new Timer(1000, app);
timer.start();
});
}
#Override
public void actionPerformed(ActionEvent ae){
if(null == chooser){
chooser = new JFileChooser();
chooser.showOpenDialog(this);
loadImage();
}
showImage();
repaint();
}
#Override
public void paint(Graphics g){
super.paint(g);
if(null == temp){
return;
}
g.drawImage(temp, 0, 0, null);
}
public void loadImage(){
try{
originalImage = ImageIO.read(chooser.getSelectedFile());
} catch(IOException ioe){
ioe.printStackTrace();
}
}
public void showImage() {
int width = (int) ((Math.random()*1000)+1);
int height = (int) ((Math.random()*1000)+1);
temp = Scalr.resize(originalImage,Scalr.Mode.BEST_FIT_BOTH, width, height);
setSize(width, height);
}
}

You might want to try
originalImage.flush();
originalImage = null;
temp.flush();
temp = null;
but there is no guarantee when your image will get garbage collected
Apart from that you should also consider clearing and reusing the same JFrame.
removeAll();//or remove the previous JLabel
revalidate();
repaint();
Also the proper way to display a JFrame is by using the SwingUtilities invokeLater method to make sure this "job" is placed on the Event Dispatch Thread (EDT).
// schedule this for the event dispatch thread (edt)
SwingUtilities.invokeLater(yourJFrame);

Related

How do I draw an image in the shape of a parallelogram?

So, I know that images can be cropped, shrunk, and expanded, but can you adapt an image into the shape of a parallelogram? I'm using Java Swing to draw images. I was thinking maybe some class or some method of BufferedImage might do the trick, but I couldn't find anything. I have also searched Google for a while, but I cannot find an answer. Does anyone know of a way I could do this, or a webpage that explains it? Thanks in advance.
You could use AffineTransform.getShearInstance.
This example uses a AffineTransformOp to "filter" the original image
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SkewImage {
public static void main(String[] args) {
new SkewImage();
}
public SkewImage() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
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() {
setLayout(new GridLayout(1, 2));
try {
BufferedImage original = ImageIO.read(new File("C:\\hold\\thumbnails\\Megatokyo_707___Torn_by_crusaderky.jpg"));
BufferedImage skew = new BufferedImage(original.getWidth(), original.getHeight(), BufferedImage.TYPE_INT_ARGB);
// Adjust the image width if we use a negative skew...
double skewX = 0.3d;
double x = (skewX < 0) ? -skewX * original.getHeight() : 0;
AffineTransform at = AffineTransform.getTranslateInstance(x, 0);
at.shear(skewX, 0);
AffineTransformOp op = new AffineTransformOp(at,
new RenderingHints(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC));
skew = op.filter(original, null);
add(new JLabel(new ImageIcon(original)));
add(new JLabel(new ImageIcon(skew)));
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
Just an example using a Java image processing framework.
Output:
public class SkewExample extends JFrame{
MarvinImagePlugin skew = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.transform.skew");
public SkewExample(){
super("Skew Example");
// Layout
setLayout(new GridLayout(6,1));
// Load Image
MarvinImage image = MarvinImageIO.loadImage("./res/chamaleon.jpg");
skew.setAttribute("skew", "Horizontal");
// Process the image multiple times with different angle.
for(int i=1; i<=6; i++){
add(new JLabel(new ImageIcon(skew(image, i*7).getBufferedImage())));
}
setSize(340,880);
setVisible(true);
}
private MarvinImage skew(MarvinImage imageIn, int angle){
skew.setAttribute("SkewAngle", angle);
MarvinImage ret = new MarvinImage(imageIn.getWidth(),imageIn.getHeight());
ret.fillRect(0, 0,imageIn.getWidth(),imageIn.getHeight(), new Color(238,238,238));
ret.update();
skew.process(imageIn, ret);
ret.update();
return ret;
}
public static void main(String[] args) {
new SkewExample().setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

Layering text over JLabel

This is how I want my app to look like.
Trouble is, if I drag the JLabel with the "Hello, I'm Myra" over another JLabel (whose icon is the speech bubble), rather than superimposing or layering, NetBeans shifts the JLabels to be adjacent.
How do I superimpose ie. place the text JLabel on top of another JLabel?
Do note, I'm using NetBeans. It doesn't allow me to edit much of the JFrame or JLabel code.
Netbeans won't let you add components to a JLabel, it doesn't see them as a valid Container.
This won't be easily achieved using component labels, as the icon placement is outside of your control. A better solution might be to use a custom component, such as a JPanel and manually draw the speech bubble image yourself, then using a combination of Border and LayoutManager it would allow you to add other components to it
This is a very basic example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
public class SpeechBubble {
public static void main(String[] args) {
new SpeechBubble();
}
public SpeechBubble() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
SpeechBubblePane bubble = new SpeechBubblePane();
JLabel hello = new JLabel("Hello, I'm Myra");
hello.setFont(hello.getFont().deriveFont(28f));
hello.setForeground(Color.CYAN);
JLabel message = new JLabel("<html>What would you like to know today?</html>");
message.setFont(message.getFont().deriveFont(22f));
message.setForeground(Color.WHITE);
bubble.setLayout(new GridLayout(2, 1));
bubble.add(hello);
bubble.add(message);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setBackground(Color.BLACK);
frame.add(bubble);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class SpeechBubblePane extends JPanel {
private BufferedImage background;
public SpeechBubblePane() {
setOpaque(false);
try {
background = ImageIO.read(getClass().getResource("/speechbubble.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
setBorder(new EmptyBorder(19, 19, 66, 19));
}
#Override
public Dimension getPreferredSize() {
Dimension size = new Dimension(200, 200);
if (background != null) {
size = new Dimension(background.getWidth(), background.getHeight());
}
return size;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight()- background.getHeight()) / 2;
g2d.drawImage(background, x, y, this);
g2d.dispose();
}
}
}
}
If I was doing this, I would consider developing up a "9-path" which would allow you to break the image down into 9 parts and scale the outer sections based on what the content requires, for example...
It sounds like you just want to add a z-order. If so, you need a LayeredPane:
http://docs.oracle.com/javase/7/docs/api/index.html
http://docs.oracle.com/javase/7/docs/api/javax/swing/JLayeredPane.html

Jbutton acction listener

I am trying to create a form. there is a button that when clicking the button, a photo which is specified would appear. my problem is, when I click the button, the picture pops up and if the cursor passes the form boundary, the image disappears. here is my code:
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import static java.lang.Math.abs;
import static java.lang.Math.min;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
public class SeamCarving extends JFrame
{
public static void main(String[] args) throws IOException {
final BufferedImage input = ImageIO.read(new File("path"));
final BufferedImage[] toPaint = new BufferedImage[]{input};
final Frame frame = new Frame("Seams") {
#Override
public void update(Graphics g) {
final BufferedImage im = toPaint[0];
if (im != null) {
g.clearRect(0, 0, getWidth(), getHeight());
g.drawImage(im, 0, 0, this);
}
}
};
frame.setSize(input.getWidth(), input.getHeight());
frame.setVisible(true);
frame.add(startButton);
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
BufferedImage out = input;
out = deleteVerticalSeam(out);
toPaint[0] = out;
frame.repaint();
System.out.println("Do Something Clicked");
}
});
}
}
Don't override update, this isn't how painting is achieved in Swing. Attempting to paint directly to a top level container like JFrame is problematic at best.
Instead, start with a JPanel and use it's paintComponent method instead. Make sure you call super.paintComponent as well.
In fact, you could probably just use a JLabel to display the image instead.
Take a look at;
Performing Custom Painting
How to use labels
For more details
Updated with example
I still think a JLabel would be simpler solution, but what do I know.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SeamCarving {
public static void main(String[] args) {
new SeamCarving();
}
public SeamCarving() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
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 BufferedImage input;
private BufferedImage[] toPaint;
public TestPane() {
try {
input = ImageIO.read(new File("C:\\hold\\thumbnails\\2005-09-29-3957.jpeg"));
toPaint = new BufferedImage[1];
} catch (IOException ex) {
ex.printStackTrace();
}
setLayout(new GridBagLayout());
JButton startButton = new JButton("Start");
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
BufferedImage out = input;
out = input; //deleteVerticalSeam(out);
toPaint[0] = out;
repaint();
System.out.println("Do Something Clicked");
}
});
add(startButton);
}
#Override
public Dimension getPreferredSize() {
return input == null ? new Dimension(400, 400) : new Dimension(input.getWidth(), input.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (toPaint[0] != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(input, 0, 0, this);
g2d.dispose();
}
}
}
}
The problem with overriding update is the paint subsystem can choose to avoid calling and end up calling paint directly, circumventing your painting.
Painting also involves painting child components (like your button) and borders, which you've conveniently discarded by not calling super.update.

JLabel text gets overwritten on transparent bg

Im a newbie to java, Im trying to create an application like a desktop widget for which i have made the JPanel transparent. I have two JLabels on top of it one for holding an image and other for displaying time. I had a timer to update the time displayed in the JLabel. But With a transparent JPanel behind the jlabel's text gets overwritten instead of replacement. After Googling and Looking up on stackoverflow i tried many methods to override the paintcomponent method of the JLabel. But it didnt affect anything. Later I manually called the paintcomponent method inside the timer which worked out. But I feel its just a workaround. I need to know why the paintcomponent didnt get invoked and when it usually gets invoked.
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.RepaintManager;
import javax.swing.SwingConstants;
import javax.swing.text.SimpleAttributeSet;
import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class WindowSample {
private JFrame frame;
MyLabel panel1;
// JLabel panel1;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
WindowSample window = new WindowSample();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public WindowSample() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
frame.setSize(dim);
frame.setBounds(0, 0, 500, 500);
frame.setBackground(new Color(0, 255, 0, 0));
frame.setUndecorated(true);
frame.setContentPane(new ContentPane());
frame.getContentPane().setBackground(Color.WHITE);
frame.getContentPane().setLayout(null);
// ImagePanel panel = new ImagePanel();
JLabel panel = new JLabel(
scale(new ImageIcon("Science Drops.png").getImage()));
panel.setBounds(0, 0, 200, 200);
panel1 = new MyLabel();
// panel1 = new JLabel();
panel1.setHorizontalAlignment(SwingConstants.CENTER);
panel1.setAlignmentX(SwingConstants.CENTER);
panel1.setFont(new Font("Calibiri",Font.BOLD,16));
panel1.setBounds(0, 205, 200, 50);
Timer n = new Timer();
panel1.setBackground(Color.white);
n.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
DateFormat df = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
// this manual call to paintComponent did the trick. If i remove this line the text gets overwritten over itself for every second.
panel1.paintComponents(panel1.getGraphics());
panel1.setText(df.format(new Date()));
}
}, 1000, 1000);
frame.getContentPane().add(panel1);
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
#SuppressWarnings("serial")
public class MyLabel extends JLabel {
MyLabel() {
setOpaque(false);
}
#Override
public void paintComponents(Graphics arg0) {
Graphics2D g2d = (Graphics2D) arg0.create();
g2d.clearRect(0, 0, getWidth(), getHeight());
g2d.dispose();
super.paintComponents(arg0);
}
}
public class ContentPane extends JPanel {
public ContentPane() {
setOpaque(false);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.0f));
g2d.setColor(getBackground());
g2d.fill(getBounds());
g2d.dispose();
super.paintComponent(g);
}
}
public ImageIcon scale(Image src) {
int w = 200;
int h = 200;
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage dst = new BufferedImage(w, h, type);
Graphics2D g2 = dst.createGraphics();
g2.drawImage(src, 0, 0, w, h, frame);
g2.dispose();
return new ImageIcon(dst);
}
}
Read Backgrounds With Transparency for information on how transparency works and for some possible solutions.
Also, some other comments with your code:
Don't use a null layout. Swing was designed to be used with layout managers for to many reasons to list here.
Custom painting is done by overriding paintComponent() (no "s"). However, in your case I don't see any reason to do custom painting if you follow the advice in the link I provided above. I also don't think you need to do custom painting in your panel, but I don't totally understand what you are attempting to do.
Use javax.swing.Timer instead of java.util.Timer. Have a look at this tutorial from oracle about timers and swing.
You seem to be going about it the hard way...
labels are transparent by default.
labels support icons out of the box (include animated gifs ;))
null layouts are never a good idea, they might seem like a good idea, but you will spend more time correcting for funny little inconsistencies which be resolved using an appropriate layout manager...
java.util.Timer is not a suitable timer for Swing, instead you want to use javax.swing.Timer instead. It will trigger it's updates within the context of the EDT.
Based off what I think you want to do...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class MyClock {
public static void main(String[] args) {
new MyClock();
}
public MyClock() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final DateFormat df = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
final JLabel label = new JLabel(df.format(new Date()));
label.setIcon(new ImageIcon("Clock.png"));
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText(df.format(new Date()));
}
});
timer.start();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setUndecorated(true);
frame.setBackground(new Color(0, 255, 0, 0));
frame.add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Take a look at How to use icons for more details about icon support in Swing.
You may also find Window#alwaysOnTop useful (remember, all frames lead to Window)
I can't believe there is still nobody who answered the right answer. Here's how you get away with this kind of problem :
Apply setOpaque(false) to your components, but also to all the parents.
It will prevent painting problems on your components with transparent backgrounds.

more efficient layout than Box

I've got some very old code which uses a Box to list some information. I create it like so:
Box patterns = Box.createVerticalBox();
Very (very) often, new items are added and old items are removed eg:
label = new JLabel("xyz");
patterns.add(label);
and later
patterns.remove(label);
whenever something is added ore removed I have to have it repaint, so I call:
patterns.revalidate();
patterns.repaint();
Problem is, since this happens very often it chokes up the UI. I think I need a better implementation in order to make it more efficient.
I know I could maintain a list of the active items in the background and then intermittently update the actual UI (batch update) but...
Can someone suggest a more efficient alternative approach?
Why don't you just use a JList and implement a cell renderer?
Or more flexibility with a JTable and implement a table cell renderer (returns a Component instead)?
Based on this example, the following code loafs doing 16 labels at 10 Hz.
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
/** #see https://stackoverflow.com/questions/6605554 */
public class ImageLabelPanel extends Box implements ActionListener {
private static final int N = 16;
private final List<JLabel> list = new ArrayList<JLabel>();
private final Timer timer = new Timer(100, this);
ImageLabelPanel() {
super(BoxLayout.Y_AXIS);
BufferedImage bi = null;
try {
bi = ImageIO.read(new File("image.jpg"));
} catch (IOException e) {
e.printStackTrace(System.err);
}
for (int r = 0; r < N; r++) {
int w = bi.getWidth();
int h = bi.getHeight() / N;
BufferedImage b = bi.getSubimage(0, r * h, w, h);
list.add(new JLabel(new ImageIcon(b)));
}
createPane();
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setVisible(true);
timer.start();
}
private void createPane() {
this.removeAll();
for (JLabel label : list) {
add(label);
}
this.revalidate();
}
#Override
public void actionPerformed(ActionEvent e) {
Collections.shuffle(list);
createPane();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ImageLabelPanel();
}
});
}
}

Categories

Resources