21
June

Прозрачные окна в Java Swing (transparent windows)

Posted in: Java GUI, Swing, AWT, SWT, Java technologies, J2SE |

Библиотека Java Swing не предоставляет возможности создания прозрачных (transparent) окон без использования native кода.
Наткнулся на одну интересную статью. В ней рассказывается, как можно без этого самого native кода сделать “прозрачные” окошки в Java Swing. Реализуется это при помощи хитрого хака:

  • Делаем скриншот перед прорисовкой окна.
  • Используем этот screenshot как background фон для нашего окошка.
  • Располагаем все это дело так, чтобы реальное изображение экрана и фон окна совпадали - соответственно, создастся иллюзия прозрачного окна.


Для того, чтобы сделать скриншот экрана, можно воспользоваться методом createScreenCapture из класса java.awt.Robot.
В статье мне не понравилась фраза, что мол это все просто, а вот сделать, чтобы окно обновлялось при изменении и перемещении окна - намного сложнее.
Данное заявление было опровергнуто буквально через 2 минуты.
Достаточно было навесить Listener на объект JFrame, который бы перерисовывал background.
А вот собственно и код на Java (импорт классов опущен):

public class TransparentBackground extends JComponent {
   private JFrame frame;
   private Image background;

   public TransparentBackground(JFrame frame) {
       this.frame = frame;
       updateBackground();
   }

   public void updateBackground() {
       try {
           Robot rbt = new Robot();
           Toolkit tk = Toolkit.getDefaultToolkit();
           Dimension dim = tk.getScreenSize();
           background = rbt.createScreenCapture(
                   new Rectangle(0, 0, (int) dim.getWidth(),
                           (int) dim.getHeight()));
       } catch (Exception ex) {
           ex.printStackTrace();
       }
   }

   public void paintComponent(Graphics g) {
       Point pos = this.getLocationOnScreen();
       Point offset = new Point(-pos.x, -pos.y);
       g.drawImage(background, offset.x, offset.y, null);
   }

   private static class TransparentComponentListener
           implements ComponentListener {

       public void componentResized(ComponentEvent e) {
           Component[] components = ((JFrame) e.getComponent())
                   .getContentPane().getComponents();
           components[0].repaint();
       }

       public void componentMoved(ComponentEvent e) {
           componentResized(e);
       }

       public void componentShown(ComponentEvent e) {
           componentResized(e);
       }

       public void componentHidden(ComponentEvent e) {
           componentResized(e);
       }
   }

   public static void main(String[] args) {
       JFrame frame = new JFrame("Transparent Window");
       frame.addComponentListener(new TransparentComponentListener());
       TransparentBackground bg = new TransparentBackground(frame);
       bg.setLayout(new BorderLayout());
       JButton button = new JButton("This is a button");
       bg.add("North", button);
       JLabel label = new JLabel("This is a label");
       bg.add("South", label);
       frame.getContentPane().add("Center", bg);
       frame.pack();
       frame.setSize(200, 200);
       frame.show();
   }
}

Вот результат выполнения программы:
Transparent Window in Java Swing
А оригинал статьи находится здесь.

5 Comments »

RSS feed for comments on this post.



blackcat3713
November 6, 2007 #

попробовал окошко в действии)) немного подтормаживает, но в принципе неплохо)

один вопрос автору..
у меня это окошко не видит (т.е. я на экране вижу эти элементы, а оно показывает как будто их нет!) элементы: собственную закладку в панели задач, закладку диспетчера задач, иконку диспетчера задач в трее.

и! самое интересное!) показывает скрытые мной иконки в Eclipse! т.е. иконки я скрыл (а окошко их на старом месте показывает (= )

на экране их ясный пень не видно))

как автор может это прокомментировать? ведь за основу должен браться “скриншот”, а винда похоже подсовывает что-то левое

blackcat3713
November 6, 2007 #

еще одна фича)) граничит уже с хаком))

окошко это “видит” eclipse сквозь другие приложения, как рентген)

автору респект за такую штуку)) пошел дальше играцца)

скриншот делается один раз при создании окна, на все остальные изменения окошку “всеравно”. халтура) скриншот надо делать при каждом repaint. ушел переписывать окошко)

November 13, 2007 #

Попытался сделать получше…
Получилось конечно кривовато, но получше.
Теперь окно совсем не видно, а перетаскивается за любую часть.
Исправлен баг с тем отображением фона, который сохранился при первом открытии.
Но остался баг. Если открыть программу, а затем свернуть окно находящееся за ним, то программа перерисует фон только при активизации ее фрейма.


import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

import javax.swing.*;

public class TransparentBackground extends JComponent implements
MouseMotionListener, MouseListener, ComponentListener, WindowListener {
/**
*
*/
private static final long serialVersionUID = 1L;

private JFrame frame;

private int x, y;

private Image background;

public TransparentBackground(JFrame frame) {
this.frame = frame;
frame.addWindowListener(this);
addMouseMotionListener(this);
addMouseListener(this);
updateBackground();
}

public void updateBackground() {
try {
Dimension frameDim = frame.getSize();
frame.setSize(0, 0);
Robot rbt = new Robot();
Toolkit tk = Toolkit.getDefaultToolkit();
Dimension dim = tk.getScreenSize();

background = rbt.createScreenCapture(new Rectangle(0, 0, (int) dim
.getWidth(), (int) dim.getHeight()));
frame.setSize(frameDim);
} catch (Exception ex) {
ex.printStackTrace();
}
}

public void paintComponent(Graphics g) {
Point pos = this.getLocationOnScreen();
Point offset = new Point(-pos.x, -pos.y);
g.drawImage(background, offset.x, offset.y, null);
}

public void componentResized(ComponentEvent e) {
Component[] components = ((JFrame) e.getComponent()).getContentPane()
.getComponents();
if (components.length > 0)
components[0].repaint();
}

public void componentMoved(ComponentEvent e) {
componentResized(e);
}

public void componentShown(ComponentEvent e) {
componentResized(e);
}

public void componentHidden(ComponentEvent e) {
componentResized(e);

}

public static void main(String[] args) {
final JFrame frame = new JFrame("Transparent Window");
frame.setUndecorated(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("This is a button");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
frame.dispose();
}
});
TransparentBackground bg = new TransparentBackground(frame);

bg.setLayout(new BorderLayout());
bg.add("North", button);
JLabel label = new JLabel("This is a label");
bg.add("South", label);
frame.getContentPane().add("Center", bg);
frame.setSize(200, 200);

frame.setVisible(true);
}

public void mouseDragged(MouseEvent e) {
frame.setLocation(e.getX() + frame.getX() - x, e.getY() + frame.getY()
- y);
frame.repaint();
}

public void mouseMoved(MouseEvent e) {
}

public void mouseClicked(MouseEvent e) {

}

public void mouseEntered(MouseEvent e) {
}

public void mouseExited(MouseEvent e) {
}

public void mousePressed(MouseEvent e) {
x = e.getX();
y = e.getY();
}

public void mouseReleased(MouseEvent e) {
}

public void windowActivated(WindowEvent e) {
updateBackground();
frame.repaint();
}

public void windowClosed(WindowEvent e) {}

public void windowClosing(WindowEvent e) {}

public void windowDeactivated(WindowEvent e) {
updateBackground();
frame.repaint();
}

public void windowDeiconified(WindowEvent e) {}

public void windowIconified(WindowEvent e) {}

public void windowOpened(WindowEvent e) {}
}

Stas
January 27, 2008 #

А еще это окно “видит” неактивные окна как активные, подменяет элементы интерфейса окон на стандартные Windows и делает из белого текста черный (некоторые скрытые Label’и). Может есть еще какой-нибудь метод для снятия скринов с экрана?

Balabas
February 20, 2008 #

Прикольно. Только дёрганое немного окошко и когда деактивируется, становится непрозрачным, хотя :
[qoute]
public void windowDeactivated(WindowEvent e) {
updateBackground();
frame.repaint();
[/qoute]
????

Leave a comment

XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>