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);
}
}

Upvotes: 3

Views: 1205

Answers (4)

Jack'
Jack'

Reputation: 2526

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.

Upvotes: 0

MadProgrammer
MadProgrammer

Reputation: 347314

You seem to be going about it the hard way...

  1. labels are transparent by default.
  2. labels support icons out of the box (include animated gifs ;))
  3. 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...
  4. 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...

enter image description here

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)

Upvotes: 2

BetaRide
BetaRide

Reputation: 16844

Use javax.swing.Timer instead of java.util.Timer. Have a look at this tutorial from oracle about timers and swing.

Upvotes: 2

camickr
camickr

Reputation: 324147

Read Backgrounds With Transparency for information on how transparency works and for some possible solutions.

Also, some other comments with your code:

  1. Don't use a null layout. Swing was designed to be used with layout managers for to many reasons to list here.
  2. 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.

Upvotes: 4

Related Questions