【问题标题】:How to change or override the tooltip in JavaFX ColorPicker如何更改或覆盖 JavaFX ColorPicker 中的工具提示
【发布时间】:2015-01-13 07:17:45
【问题描述】:

我在我的应用程序中使用 JavaFX ColorPicker。根据我的要求,我已将颜色选择器上的默认颜色映射到一个数字。我希望这个数字在悬停在颜色而不是颜色的十六进制值上时显示为工具提示。我怎样才能做到这一点?

//部分代码

public void handleNodes(Circle circularNode) {

    final Delta offset = new Delta();

    circularNode.setOnMouseEntered(new EventHandler<MouseEvent>() {
        @Override 
        public void handle(MouseEvent event) {
            ((Circle)(event.getSource())).setCursor(Cursor.HAND);
        }
    });

    circularNode.setOnMousePressed(new EventHandler<MouseEvent>() {
        @Override 
        public void handle(MouseEvent event) {
                    if(event.getButton().equals(MouseButton.SECONDARY)) {
                System.out.println("Right click");

                Circle parent = ((Circle)(event.getSource()));

                final ContextMenu contextMenu = new ContextMenu();
                MenuItem editLabel = new MenuItem("Edit Label");
                editLabel.setOnAction(new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(ActionEvent event) {
                      System.out.println("Edit Label");

                      final ColorPicker colorPicker = new ColorPicker();
                      colorPicker.setStyle("-fx-border-radius: 10 10 10 10;" 
                                            + "-fx-background-radius: 10 10 10 10;");
                      colorPicker.setValue((Color) parent.getFill());
                                      colorPicker.showingProperty().addListener((obs,b,b1)->{
                          if(b1){
                              PopupWindow popupWindow = getPopupWindow();
                              javafx.scene.Node popup =            popupWindow.getScene().getRoot().getChildrenUnmodifiable().get(0);
                              popup.lookupAll(".color-rect").stream()
                                  .forEach(rect->{
                                      Color c = (Color)((Rectangle)rect).getFill();
                                      Tooltip.install(rect.getParent(), new Tooltip("Custom tip for "+c.toString()));
                                  });
                          }
                      });

                      panelMain.getChildren().add(colorPicker);                       
                    }
                });

【问题讨论】:

    标签: javafx-8 color-picker


    【解决方案1】:

    根据我第一次回答后 OP 发布的代码,并且由于所解决问题的实质性变化,我添加了一个涵盖两种情况的新答案:

    • ColorPicker 嵌入在主场景中,作为常规节点
    • ColorPicker 嵌入在 ContextMenu

    在第二种情况下,第一种情况的建议解决方案不再有效,因为找到的窗口将是带有上下文菜单的窗口。

    需要一个任务来继续寻找窗口,直到找到带有ComboBoxPopupControl 的窗口。

    这是一个完整的可运行示例:

    public class ColorPickerFinder extends Application {
    
        ExecutorService findWindowExecutor = createExecutor("FindWindow");
    
        @Override
        public void start(Stage primaryStage) {
    
            AnchorPane panCircles = new AnchorPane();
            Scene scene = new Scene(panCircles, 400, 400);
    
            final Random random = new Random();
            IntStream.range(0,5).boxed().forEach(i->{
                final Circle circle= new Circle(20+random.nextInt(80), 
                    Color.rgb(random.nextInt(255),random.nextInt(255),random.nextInt(255)));
                circle.setTranslateX(100+random.nextInt(200));
                circle.setTranslateY(100+random.nextInt(200));
                panCircles.getChildren().add(circle);
            });
            panCircles.setPrefSize(400, 400);
            ColorPicker colorPicker = new ColorPicker();
    
            panCircles.getChildren().add(colorPicker);
            primaryStage.setScene(scene);
            primaryStage.show();
    
            // We add listeners AFTER showing the stage, as we are looking for nodes 
            // by css selectors, these will be available only after the stage is shown
            colorPicker.showingProperty().addListener((obs,b,b1)->{
                if(b1){
                    // No need for task in this case
                    getPopupWindow();
                }
            });
    
            panCircles.getChildren().stream()
                    .filter(c->c instanceof Circle)
                    .map(c->(Circle)c)
                    .forEach(circle->{
                circle.setOnMouseClicked(e->{
                    if(e.getButton().equals(MouseButton.SECONDARY)){
                        // We need a task, since the first window found is the ContextMenu one
                        findWindowExecutor.execute(new WindowTask());
    
                        final ColorPicker picker = new ColorPicker();
                        picker.setStyle("-fx-border-radius: 10 10 10 10;" 
                                              + "-fx-background-radius: 10 10 10 10;");
                        picker.setValue((Color)(circle.getFill()));
                        picker.valueProperty().addListener((obs,c0,c1)->circle.setFill(c1));
    
                        final ContextMenu contextMenu = new ContextMenu();
                        MenuItem editLabel = new MenuItem();
                        contextMenu.getItems().add(editLabel);
                        editLabel.setGraphic(picker);
                        contextMenu.show(panCircles,e.getScreenX(),e.getScreenY());
                    }
                });
            });
    
        }
    
        private PopupWindow getPopupWindow() {
            @SuppressWarnings("deprecation") 
            final Iterator<Window> windows = Window.impl_getWindows();
    
            while (windows.hasNext()) {
                final Window window = windows.next();
                if (window instanceof PopupWindow) {
                    if(window.getScene()!=null && window.getScene().getRoot()!=null){ 
                        Parent root = window.getScene().getRoot();
                        if(root.getChildrenUnmodifiable().size()>0){
                            Node popup = root.getChildrenUnmodifiable().get(0);
                            if(popup.lookup(".combo-box-popup")!=null){
    
                                // only process ComboBoxPopupControl
                                Platform.runLater(()->{
                                    popup.lookupAll(".color-rect").stream()
                                        .forEach(rect->{
                                            Color c = (Color)((Rectangle)rect).getFill();
                                            Tooltip.install(rect.getParent(), 
                                                new Tooltip("Custom tip for "+c.toString()));
                                        });
                                });
                                return (PopupWindow)window;
                            }
                        }
                    }
    
                    return null;
                }
            }
            return null;
        }
    
        private class WindowTask extends Task<Void> {
    
            @Override
            protected Void call() throws Exception {
                boolean found=false;
                while(!found){
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                    }
                    found=(getPopupWindow()!=null);
                }
                return null;
            }
    
        } 
    
        private ExecutorService createExecutor(final String name) {
            ThreadFactory factory = r -> {
                Thread t = new Thread(r);
                t.setName(name);
                t.setDaemon(true);
                return t;
            };
            return Executors.newSingleThreadExecutor(factory);
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
    }
    

    这将是右键单击一个圆圈并单击颜色选择器后的结果:

    【讨论】:

    • 绝对完美!!我自己做不到。谢谢!!
    • 谢谢,这真的是一个非常难的问题......而且这是一个非常老套的答案。顺便说一句,不要点击自定义颜色,有一个错误,它会给你一个 NPE。也许你可以尝试禁用它。
    • 哦,好的部分是我已禁用该链接。我只需要颜色选择器上的默认颜色。所以现在一切都按照我想要的方式进行。
    【解决方案2】:

    这真是一个老生常谈的答案。

    第一个问题:弹出节点一出现就必须在场景中找到。但你不会……因为它不在同一个窗口中!

    深入了解ScenicView 是如何做到的,诀窍是获取当时的窗口列表,但使用了一种已弃用的方法:

    private PopupWindow getPopupWindow() {
        @SuppressWarnings("deprecation") final Iterator<Window> windows = Window.impl_getWindows();
        while (windows.hasNext()) {
            final Window window = windows.next();
            if (window instanceof PopupWindow) {
                return (PopupWindow)window;
            }
        }
        return null;
    }
    

    弹出窗口后,我们现在可以使用 lookupAll 和 CSS 选择器 color-rect 检查所有 Rectangle 节点,以获取它们的颜色,并将工具提示安装在其父容器上:

    @Override
    public void start(Stage primaryStage) {
        ColorPicker picker = new ColorPicker();
        StackPane root = new StackPane(picker);
    
        Scene scene = new Scene(root, 500, 400);
    
        primaryStage.setScene(scene);
        primaryStage.show();
        picker.showingProperty().addListener((obs,b,b1)->{
            if(b1){
                PopupWindow popupWindow = getPopupWindow();
                Node popup = popupWindow.getScene().getRoot().getChildrenUnmodifiable().get(0);
                popup.lookupAll(".color-rect").stream()
                    .forEach(rect->{
                        Color c = (Color)((Rectangle)rect).getFill();
                        Tooltip.install(rect.getParent(), new Tooltip("Custom tip for "+c.toString()));
                    });
            }
        });
    }
    

    这就是它的样子:

    【讨论】:

    • 是的……问题出在 Window.impl_getWindows();它给null。有什么替代方法吗?
    • 它已被弃用,但它在 JDK 8u25 下为我工作。有什么问题?有什么例外吗?
    • 我只是从 Window.impl_getWindows() 得到一个窗口,而不是 PopupWindow。所以该方法返回null。
    • 如果我在显示舞台后调用getPopupWindow(),它会给出主舞台,点击颜色选择器后会弹出这个窗口:com.sun.javafx.scene.control.skin.ComboBoxPopupControl$1@1d0fc4ab。你是在一个孤立的新项目上运行它吗?试试看它是否适用于您的系统。
    • 好的,让我再试一次......这是现有项目的一部分......基本上一旦舞台打开,我就有一个圆圈,点击它就会打开这个颜色选择器......我已经完成了正如你所展示的......但它仍然检测到一个窗口 - javafx.stage.Stage@40a02f62
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-21
    • 2018-01-15
    • 2019-08-16
    • 1970-01-01
    • 2023-03-05
    • 2014-10-06
    • 2020-11-29
    相关资源
    最近更新 更多