Re-paint on translucent frame/panel/component.
Asked Answered
L

4

15

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


    }   
}
Lum answered 29/1, 2010 at 16:57 Comment(2)
Try resetting the background in the timer code too, or calling repaint on the entire panel. I thing the background just doesn't know that it needs to be repainted.Comma
If that fixes it, I'll make it an answer, but it's just a guess right now.Comma
P
16

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();
            }
        });
    }
}
Prokofiev answered 30/1, 2010 at 1:52 Comment(11)
+1 for adding the screenshot. Looks promising. The only difference is, the text should not be translucent, but opaque ( like this screenshot in windows ) but given the current state of my code, I guess this should the be workaround for OSX. I'll give it a try tomorrow when I have my Mac at hand again. Thank you.Lum
Oh, you're not using a JLabel but painting directly on the panel... mmhhhh I have to try that.Lum
Yes, at some point you have to do a composite with the frame's background. I'll update to show how to do opaque text.Prokofiev
This is great. Thank you. I'm still having a bit of a problem displaying the text, there is a "shadow" of the last number printed after a resize, but that's something I can live with. So, If I understand this right, what you do is to paint over the panel the previous number but is the AlphaComposite as "Clear" which I assume clears the panel and then you paint on top of that. Is this the principle? I'll look further to the code. Here's that little "ghost" : img21.imageshack.us/img21/9195/capturadepantalla201002.pngLum
Now this is odd. If I call setUndecorated( true ) the ghost is gone. I don't have an Idea of why?... img36.imageshack.us/img36/9195/capturadepantalla201002.pngLum
Drat! I see it, too. I thought a ComponentListener might coerce it, but it appears to be a Quartz "feature"; it disappears after inconify/de-inconify. The apple.awt.CWindow trick didn't seem to work. Yes, setting Clear mode causes all subsequent drawing to clear the desitiantion pixel(s).Prokofiev
I think the shadow is an artifact of Apple's Quartz trying to optimize repaints; setUndecorated() may work by preventing resizing.Prokofiev
I copied this code exactly and got a white, opaque background instead of the black transparent one shown in the screenshot. Any ideas?Colmar
My case encountered error java.awt.IllegalComponentStateException: The frame is decorated, I found a solution which is to put f.setUndecorated(true); before f.setBackground(new Color(0f, 0f, 0f, 0.3f)); and it works fine.Doe
I missed one thing; it works fine, but not translucent.Doe
This may depend on the host platform's renderer, e.g. on MacOS, apple.awt.graphics.UseQuartz; don't hesitate to cite/use this answer if you post a question.Prokofiev
M
4

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.

Machado answered 29/1, 2010 at 17:14 Comment(4)
In case it's not clear, if you do Swing-related stuff OFF the EDT, then you get artifacts like you're experiencing--paint events, for example, might arrive out of order. jjnguy: a revalidation should be automatically triggered already by the JLabel setText() method; the reason a repaint() event might work here is because it will be scheduled on the EDT for later execution. It's better to make sure the initial setText() call was done on the EDT in the first place.Machado
I tried switching the timertask to this: timer.schedule( new TimerTask(){ int i = 0; public void run() { SwingUtilities.invokeLater(new Runnable(){ public void run(){ label.setText("Hola "+ i++ ); } }); } }, 0, 1000 ); And it didn't work. Sun Bug: bugs.sun.com/view_bug.do?bug_id=4297006 states that you may have to override paintComponent() because opaqueness and translucent don't interact well.Crumpet
Yeap, I tried actually with SwingUtilities.invokeLater which has the same effect ( send the message in the EDT ) with exactly the same results. I remove it before posting to make the code brief.Lum
@Liard: I've tried your code ( just to be sure ) but it has exactly the same effect.Lum
C
2

I don't know if the problem is solved, but I solved it in my application with a "Frame.repaint();"

So every second my Frame will be repainted and my JLabel will be updatet with the actual time.

Cos answered 13/10, 2011 at 10:1 Comment(0)
G
0
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package mreg;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

/**
 *
 * @author Manoj
 */
public class TranscluentWindow {

 public static void main(String[] args) {
        new TranscluentWindow();
    }

    public TranscluentWindow() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (Exception ex) {
                    }

                    JWindow frame = new JWindow();
                    frame.setAlwaysOnTop(true);
                    frame.addMouseListener(new MouseAdapter() {



                    });
                    frame.setBackground(new Color(0,0,0,0));
                    frame.setContentPane(new TranslucentPane());
                    frame.add(new JLabel(new ImageIcon(ImageIO.read(getClass().getResource("/124742-high-school-collection/png/image_4.png")))));
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);

                       new Thread(new Runnable() {
            public void run() {

                    try {

                        Thread.sleep(2500);
                    } catch (InterruptedException ex) {
                    }

                frame.dispose();
                new loging().setVisible(true);
            }
        }).start();






                } catch (IOException ex) {
                    ex.printStackTrace();
                }

            }
        });
    }

    public class TranslucentPane extends JPanel {

        public TranslucentPane() {
            setOpaque(false);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g); 

            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setComposite(AlphaComposite.SrcOver.derive(0.0f));
            g2d.setColor(getBackground());
            g2d.fillRect(0, 0, getWidth(), getHeight());

        }

    }   



}
Gerontology answered 6/10, 2016 at 7:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.