Java: создание переходов по окнам (включая текст / изображения)
Я хочу создать оверлей в Java, который будет прозрачным, всегда сверху, и который я могу щелкнуть. Я нашел несколько похожих сообщений об этой проблеме, но даже после их ответов у меня возникла одна проблема.
Моя проблема заключается в том, чтобы сделать все окно кликом. У меня нет проблем с тем, чтобы он работал с JFrame, но как только я добавляю в него какие-либо компоненты (JLabel или ImagePanel), атрибут click-through не переносится на них.
Поскольку я хочу иметь фоновое изображение для своего приложения, это в основном делает мой код бесполезным, видя, как окно фокусируется всякий раз, когда я щелкаю область, которую покрывает текст / изображение.
Прежде чем показать код, который я использую, сначала я бы хотел обратиться к этим потокам, которые, по сути, точно описывают то, что я хочу, кроме C#.
Моя цель - создать оверлей с прозрачным.png-изображением и текстом сверху, который будет меняться при ключевых событиях. Если он использует JFrame или любую другую библиотеку, не имеет значения. Мне нужно только совместимо с Windows.
Я также хотел бы отметить, что у меня есть некоторый опыт работы с Java, но я новичок в использовании JFrame.
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import com.sun.jna.platform.WindowUtils;
public class Overlay {
public static void main(String[] args) {
JFrame frame = new JFrame("Overlay Window");
frame.setUndecorated(true);
frame.setAlwaysOnTop(true);
frame.getRootPane().putClientProperty("apple.awt.draggableWindowBackground", false);
frame.setLocation(400, 400);
frame.getContentPane().setLayout(new java.awt.BorderLayout());
JLabel textLabel = new JLabel("I'm a label in the window", SwingConstants.CENTER);
frame.getContentPane().add(textLabel, BorderLayout.CENTER);
frame.pack();
System.setProperty("sun.java2d.noddraw", "true");
WindowUtils.setWindowTransparent(frame, true);
WindowUtils.setWindowAlpha(frame, 1.0f);
//Using AWTUtilities gives the same result as WindowUtils
//AWTUtilities.setWindowOpaque(frame, false);
//AWTUtilities.setWindowOpacity(frame, 1.0f);
frame.setVisible(true);
}
}
Обратите внимание, что проблема не в том, что окно фокусируется (хотя это является результатом проблемы), а в том, что JLabel и ImagePanel не проходят по кликам.
3 ответа
Проблема с тем, что окно "кликает", заключается в том, что оно обрабатывается на системном уровне, вне рамок стандартных API. Это означает, что любой код, написанный для "щелчка по окну", будет зависеть от системы. При этом процесс выполнения этого на окнах довольно прост.
В Windows 2000 и более поздних версиях, установив флаги WS_EX_LAYERED и WS_EX_TRANSPARENT в окне, окно будет затем щелкнуто. Пример кода использует JNA для достижения этой цели:
public static void main(String[] args) {
Window w = new Window(null);
w.add(new JComponent() {
/**
* This will draw a black cross on screen.
*/
protected void paintComponent(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(0, getHeight() / 2 - 10, getWidth(), 20);
g.fillRect(getWidth() / 2 - 10, 0, 20, getHeight());
}
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
});
w.pack();
w.setLocationRelativeTo(null);
w.setVisible(true);
w.setAlwaysOnTop(true);
/**
* This sets the background of the window to be transparent.
*/
AWTUtilities.setWindowOpaque(w, false);
setTransparent(w);
}
private static void setTransparent(Component w) {
WinDef.HWND hwnd = getHWnd(w);
int wl = User32.INSTANCE.GetWindowLong(hwnd, WinUser.GWL_EXSTYLE);
wl = wl | WinUser.WS_EX_LAYERED | WinUser.WS_EX_TRANSPARENT;
User32.INSTANCE.SetWindowLong(hwnd, WinUser.GWL_EXSTYLE, wl);
}
/**
* Get the window handle from the OS
*/
private static HWND getHWnd(Component w) {
HWND hwnd = new HWND();
hwnd.setPointer(Native.getComponentPointer(w));
return hwnd;
}
Почему бы просто не использовать существующие JLayeredPane
? Эта запись блога демонстрирует наложение на JFrame разнообразных оверлеев, включая текст, изображения и динамически нарисованные пиксели.
Я пытался сделать полностью "прозрачным для событий" (сквозное, как вы его называете) окно, но, кажется, есть некоторые нативные ограничения на этот трюк.
Проверьте этот пример окна:
public static void main ( String[] args )
{
Window w = new Window ( null );
w.add ( new JComponent ()
{
protected void paintComponent ( Graphics g )
{
g.setColor ( Color.BLACK );
g.fillRect ( 0, getHeight () / 2 - 10, getWidth (), 20 );
g.fillRect ( getWidth () / 2 - 10, 0, 20, getHeight () );
}
public Dimension getPreferredSize ()
{
return new Dimension ( 100, 100 );
}
public boolean contains ( int x, int y )
{
return false;
}
} );
AWTUtilities.setWindowOpaque ( w, false );
AWTUtilities.setWindowOpacity ( w, 0.5f );
w.pack ();
w.setLocationRelativeTo ( null );
w.setVisible ( true );
}
Окно и компонент НЕ имеют:
- Мыши слушатели
- Слушатели движения мыши
- Слушатели колесика мыши
- Ключевые слушатели
Также компонент должен игнорировать любые события мыши ДАЖЕ, если есть какие-либо слушатели из-за измененного contains
метод.
Как видите, область, где на компоненте ничего не нарисовано, прозрачна для событий, а заполненная область - нет. К сожалению, я не нашел никакого обходного пути, чтобы изменить это поведение. Кажется, что некоторые "низкоуровневые" методы Java блокируют события.
И это только базовый пример на основе JComponent. Я даже не говорю о более сложных компонентах Swing, таких как ярлыки, кнопки и т. Д., Которые могут иметь своих собственных прослушивателей событий, которые могут блокировать события.