如何将 Eventhandler 调用统一到两个堆叠的 JComponents 上

How to unify a Eventhandler call onto two stacked JComponents

提问人:Georodin 提问时间:5/4/2021 更新时间:5/5/2021 访问量:32

问:

问题:我有一个带有 Icon 和 RolloverIcon(Hover effect) 的 JButon,为此我添加了一个 via add() 一个 JFXPanel,其中包含写入该 JButton 的字符串。如何正确地将展期转发到 JFXPanel?因为目前我在 JFXPanel 中调用 setOnMouseEntered 来更改 JButton 的背景,但从 Rollover JButton 事件到内部 JFXPanel setOnMouseEntered 的过渡并不是无缝的。

更精确的解释:当我将鼠标从包装器 JButton 悬停到内部 JFXPanel 上时,JButton 的翻转会尝试将按钮上的图标恢复为非翻转版本。但与此同时,新调用的 setOnMouseEntered 尝试将 JButton Icon 设置为 Rollover 版本。导致视觉 Race条件或只是口吃。

我将资源和 src 文件打包到这个 Zip 文件中

我还有迷你视频,以便更好地理解

我心目中的决议:

  • 有没有办法让 JFXPanel 完全点击,所以我只需要父按钮的翻转
  • 也许在 JButton 类中将 JFXPanel 渲染成图标或图像,这样我就可以纯粹依赖翻转效果

主类:

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级:

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类:

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);
    }
}
Java Swing 事件 JavaFX

评论


答:

0赞 Georodin 5/5/2021 #1

为了将 Swing JButton 上的图标与 JavaFX 文本无缝结合,我做了以下操作:

  1. 将 Icon 和 RolloverIcon 加载到上,以便图像已加载JButton
  2. 因为 AWT 是异步的,需要加载才能更改Image
  3. 在 (extends JButton) 构造函数中创建一个 (extends FXJPanel) 对象FXButtonFontButtonIconHover
  4. 所有后续调用都是在嵌套中进行的,因为我们需要 JavaFX 线程Platform.runLater
  5. 调用 JFXPanel 和现场getScenesnapshot(WritableImage)
  6. 用于将 转换为SwingFXUtils.fromFXImage(WritableImage, null)WritableImageBufferedImage
  7. 然后调用自写的 wich 将两者合二为一mergeImagesBufferedImages
  8. 现在将返回的设置为BufferedImagesetIconJButton

这里修改了 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);
        }
    });  
    
}