如何将事件处理程序调用统一为两个堆叠的 JComponents
How to unify a Eventhandler call onto two stacked JComponents
问题:我有一个带有图标和 RolloverIcon(悬停效果)的 JButton,为此我添加了一个 via add()
包含写入该 JButton 的字符串的 JFXPanel。我怎样才能正确地将 Rollover 转发到 JFXPanel?因为目前我在 JFXPanel 中调用 setOnMouseEntered 来改变 JButton 的背景,但是从 Rollover JButton 事件到内部 JFXPanel setOnMouseEntered 的转换并不是无缝的。
更准确的解释:当我将鼠标从包装 JButton 悬停到内部 JFXPanel 上时,JButton 的滚动尝试将按钮上的图标恢复为非滚动版本。但与此同时,新调用的 setOnMouseEntered 尝试将 JButton 图标设置为滚动版本。导致视觉 Racecondition 或只是卡顿。
我把资源和src文件打包到这个Zip-File
I also have mini-video for better understanding
我的决议是什么:
- 有没有办法让 JFXPanel 完全点击通过所以我只需要父 Button 的滚动
- 可能会在 JButton-Class 中将 JFXPanel 渲染到图标或图像中,这样我就可以完全依赖滚动效果
主要-Class:
package main.testbench;
import java.awt.Color;
import java.awt.Dimension;
import java.net.URL;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class MainStarter implements UrlGetter{
static final double MULTI = 2.0;
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension((int) (400*MULTI),(int) (400*MULTI)));
frame.setMinimumSize(new Dimension((int) (400*MULTI),(int) (400*MULTI)));
frame.setVisible(true);
JPanel pnl = new JPanel();
pnl.setBackground(new Color(0, 255, 0));
pnl.add(new ButtonIconHover("Reisen", "test", new Dimension((int) (110*MULTI),(int) (40*MULTI))));
frame.add(pnl);
}
});
}
}
JButton-Class:
public class ButtonIconHover extends JButton implements UrlGetter {
public ButtonIconHover(String display, String action, Dimension dimension) {
super();
super.setOpaque(false);
super.setContentAreaFilled(false);
super.setBorderPainted(false);
super.setPreferredSize(dimension);
super.setMinimumSize(dimension);
ImageIcon imageIcon = new ImageIcon(getURL("resources/Button.jpg"));
ImageIcon imageIconHover = new ImageIcon(getURL("resources/Button_h.jpg"));
Image image = imageIcon.getImage();
Image newimg = image.getScaledInstance(dimension.width, dimension.height, java.awt.Image.SCALE_SMOOTH);
imageIcon = new ImageIcon(newimg);
Image imageHover = imageIconHover.getImage();
Image newimgHover = imageHover.getScaledInstance(dimension.width, dimension.height, java.awt.Image.SCALE_SMOOTH);
imageIconHover = new ImageIcon(newimgHover);
super.setIcon(imageIcon);
super.setRolloverIcon(imageIconHover);
super.setPressedIcon(imageIconHover);
super.setActionCommand(action);
add(new FXButtonFont(display, dimension, this));
}
}
JFXPanel-Class:
package main.testbench;
import java.awt.Dimension;
import javax.swing.Icon;
import javafx.embed.swing.JFXPanel;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.Light;
import javafx.scene.effect.Lighting;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
public class FXButtonFont extends JFXPanel implements UrlGetter{
public FXButtonFont(String display, Dimension dimension, ButtonIconHover buttonIconHover) {
super();
Light.Distant light = new Light.Distant();
light.setAzimuth(-45.0);
Lighting lighting = new Lighting();
lighting.setLight(light);
lighting.setSurfaceScale(1.5*MainStarter.MULTI);
lighting.setSpecularConstant(2.0);
lighting.setSpecularExponent(10.0);
Text text = new Text(0 ,0, display);
// text.setText(display);
text.setFill(Color.WHITE);
Font font = Font.loadFont(getURL("resources/fonts/Arson-Regular.ttf").toString(), 20*MainStarter.MULTI);
text.setFont(font);
DropShadow shadow = new DropShadow();
shadow.setSpread(0);
shadow.setColor(Color.web("0x000000", 0.7));
shadow.setOffsetX(-2);
shadow.setOffsetY(2);
shadow.setWidth(4);
shadow.setRadius(4);
shadow.setInput(lighting);
text.setEffect(shadow);
Icon imageIcon = buttonIconHover.getIcon();
Icon imageIconHover = buttonIconHover.getRolloverIcon();
text.setOnMouseEntered((event) -> {
buttonIconHover.setIcon(imageIconHover);
});
text.setOnMouseExited((event) -> {
buttonIconHover.setIcon(imageIcon);
});
//Setting the stage
StackPane root = new StackPane();
StackPane.setAlignment(text, Pos.CENTER);
StackPane.setMargin(text, new Insets(0, 0, 0, 0));
root.getChildren().add(text);
Scene scene = new Scene(root, dimension.getWidth(),dimension.getHeight(), Color.TRANSPARENT);
setPreferredSize(dimension);
setMinimumSize(dimension);
this.setScene(scene);
}
}
UrlGetter 接口:
package main.testbench;
import java.net.URL;
public interface UrlGetter {
public default URL getURL(String resource) {
return getClass().getClassLoader().getResource(resource);
}
}
为了将 Swing JButton 上的图标与 JavaFX 文本无缝结合,我执行了以下操作:
- 将 Icon 和 RolloverIcon 加载到
JButton
上,以便图像已经加载
- 因为awt
Image
是异步的,需要加载才能改变
- 在
ButtonIconHover
(扩展 JButton)构造函数中创建一个 FXButtonFont
(扩展 FXJPanel)对象
- 所有后续调用都在嵌套的
Platform.runLater
中进行,因为我们将需要 JavaFX 线程
- 在 JFXPanel
getScene
和场景 snapshot(WritableImage)
上调用
- 使用
SwingFXUtils.fromFXImage(WritableImage, null)
将WritableImage
转换为BufferedImage
- 然后调用自己编写的
mergeImages
将两个 BufferedImages
合并为一个
- 现在将返回的
BufferedImage
设置为 setIcon
到 JButton
这里是修改后的 ButtonIconHover:
public ButtonIconHover(String display, String action, Dimension dimension) {
super();
super.setOpaque(false);
super.setContentAreaFilled(false);
super.setBorderPainted(false);
super.setPreferredSize(dimension);
super.setMinimumSize(dimension);
ImageIcon imageIcon = new ImageIcon(getURL("resources/Button.jpg"));
ImageIcon imageIconHover = new ImageIcon(getURL("resources/Button_h.jpg"));
Image image = imageIcon.getImage();
Image newimg = image.getScaledInstance(dimension.width, dimension.height, java.awt.Image.SCALE_SMOOTH);
Image imageHover = imageIconHover.getImage();
Image newimgHover = imageHover.getScaledInstance(dimension.width, dimension.height, java.awt.Image.SCALE_SMOOTH);
super.setIcon(new ImageIcon(newimg));
super.setRolloverIcon(new ImageIcon(newimgHover));
FXButtonFont FXBtnString = new FXButtonFont(display, dimension, false);
FXButtonFont FXBtnStringHover = new FXButtonFont(display, dimension, true);
ButtonIconHover thisbtn = this;
Platform.runLater(new Runnable() {
@Override
public void run() {
WritableImage img = new WritableImage((int)dimension.getWidth(), (int)dimension.getHeight()) ;
WritableImage imgHover = new WritableImage((int)dimension.getWidth(), (int)dimension.getHeight()) ;
FXBtnString.getScene().snapshot(img);
FXBtnStringHover.getScene().snapshot(imgHover);
BufferedImage FXBtnStringConverted = SwingFXUtils.fromFXImage(img, null);
BufferedImage FXBtnStringConvertedHover = SwingFXUtils.fromFXImage(imgHover, null);
BufferedImage combined = ImageUtil.mergeImages(ImageUtil.convertToBufferedImage(newimg), FXBtnStringConverted);
BufferedImage combinedRollPress = ImageUtil.mergeImages(ImageUtil.convertToBufferedImage(newimgHover), FXBtnStringConvertedHover);
ImageIcon nonConverted = new ImageIcon(combined);
ImageIcon rollPress = new ImageIcon(combinedRollPress);
thisbtn.setIcon(nonConverted);
thisbtn.setRolloverIcon(rollPress);
thisbtn.setPressedIcon(rollPress);
}
});
}
问题:我有一个带有图标和 RolloverIcon(悬停效果)的 JButton,为此我添加了一个 via add() 包含写入该 JButton 的字符串的 JFXPanel。我怎样才能正确地将 Rollover 转发到 JFXPanel?因为目前我在 JFXPanel 中调用 setOnMouseEntered 来改变 JButton 的背景,但是从 Rollover JButton 事件到内部 JFXPanel setOnMouseEntered 的转换并不是无缝的。
更准确的解释:当我将鼠标从包装 JButton 悬停到内部 JFXPanel 上时,JButton 的滚动尝试将按钮上的图标恢复为非滚动版本。但与此同时,新调用的 setOnMouseEntered 尝试将 JButton 图标设置为滚动版本。导致视觉 Racecondition 或只是卡顿。
我把资源和src文件打包到这个Zip-File
I also have mini-video for better understanding
我的决议是什么:
- 有没有办法让 JFXPanel 完全点击通过所以我只需要父 Button 的滚动
- 可能会在 JButton-Class 中将 JFXPanel 渲染到图标或图像中,这样我就可以完全依赖滚动效果
主要-Class:
package main.testbench;
import java.awt.Color;
import java.awt.Dimension;
import java.net.URL;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class MainStarter implements UrlGetter{
static final double MULTI = 2.0;
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension((int) (400*MULTI),(int) (400*MULTI)));
frame.setMinimumSize(new Dimension((int) (400*MULTI),(int) (400*MULTI)));
frame.setVisible(true);
JPanel pnl = new JPanel();
pnl.setBackground(new Color(0, 255, 0));
pnl.add(new ButtonIconHover("Reisen", "test", new Dimension((int) (110*MULTI),(int) (40*MULTI))));
frame.add(pnl);
}
});
}
}
JButton-Class:
public class ButtonIconHover extends JButton implements UrlGetter {
public ButtonIconHover(String display, String action, Dimension dimension) {
super();
super.setOpaque(false);
super.setContentAreaFilled(false);
super.setBorderPainted(false);
super.setPreferredSize(dimension);
super.setMinimumSize(dimension);
ImageIcon imageIcon = new ImageIcon(getURL("resources/Button.jpg"));
ImageIcon imageIconHover = new ImageIcon(getURL("resources/Button_h.jpg"));
Image image = imageIcon.getImage();
Image newimg = image.getScaledInstance(dimension.width, dimension.height, java.awt.Image.SCALE_SMOOTH);
imageIcon = new ImageIcon(newimg);
Image imageHover = imageIconHover.getImage();
Image newimgHover = imageHover.getScaledInstance(dimension.width, dimension.height, java.awt.Image.SCALE_SMOOTH);
imageIconHover = new ImageIcon(newimgHover);
super.setIcon(imageIcon);
super.setRolloverIcon(imageIconHover);
super.setPressedIcon(imageIconHover);
super.setActionCommand(action);
add(new FXButtonFont(display, dimension, this));
}
}
JFXPanel-Class:
package main.testbench;
import java.awt.Dimension;
import javax.swing.Icon;
import javafx.embed.swing.JFXPanel;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.Light;
import javafx.scene.effect.Lighting;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
public class FXButtonFont extends JFXPanel implements UrlGetter{
public FXButtonFont(String display, Dimension dimension, ButtonIconHover buttonIconHover) {
super();
Light.Distant light = new Light.Distant();
light.setAzimuth(-45.0);
Lighting lighting = new Lighting();
lighting.setLight(light);
lighting.setSurfaceScale(1.5*MainStarter.MULTI);
lighting.setSpecularConstant(2.0);
lighting.setSpecularExponent(10.0);
Text text = new Text(0 ,0, display);
// text.setText(display);
text.setFill(Color.WHITE);
Font font = Font.loadFont(getURL("resources/fonts/Arson-Regular.ttf").toString(), 20*MainStarter.MULTI);
text.setFont(font);
DropShadow shadow = new DropShadow();
shadow.setSpread(0);
shadow.setColor(Color.web("0x000000", 0.7));
shadow.setOffsetX(-2);
shadow.setOffsetY(2);
shadow.setWidth(4);
shadow.setRadius(4);
shadow.setInput(lighting);
text.setEffect(shadow);
Icon imageIcon = buttonIconHover.getIcon();
Icon imageIconHover = buttonIconHover.getRolloverIcon();
text.setOnMouseEntered((event) -> {
buttonIconHover.setIcon(imageIconHover);
});
text.setOnMouseExited((event) -> {
buttonIconHover.setIcon(imageIcon);
});
//Setting the stage
StackPane root = new StackPane();
StackPane.setAlignment(text, Pos.CENTER);
StackPane.setMargin(text, new Insets(0, 0, 0, 0));
root.getChildren().add(text);
Scene scene = new Scene(root, dimension.getWidth(),dimension.getHeight(), Color.TRANSPARENT);
setPreferredSize(dimension);
setMinimumSize(dimension);
this.setScene(scene);
}
}
UrlGetter 接口:
package main.testbench;
import java.net.URL;
public interface UrlGetter {
public default URL getURL(String resource) {
return getClass().getClassLoader().getResource(resource);
}
}
为了将 Swing JButton 上的图标与 JavaFX 文本无缝结合,我执行了以下操作:
- 将 Icon 和 RolloverIcon 加载到
JButton
上,以便图像已经加载 - 因为awt
Image
是异步的,需要加载才能改变 - 在
ButtonIconHover
(扩展 JButton)构造函数中创建一个FXButtonFont
(扩展 FXJPanel)对象 - 所有后续调用都在嵌套的
Platform.runLater
中进行,因为我们将需要 JavaFX 线程 - 在 JFXPanel
getScene
和场景snapshot(WritableImage)
上调用
- 使用
SwingFXUtils.fromFXImage(WritableImage, null)
将WritableImage
转换为BufferedImage
- 然后调用自己编写的
mergeImages
将两个BufferedImages
合并为一个 - 现在将返回的
BufferedImage
设置为setIcon
到JButton
这里是修改后的 ButtonIconHover:
public ButtonIconHover(String display, String action, Dimension dimension) {
super();
super.setOpaque(false);
super.setContentAreaFilled(false);
super.setBorderPainted(false);
super.setPreferredSize(dimension);
super.setMinimumSize(dimension);
ImageIcon imageIcon = new ImageIcon(getURL("resources/Button.jpg"));
ImageIcon imageIconHover = new ImageIcon(getURL("resources/Button_h.jpg"));
Image image = imageIcon.getImage();
Image newimg = image.getScaledInstance(dimension.width, dimension.height, java.awt.Image.SCALE_SMOOTH);
Image imageHover = imageIconHover.getImage();
Image newimgHover = imageHover.getScaledInstance(dimension.width, dimension.height, java.awt.Image.SCALE_SMOOTH);
super.setIcon(new ImageIcon(newimg));
super.setRolloverIcon(new ImageIcon(newimgHover));
FXButtonFont FXBtnString = new FXButtonFont(display, dimension, false);
FXButtonFont FXBtnStringHover = new FXButtonFont(display, dimension, true);
ButtonIconHover thisbtn = this;
Platform.runLater(new Runnable() {
@Override
public void run() {
WritableImage img = new WritableImage((int)dimension.getWidth(), (int)dimension.getHeight()) ;
WritableImage imgHover = new WritableImage((int)dimension.getWidth(), (int)dimension.getHeight()) ;
FXBtnString.getScene().snapshot(img);
FXBtnStringHover.getScene().snapshot(imgHover);
BufferedImage FXBtnStringConverted = SwingFXUtils.fromFXImage(img, null);
BufferedImage FXBtnStringConvertedHover = SwingFXUtils.fromFXImage(imgHover, null);
BufferedImage combined = ImageUtil.mergeImages(ImageUtil.convertToBufferedImage(newimg), FXBtnStringConverted);
BufferedImage combinedRollPress = ImageUtil.mergeImages(ImageUtil.convertToBufferedImage(newimgHover), FXBtnStringConvertedHover);
ImageIcon nonConverted = new ImageIcon(combined);
ImageIcon rollPress = new ImageIcon(combinedRollPress);
thisbtn.setIcon(nonConverted);
thisbtn.setRolloverIcon(rollPress);
thisbtn.setPressedIcon(rollPress);
}
});
}