Перекрасьте полупрозрачную раму/панель/компонент.

Я пытаюсь создать полупрозрачное окно с Java на OSX и добавить к нему JLabel.

Этот JLabel меняет свой текст каждую секунду....

alt text

Однако компонент плохо перерисовывается.

Как я могу решить эту проблему?

Я нашел эти статьи, но я не могу понять, как это решить.

Если возможно, вставьте исходный код исправления, вот мой:

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


    }   
}

person OscarRyz    schedule 29.01.2010    source источник
comment
Попробуйте сбросить фон и в коде таймера или вызвать перерисовку на всей панели. Я думаю, что фон просто не знает, что его нужно перекрасить.   -  person jjnguy    schedule 29.01.2010
comment
Если это исправит, я сделаю это ответом, но сейчас это просто предположение.   -  person jjnguy    schedule 29.01.2010


Ответы (3)


Мне повезло расширить JLabel и реализовать Icon, чтобы полупрозрачный компонент работал так, как я хочу. Вы можете увидеть результат различных комбинаций правил в этом AlphaCompositeDemo. Пример ниже: 100 % белого поверх 50 % черного.

Приложение: обратите внимание, как в этом примере непрозрачный текст накладывается на чистый закадровый фон поверх полупрозрачного фона рамки.

Приложение: вот способ сделать весь кадр полупрозрачный. К сожалению, это также затемняет содержимое.

изображение

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();
            }
        });
    }
}
person trashgod    schedule 30.01.2010
comment
+1 за добавление скриншота. Выглядит многообещающе. Единственная разница в том, что текст должен быть не полупрозрачным, а непрозрачным (как этот снимок экрана в Windows), но, учитывая текущее состояние моего кода, я думаю, это должно быть обходным путем для OSX. Я попробую завтра, когда мой Mac снова будет под рукой. Спасибо. - person OscarRyz; 01.02.2010
comment
О, вы не используете JLabel, а рисуете прямо на панели... ммм, надо попробовать. - person OscarRyz; 01.02.2010
comment
Да, в какой-то момент вам придется сделать композицию с фоном кадра. Я обновлю, чтобы показать, как сделать непрозрачный текст. - person trashgod; 02.02.2010
comment
Черт! Я тоже это вижу. Я думал, что ComponentListener может принудить к этому, но, похоже, это функция Quartz; он исчезает после inconify/de-inconify. Уловка apple.awt.CWindow не сработала. Да, установка режима Clear приводит к тому, что все последующие отрисовки удаляют ненужные пиксели. - person trashgod; 02.02.2010
comment
Я думаю, что тень — это результат попытки Apple Quartz оптимизировать перерисовку; setUndecorated() может работать, предотвращая изменение размера. - person trashgod; 02.02.2010
comment
Я в точности скопировал этот код и получил белый непрозрачный фон вместо черного прозрачного, показанного на скриншоте. Любые идеи? - person gsingh2011; 29.03.2012

Проблема также может быть связана с тем, что вы устанавливаете текст 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.

person Laird Nelson    schedule 29.01.2010
comment
В случае, если это неясно, если вы делаете вещи, связанные со Swing, вне EDT, то вы получаете артефакты, подобные тем, которые вы испытываете - например, события рисования могут поступать не по порядку. jjnguy: повторная проверка должна автоматически запускаться уже методом JLabel setText(); причина, по которой здесь может работать событие repaint(), заключается в том, что оно будет запланировано в EDT для последующего выполнения. Лучше сначала убедиться, что первоначальный вызов setText() был выполнен в EDT. - person Laird Nelson; 29.01.2010
comment
Я попытался переключить timertask на это: ); } }); } }, 0, 1000 ); И это не сработало. Ошибка Sun: bugs.sun.com/view_bug.do?bug_id=4297006 заявляет, что вам, возможно, придется переопределить paintComponent(), потому что непрозрачность и полупрозрачность плохо взаимодействуют. - person Kylar; 29.01.2010
comment
Да, я пробовал на самом деле с SwingUtilities.invokeLater, который имеет тот же эффект (отправить сообщение в EDT) с точно такими же результатами. Я удаляю его перед публикацией, чтобы сделать код кратким. - person OscarRyz; 29.01.2010
comment
@Liard: я пробовал ваш код (просто чтобы быть уверенным), но он имеет точно такой же эффект. - person OscarRyz; 29.01.2010

Я не знаю, решена ли проблема, но я решил ее в своем приложении с помощью "Frame.repaint();"

Таким образом, каждую секунду мой кадр будет перекрашиваться, а мой JLabel будет обновляться с учетом фактического времени.

person Icy    schedule 13.10.2011