在半透明框架/面板/组件上重新绘制。

Posted

技术标签:

【中文标题】在半透明框架/面板/组件上重新绘制。【英文标题】:Re-paint on translucent frame/panel/component. 【发布时间】:2011-01-10 22:28:02 【问题描述】:

我正在尝试在 OSX 上使用 Java 创建一个半透明窗口,并向其中添加 JLabel

这个JLabel 每秒都会改变它的文字......

但是组件没有很好地重新绘制。

我该如何解决这个问题?

我找到了thesearticles,但我不知道如何解决它。

如果可能,请粘贴修复源代码,这是我的:

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


       

【问题讨论】:

尝试在计时器代码中也重置背景,或者在整个面板上调用重绘。我觉得背景只是不知道它需要重新粉刷。 如果可以解决,我会给出答案,但现在只是猜测。 【参考方案1】:

我很幸运地扩展了JLabel 并实现了Icon 以使半透明组件按我想要的方式工作。您可以在这个AlphaCompositeDemo 中看到各种规则组合的结果。 下面的例子是 100% 的白色和 50% 的黑色。

附录:请注意此示例如何在透明的屏幕外背景上合成不透明文本和半透明框架背景。

附录:这是制作whole frame translucent 的方法。不幸的是,它也会使内容变暗。

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

【讨论】:

+1 用于添加屏幕截图。看起来很有希望。唯一的区别是,文本不应该是半透明的,而是不透明的(就像 windows 中的这个屏幕截图),但考虑到我的代码的当前状态,我想这应该是 OSX 的解决方法。明天当我手头有我的 Mac 时,我会试一试。谢谢。 哦,你用的不是JLabel,而是直接在面板上绘画……嗯嗯,我得试试。 是的,在某些时候您必须与框架的背景进行合成。我将更新以展示如何制作不透明的文本。 这很棒。谢谢你。我在显示文本时仍然遇到一些问题,调整大小后打印的最后一个数字有一个“阴影”,但这是我可以忍受的。因此,如果我理解正确,您所做的就是在面板上绘制前一个数字,但 AlphaComposite 是“清除”,我假设它会清除面板,然后在上面绘制。这是原理吗?我将进一步查看代码。这是那个小“鬼”:img21.imageshack.us/img21/9195/capturadepantalla201002.png 现在这很奇怪。如果我打电话给setUndecorated( true ),鬼就消失了。我不知道为什么?... img36.imageshack.us/img36/9195/capturadepantalla201002.png【参考方案2】:

问题也可能与您从不是事件调度线程的线程设置JLabel 的文本有关。

有两种方法可以解决这个问题。在不测试您的问题的情况下,我将使用 javax.swing.Timer 类而不是 java.util.Timer 类来解决它。 javax.swing.Timer 将确保在调度线程上触发事件。

所以(未经测试的代码):

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

解决此问题的另一种方法是继续使用java.util.Timer,但要确保您使用EventQueue.invokeLater(Runnable) 以确保在 EDT 上更新标签。

【讨论】:

如果不清楚,如果您在 EDT 之外执行与 Swing 相关的事情,那么您会得到像您正在经历的工件——例如,绘画事件可能会乱序到达。 jjnguy:JLabel setText() 方法应该已经自动触发了重新验证; repaint() 事件可能在这里起作用的原因是因为它将被安排在 EDT 上以供以后执行。最好首先确保在 EDT 上完成了初始 setText() 调用。 我尝试将 timertask 切换为: timer.schedule( new TimerTask() int i = 0; public void run() SwingUtilities.invokeLater(new Runnable() public void run() label.setText("Hola "+ i++ ); ); , 0, 1000 );它没有用。 Sun Bug:bugs.sun.com/view_bug.do?bug_id=4297006 声明您可能必须重写paintComponent(),因为不透明和半透明不能很好地交互。 是的,我实际上尝试了SwingUtilities.invokeLater,它具有相同的效果(在 EDT 中发送消息),结果完全相同。我在发布之前将其删除以使代码简短。 @Liard:我试过你的代码(只是为了确定),但效果完全相同。【参考方案3】:

我不知道问题是否已解决,但我在我的应用程序中使用“Frame.repaint();”解决了它

所以每一秒我的 Frame 都会被重新绘制,而我的 JLabel 会根据实际时间进行更新。

【讨论】:

【参考方案4】:
/*
 * 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());

        

       




【讨论】:

以上是关于在半透明框架/面板/组件上重新绘制。的主要内容,如果未能解决你的问题,请参考以下文章

如何在半透明的 QWidget 上播放视频?

在 XP 上,如何让工具提示出现在半透明表单上方?

iOS Autolayout - 在半透明导航栏下方正确定位视图

SceneKit:使子球体在半透明父球体内可见?

无法使 UITableView 出现在半透明导航栏下

在具有一些控件的 Windows 窗体上绘制半透明覆盖图像