Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Re-paint on translucent frame/panel/component.

I'm trying to create a translucent window with Java on OSX and add a JLabel to it.

This JLabel changes its text every second....

alt text

However the component is not repainting well.

How can I solve this problem?

I've found the these articles, but I can't figure out how to solve it.

If possible, please paste the fixing source code, here's mine:

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.awt.Color;
import java.awt.Font;
import java.util.Timer;
import java.util.TimerTask;

public class Translucent {
    public static void main( String [] args ) {

        JFrame frame = new JFrame();

        frame.setBackground( new Color( 0.0f,0.0f,0.0f,0.3f));

        final JLabel label =  new JLabel("Hola");
        label.setFont( new Font( label.getFont().getFamily(), Font.PLAIN, 46 ) );
        label.setForeground( Color.white );

        frame.add( label );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible( true );

        Timer timer = new Timer();
        timer.schedule( new TimerTask(){
            int i = 0;
            public void run() {
                label.setText("Hola "+ i++ );
            }
        }, 0, 1000 );


    }   
}
like image 629
OscarRyz Avatar asked Jan 29 '10 16:01

OscarRyz


People also ask

How does repaint work in Java?

The repaint method is an asynchronous method of applet class. When call to repaint method is made, it performs a request to erase and perform redraw of the component after a small delay in time.

What happens when a UI component is obscured by another window?

For example, when you obscure a component with another window and then reexpose it, a Swing thread may ask the component to redraw itself. Swing asks a component to draw itself by calling its paint() method.


2 Answers

I've had some luck extending JLabel and implementing Icon to get a translucent component working the way I want. You can see the result of various rule combinations in this AlphaCompositeDemo. The example below is 100% white atop 50% black.

Addendum: Note how this example composites opaque text on a clear offscreen background over the translucent frame background.

Addendum: Here's a way to make the whole frame translucent. Unfortunately, it dims the content, too.

image

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Translucent extends JPanel implements ActionListener {

    private static final int W = 300;
    private static final int H = 100;
    private static final Font font =
        new Font("Serif", Font.PLAIN, 48);
    private static final SimpleDateFormat df =
        new SimpleDateFormat("HH:mm:ss");
    private final Date now = new Date();
    private final Timer timer = new Timer(1000, this);
    private BufferedImage time;
    private Graphics2D timeG;

    public Translucent() {
        super(true);
        this.setPreferredSize(new Dimension(W, H));
        timer.start();
    }

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        int w = this.getWidth();
        int h = this.getHeight();
        g2d.setComposite(AlphaComposite.Clear);
        g2d.fillRect(0, 0, w, h);
        g2d.setComposite(AlphaComposite.Src);
        g2d.setPaint(g2d.getBackground());
        g2d.fillRect(0, 0, w, h);
        renderTime(g2d);
        int w2 = time.getWidth() / 2;
        int h2 = time.getHeight() / 2;
        g2d.setComposite(AlphaComposite.SrcOver);
        g2d.drawImage(time, w / 2 - w2, h / 2 - h2, null);
    }

    private void renderTime(Graphics2D g2d) {
        g2d.setFont(font);
        String s = df.format(now);
        FontMetrics fm = g2d.getFontMetrics();
        int w = fm.stringWidth(s);
        int h = fm.getHeight();
        if (time == null && timeG == null) {
            time = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
            timeG = time.createGraphics();
            timeG.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
            timeG.setFont(font);
        }
        timeG.setComposite(AlphaComposite.Clear);
        timeG.fillRect(0, 0, w, h);
        timeG.setComposite(AlphaComposite.Src);
        timeG.setPaint(Color.green);
        timeG.drawString(s, 0, fm.getAscent());
    }

    private static void create() {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setBackground(new Color(0f, 0f, 0f, 0.3f));
        f.setUndecorated(true);
        f.add(new Translucent());
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        now.setTime(System.currentTimeMillis());
        this.repaint();
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                create();
            }
        });
    }
}
like image 110
trashgod Avatar answered Oct 19 '22 03:10

trashgod


The problem may also have to do with the fact that you're setting the JLabel's text from a thread that is not the Event Dispatch Thread.

There are two ways to solve this. Without testing your problem, I'd solve it by using the javax.swing.Timer class, instead of the java.util.Timer class. javax.swing.Timer will ensure that events are fired on the dispatch thread.

So (untested code):

final ActionListener labelUpdater = new ActionListener() {
  private int i;
  @Override
  public final void actionPerformed(final ActionEvent event) {
    label.setText("Hola " + this.i++);
  }
};
final javax.swing.Timer timer = new javax.swing.Timer(1000L, labelUpdater);

The other way to solve it is to continue to use java.util.Timer but to make sure that you use EventQueue.invokeLater(Runnable) to ensure that updates to the label take place on the EDT.

like image 30
Laird Nelson Avatar answered Oct 19 '22 03:10

Laird Nelson