是否有可能在 javafx 中有一个透明的实用程序阶段?
Is it possible to have a transparent utility stage in javafx?
我知道舞台可以设置为实用风格"Stage.InitStyle(StageStyle.UTILITY);"也可以设置为透明风格"Stage.InitStyle(StageStyle.TRANSPARENT);"但是可以在同一个舞台上同时设置吗?我很累,所以舞台不会在开始菜单中显示为window,我希望舞台不可见,这样你只能看到场景。
您始终可以使用具有该功能的 Swing 以旧方式进行操作。 Swing 允许您嵌入 JavaFX。当然最好有一个没有 Swing 的干净机制,但 afaik 它不存在(还)。
示例:
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.Background;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import java.awt.geom.GeneralPath;
public class Widget extends JFrame {
class DragContext {
double x;
double y;
}
public Widget() {
// decoration
setType(Type.UTILITY);
setUndecorated(true);
setSize(200, 200);
toBack();
// position
// setLocation(100, 100);
setLocationRelativeTo(null); // centers on screen
// frame operations
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame shape (a star)
double points[][] = { { 0, 85 }, { 75, 75 }, { 100, 10 }, { 125, 75 }, { 200, 85 }, { 150, 125 }, { 160, 190 }, { 100, 150 }, { 40, 190 }, { 50, 125 }, { 0, 85 } };
GeneralPath star = new GeneralPath();
star.moveTo(points[0][0], points[0][1]);
for (int k = 1; k < points.length; k++)
star.lineTo(points[k][0], points[k][2]);
star.closePath();
setShape(star);
// embed fx into swing
JFXPanel fxPanel = new JFXPanel();
Widget.this.getContentPane().add(fxPanel);
Platform.runLater(new Runnable() {
@Override
public void run() {
// set scene in JFXPanel
fxPanel.setScene( createFxScene());
// show frame
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
Widget.this.setVisible(true);
// send it to the desktop, behind all other existing windows
// Widget.this.toBack();
// Widget.this.repaint();
}
});
}
});
}
private Scene createFxScene() {
StackPane rootPane = new StackPane();
rootPane.setBackground(Background.EMPTY);
// add some node
Label label = new Label("Bright & Shiny");
label.setTextFill(Color.RED);
rootPane.getChildren().add(label);
// create scene
Scene scene = new Scene(rootPane);
// gradient fill
RadialGradient radialGradient = new RadialGradient( 270, 0.8, 0.5, 0.5, 0.7, true, CycleMethod.NO_CYCLE, new Stop( .5f, Color.YELLOW), new Stop( .7f, Color.ORANGE), new Stop( .9f, Color.ORANGERED));
scene.setFill(radialGradient);
// context menu with close button
ContextMenu contextMenu = new ContextMenu();
MenuItem closeMenuItem = new MenuItem("Close");
closeMenuItem.setOnAction(actionEvent -> System.exit(0));
contextMenu.getItems().add(closeMenuItem);
// set context menu for scene
scene.setOnMousePressed(mouseEvent -> {
if (mouseEvent.isSecondaryButtonDown()) {
contextMenu.show(rootPane, mouseEvent.getScreenX(), mouseEvent.getScreenY());
}
});
// allow the frame to be dragged around
final DragContext dragDelta = new DragContext();
rootPane.setOnMousePressed(mouseEvent -> {
dragDelta.x = Widget.this.getLocation().getX() - mouseEvent.getScreenX();
dragDelta.y = Widget.this.getLocation().getY() - mouseEvent.getScreenY();
});
rootPane.setOnMouseDragged(mouseEvent -> Widget.this.setLocation((int) (mouseEvent.getScreenX() + dragDelta.x), (int) (mouseEvent.getScreenY() + dragDelta.y)));
return scene;
}
public static void main(String[] args) {
new Widget();
}
}
这是未显示在任务栏中的小部件的屏幕截图。用鼠标左键拖动它。鼠标右键提供带有关闭按钮的上下文菜单。
上面的代码使用了秋千架的形状。下面的代码使用了 javafx 控件的形状。
这是一个只有控件可见的版本。我用的是反射标签控件。
如果要将控件直接发送到桌面,则需要激活 toBack() 调用。您可以使用鼠标滚轮缩放控件。最大缩放尺寸限制为jframe的尺寸。
您需要为自定义控件做的就是实现 createFxControl() 中的代码
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.effect.Reflection;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class LabelWidget extends JFrame {
class DragContext {
double x;
double y;
}
public LabelWidget() {
// decoration
setType(Type.UTILITY);
setUndecorated(true);
// make frame transparent, we only want the control to be visible
setBackground(new java.awt.Color(0,0,0,0));
setSize(400, 400);
// position
// setLocation(100, 100);
setLocationRelativeTo(null); // centers on screen
// frame operations
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// embed fx into swing
JFXPanel fxPanel = new JFXPanel();
LabelWidget.this.getContentPane().add(fxPanel);
Platform.runLater(new Runnable() {
@Override
public void run() {
// set scene in JFXPanel
fxPanel.setScene( createFxScene());
// show frame
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
LabelWidget.this.setVisible(true);
// send it to the desktop, behind all other existing windows
// ClockWidget.this.toBack();
// ClockWidget.this.repaint();
}
});
}
});
}
private Scene createFxScene() {
StackPane rootPane = new StackPane();
// make pane transparent, we only want the control to be visible
rootPane.setBackground(Background.EMPTY);
// add control
Control control = createFxControl();
rootPane.getChildren().add( control);
// create scene
Scene scene = new Scene(rootPane);
// make scene transparent, we only want the control to be visible
scene.setFill( Color.TRANSPARENT);
// context menu with close button
ContextMenu contextMenu = new ContextMenu();
MenuItem closeMenuItem = new MenuItem("Close");
closeMenuItem.setOnAction(actionEvent -> System.exit(0));
contextMenu.getItems().add(closeMenuItem);
control.setContextMenu(contextMenu);
// allow the frame to be dragged around
makeDraggable( control);
// allow zooming
makeZoomable( control);
return scene;
}
/**
* Create the JavaFX control of which we use the shape.
* @return
*/
private Control createFxControl() {
Label label = new Label( "I'm a Label");
label.setFont(new Font("Tahoma", 24));
label.setEffect(new Reflection());
return label;
}
/**
* Allow dragging of the stage / control on the desktop
* @param control
* @param stage
*/
public void makeDraggable( Control control) {
final DragContext dragDelta = new DragContext();
control.setOnMousePressed(mouseEvent -> {
dragDelta.x = LabelWidget.this.getLocation().getX() - mouseEvent.getScreenX();
dragDelta.y = LabelWidget.this.getLocation().getY() - mouseEvent.getScreenY();
});
control.setOnMouseDragged(mouseEvent -> LabelWidget.this.setLocation((int) (mouseEvent.getScreenX() + dragDelta.x), (int) (mouseEvent.getScreenY() + dragDelta.y)));
}
/**
* Allow zooming
* @param control
*/
public void makeZoomable( Control control) {
// note: in order to make it larger, we'd have to resize the stage/frame => we limit the size to 1.0 for now and allow only making the control smaller
final double MAX_SCALE = 1.0;
final double MIN_SCALE = 0.1;
control.addEventFilter(ScrollEvent.ANY, new EventHandler<ScrollEvent>() {
@Override
public void handle(ScrollEvent event) {
double delta = 1.2;
double scale = control.getScaleX();
if (event.getDeltaY() < 0) {
scale /= delta;
} else {
scale *= delta;
}
scale = clamp(scale, MIN_SCALE, MAX_SCALE);
control.setScaleX(scale);
control.setScaleY(scale);
event.consume();
}
});
}
/**
* Limit bounds of value
* @param value
* @param min
* @param max
* @return
*/
public static double clamp( double value, double min, double max) {
if( Double.compare(value, min) < 0)
return min;
if( Double.compare(value, max) > 0)
return max;
return value;
}
public static void main(String[] args) {
new LabelWidget();
}
}
或者,如果您手头有 https://github.com/HanSolo/Enzo 的 Enzo 库,您可以使用此代码:
private Control createFxControl() {
// create a clock using the enzo library from https://github.com/HanSolo/Enzo
Clock clock = ClockBuilder.create()
// .prefSize(400, 400)
.design(Clock.Design.DB)
.running(true)
.text("Berlin")
.autoNightMode(true)
.build();
return clock;
}
创建这个:
找到了一个简单的方法:
创建一个实用程序 window 并使其完全透明,这样您就无法看到它或与之交互。然后创建想要的 window 并将所有者初始化为实用程序 window - 这将导致它不会出现在任务栏中。
我知道舞台可以设置为实用风格"Stage.InitStyle(StageStyle.UTILITY);"也可以设置为透明风格"Stage.InitStyle(StageStyle.TRANSPARENT);"但是可以在同一个舞台上同时设置吗?我很累,所以舞台不会在开始菜单中显示为window,我希望舞台不可见,这样你只能看到场景。
您始终可以使用具有该功能的 Swing 以旧方式进行操作。 Swing 允许您嵌入 JavaFX。当然最好有一个没有 Swing 的干净机制,但 afaik 它不存在(还)。
示例:
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.Background;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import java.awt.geom.GeneralPath;
public class Widget extends JFrame {
class DragContext {
double x;
double y;
}
public Widget() {
// decoration
setType(Type.UTILITY);
setUndecorated(true);
setSize(200, 200);
toBack();
// position
// setLocation(100, 100);
setLocationRelativeTo(null); // centers on screen
// frame operations
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame shape (a star)
double points[][] = { { 0, 85 }, { 75, 75 }, { 100, 10 }, { 125, 75 }, { 200, 85 }, { 150, 125 }, { 160, 190 }, { 100, 150 }, { 40, 190 }, { 50, 125 }, { 0, 85 } };
GeneralPath star = new GeneralPath();
star.moveTo(points[0][0], points[0][1]);
for (int k = 1; k < points.length; k++)
star.lineTo(points[k][0], points[k][2]);
star.closePath();
setShape(star);
// embed fx into swing
JFXPanel fxPanel = new JFXPanel();
Widget.this.getContentPane().add(fxPanel);
Platform.runLater(new Runnable() {
@Override
public void run() {
// set scene in JFXPanel
fxPanel.setScene( createFxScene());
// show frame
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
Widget.this.setVisible(true);
// send it to the desktop, behind all other existing windows
// Widget.this.toBack();
// Widget.this.repaint();
}
});
}
});
}
private Scene createFxScene() {
StackPane rootPane = new StackPane();
rootPane.setBackground(Background.EMPTY);
// add some node
Label label = new Label("Bright & Shiny");
label.setTextFill(Color.RED);
rootPane.getChildren().add(label);
// create scene
Scene scene = new Scene(rootPane);
// gradient fill
RadialGradient radialGradient = new RadialGradient( 270, 0.8, 0.5, 0.5, 0.7, true, CycleMethod.NO_CYCLE, new Stop( .5f, Color.YELLOW), new Stop( .7f, Color.ORANGE), new Stop( .9f, Color.ORANGERED));
scene.setFill(radialGradient);
// context menu with close button
ContextMenu contextMenu = new ContextMenu();
MenuItem closeMenuItem = new MenuItem("Close");
closeMenuItem.setOnAction(actionEvent -> System.exit(0));
contextMenu.getItems().add(closeMenuItem);
// set context menu for scene
scene.setOnMousePressed(mouseEvent -> {
if (mouseEvent.isSecondaryButtonDown()) {
contextMenu.show(rootPane, mouseEvent.getScreenX(), mouseEvent.getScreenY());
}
});
// allow the frame to be dragged around
final DragContext dragDelta = new DragContext();
rootPane.setOnMousePressed(mouseEvent -> {
dragDelta.x = Widget.this.getLocation().getX() - mouseEvent.getScreenX();
dragDelta.y = Widget.this.getLocation().getY() - mouseEvent.getScreenY();
});
rootPane.setOnMouseDragged(mouseEvent -> Widget.this.setLocation((int) (mouseEvent.getScreenX() + dragDelta.x), (int) (mouseEvent.getScreenY() + dragDelta.y)));
return scene;
}
public static void main(String[] args) {
new Widget();
}
}
这是未显示在任务栏中的小部件的屏幕截图。用鼠标左键拖动它。鼠标右键提供带有关闭按钮的上下文菜单。
上面的代码使用了秋千架的形状。下面的代码使用了 javafx 控件的形状。
这是一个只有控件可见的版本。我用的是反射标签控件。
如果要将控件直接发送到桌面,则需要激活 toBack() 调用。您可以使用鼠标滚轮缩放控件。最大缩放尺寸限制为jframe的尺寸。
您需要为自定义控件做的就是实现 createFxControl() 中的代码
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.effect.Reflection;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class LabelWidget extends JFrame {
class DragContext {
double x;
double y;
}
public LabelWidget() {
// decoration
setType(Type.UTILITY);
setUndecorated(true);
// make frame transparent, we only want the control to be visible
setBackground(new java.awt.Color(0,0,0,0));
setSize(400, 400);
// position
// setLocation(100, 100);
setLocationRelativeTo(null); // centers on screen
// frame operations
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// embed fx into swing
JFXPanel fxPanel = new JFXPanel();
LabelWidget.this.getContentPane().add(fxPanel);
Platform.runLater(new Runnable() {
@Override
public void run() {
// set scene in JFXPanel
fxPanel.setScene( createFxScene());
// show frame
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
LabelWidget.this.setVisible(true);
// send it to the desktop, behind all other existing windows
// ClockWidget.this.toBack();
// ClockWidget.this.repaint();
}
});
}
});
}
private Scene createFxScene() {
StackPane rootPane = new StackPane();
// make pane transparent, we only want the control to be visible
rootPane.setBackground(Background.EMPTY);
// add control
Control control = createFxControl();
rootPane.getChildren().add( control);
// create scene
Scene scene = new Scene(rootPane);
// make scene transparent, we only want the control to be visible
scene.setFill( Color.TRANSPARENT);
// context menu with close button
ContextMenu contextMenu = new ContextMenu();
MenuItem closeMenuItem = new MenuItem("Close");
closeMenuItem.setOnAction(actionEvent -> System.exit(0));
contextMenu.getItems().add(closeMenuItem);
control.setContextMenu(contextMenu);
// allow the frame to be dragged around
makeDraggable( control);
// allow zooming
makeZoomable( control);
return scene;
}
/**
* Create the JavaFX control of which we use the shape.
* @return
*/
private Control createFxControl() {
Label label = new Label( "I'm a Label");
label.setFont(new Font("Tahoma", 24));
label.setEffect(new Reflection());
return label;
}
/**
* Allow dragging of the stage / control on the desktop
* @param control
* @param stage
*/
public void makeDraggable( Control control) {
final DragContext dragDelta = new DragContext();
control.setOnMousePressed(mouseEvent -> {
dragDelta.x = LabelWidget.this.getLocation().getX() - mouseEvent.getScreenX();
dragDelta.y = LabelWidget.this.getLocation().getY() - mouseEvent.getScreenY();
});
control.setOnMouseDragged(mouseEvent -> LabelWidget.this.setLocation((int) (mouseEvent.getScreenX() + dragDelta.x), (int) (mouseEvent.getScreenY() + dragDelta.y)));
}
/**
* Allow zooming
* @param control
*/
public void makeZoomable( Control control) {
// note: in order to make it larger, we'd have to resize the stage/frame => we limit the size to 1.0 for now and allow only making the control smaller
final double MAX_SCALE = 1.0;
final double MIN_SCALE = 0.1;
control.addEventFilter(ScrollEvent.ANY, new EventHandler<ScrollEvent>() {
@Override
public void handle(ScrollEvent event) {
double delta = 1.2;
double scale = control.getScaleX();
if (event.getDeltaY() < 0) {
scale /= delta;
} else {
scale *= delta;
}
scale = clamp(scale, MIN_SCALE, MAX_SCALE);
control.setScaleX(scale);
control.setScaleY(scale);
event.consume();
}
});
}
/**
* Limit bounds of value
* @param value
* @param min
* @param max
* @return
*/
public static double clamp( double value, double min, double max) {
if( Double.compare(value, min) < 0)
return min;
if( Double.compare(value, max) > 0)
return max;
return value;
}
public static void main(String[] args) {
new LabelWidget();
}
}
或者,如果您手头有 https://github.com/HanSolo/Enzo 的 Enzo 库,您可以使用此代码:
private Control createFxControl() {
// create a clock using the enzo library from https://github.com/HanSolo/Enzo
Clock clock = ClockBuilder.create()
// .prefSize(400, 400)
.design(Clock.Design.DB)
.running(true)
.text("Berlin")
.autoNightMode(true)
.build();
return clock;
}
创建这个:
找到了一个简单的方法: 创建一个实用程序 window 并使其完全透明,这样您就无法看到它或与之交互。然后创建想要的 window 并将所有者初始化为实用程序 window - 这将导致它不会出现在任务栏中。