直接显示自定义颜色对话框-JavaFX ColorPicker [英] Display custom color dialog directly- JavaFX ColorPicker

查看:105
本文介绍了直接显示自定义颜色对话框-JavaFX ColorPicker的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


我需要显示一个连续"调色板,以便在ContextMenu中进行颜色选择.类似于在 ColorPicker .
是否为此目的使用其他类,或者可以通过扩展ColorPicker并直接显示CustomColorDialog而不是首先显示ColorPicker来解决此问题.


I need to show a "continuous" color palette for color selection inside a ContextMenu. Similar to CustomColorDialog that pops up on ColorPicker.
Is there a different class for this purpose or is it possible to work around by extending ColorPicker and showing directly CustomColorDialog instead of first showing ColorPicker.

TIA

推荐答案

对于初学者来说,com.sun.javafx.scene.control.skin.CustomColorDialog是私有API,不建议使用它,因为将来可能会更改,恕不另行通知.

For starters, com.sun.javafx.scene.control.skin.CustomColorDialog is private API, and it's not advisable to use it, as it may change in the future without notice.

此外,它是Dialog,这意味着您不能将其嵌入到ContextMenu中,它具有自己的窗口并且是模态的.

Besides, it is a Dialog, what means you can't embed it into a ContextMenu, it has its own window and it's modal.

这是在应用程序中不使用ColorPicker的情况下使用此对话框(非常大,不可自定义)的简短示例.

This is a short example of using this (very big, not customizable) dialog in your application, without using a ColorPicker.

@Override
public void start(Stage primaryStage) {
    Button btn = new Button();
    btn.setText("Open Custom Color Dialog");
    btn.setOnAction(e -> {
        CustomColorDialog dialog = new CustomColorDialog(primaryStage.getOwner());
        dialog.show();
    });

    Scene scene = new Scene(new StackPane(btn), 300, 250);

    primaryStage.setTitle("CustomColorDialog");
    primaryStage.setScene(scene);
    primaryStage.show();
}

您将看到对话框,但是您将无法发送自定义颜色或检索选定的颜色,因为customColorProperty()之类的属性仅在com.sun.javafx.scene.control.skin包中可用.

You'll get the dialog, but you won't get any possibility to send a custom color or retrieve the selected color, since properties like customColorProperty() are only accesible within the com.sun.javafx.scene.control.skin package.

因此,我们需要另一种方式来实现我们的自定义颜色选择器.如果您查看CustomColorDialog的源代码,您会发现它是一个相对简单的控件,最重要的是,它几乎基于公共API:窗格,区域和颜色.

So we need another way to implement our custom color selector. If you have a look at the source code of CustomColorDialog you'll see that it's relatively a simple control, and most important, almost based on public API: panes, regions and color.

尝试将所有内容放在ContextMenu中可能会过大,因此,我提出了这个基本示例,在该示例中,我将仅使用对话框的左侧部分,在顶部显示中心栏.大多数代码来自该类. CSS样式也来自modena.css(在custom-color-dialog CSS选择器下),但是由于某些节点旋转了90º而被定制.

Trying to put all in a ContextMenu could be overkilling, so I've come up with this basic example, where I'll just use the left part of the dialog, displaying the central bar on top. Most of the code is from the class. The CSS styling was also taken from modena.css (under custom-color-dialog CSS selector), but was customized as some of the nodes were rotated 90º.

这是CustomColorDialog类的简短版本:

public class MyCustomColorPicker extends VBox {

    private final ObjectProperty<Color> currentColorProperty = 
        new SimpleObjectProperty<>(Color.WHITE);
    private final ObjectProperty<Color> customColorProperty = 
        new SimpleObjectProperty<>(Color.TRANSPARENT);

    private Pane colorRect;
    private final Pane colorBar;
    private final Pane colorRectOverlayOne;
    private final Pane colorRectOverlayTwo;
    private Region colorRectIndicator;
    private final Region colorBarIndicator;
    private Pane newColorRect;

    private DoubleProperty hue = new SimpleDoubleProperty(-1);
    private DoubleProperty sat = new SimpleDoubleProperty(-1);
    private DoubleProperty bright = new SimpleDoubleProperty(-1);

    private DoubleProperty alpha = new SimpleDoubleProperty(100) {
        @Override protected void invalidated() {
            setCustomColor(new Color(getCustomColor().getRed(), getCustomColor().getGreen(), 
                    getCustomColor().getBlue(), clamp(alpha.get() / 100)));
        }
    };

    public MyCustomColorPicker() {

        getStyleClass().add("my-custom-color");

        VBox box = new VBox();

        box.getStyleClass().add("color-rect-pane");
        customColorProperty().addListener((ov, t, t1) -> colorChanged());

        colorRectIndicator = new Region();
        colorRectIndicator.setId("color-rect-indicator");
        colorRectIndicator.setManaged(false);
        colorRectIndicator.setMouseTransparent(true);
        colorRectIndicator.setCache(true);

        final Pane colorRectOpacityContainer = new StackPane();

        colorRect = new StackPane();
        colorRect.getStyleClass().addAll("color-rect", "transparent-pattern");

        Pane colorRectHue = new Pane();
        colorRectHue.backgroundProperty().bind(new ObjectBinding<Background>() {

            {
                bind(hue);
            }

            @Override protected Background computeValue() {
                return new Background(new BackgroundFill(
                        Color.hsb(hue.getValue(), 1.0, 1.0), 
                        CornerRadii.EMPTY, Insets.EMPTY));

            }
        });            

        colorRectOverlayOne = new Pane();
        colorRectOverlayOne.getStyleClass().add("color-rect");
        colorRectOverlayOne.setBackground(new Background(new BackgroundFill(
                new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE, 
                new Stop(0, Color.rgb(255, 255, 255, 1)), 
                new Stop(1, Color.rgb(255, 255, 255, 0))), 
                CornerRadii.EMPTY, Insets.EMPTY)));

        EventHandler<MouseEvent> rectMouseHandler = event -> {
            final double x = event.getX();
            final double y = event.getY();
            sat.set(clamp(x / colorRect.getWidth()) * 100);
            bright.set(100 - (clamp(y / colorRect.getHeight()) * 100));
            updateHSBColor();
        };

        colorRectOverlayTwo = new Pane();
        colorRectOverlayTwo.getStyleClass().addAll("color-rect");
        colorRectOverlayTwo.setBackground(new Background(new BackgroundFill(
                new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE, 
                new Stop(0, Color.rgb(0, 0, 0, 0)), new Stop(1, Color.rgb(0, 0, 0, 1))), 
                CornerRadii.EMPTY, Insets.EMPTY)));
        colorRectOverlayTwo.setOnMouseDragged(rectMouseHandler);
        colorRectOverlayTwo.setOnMousePressed(rectMouseHandler);

        Pane colorRectBlackBorder = new Pane();
        colorRectBlackBorder.setMouseTransparent(true);
        colorRectBlackBorder.getStyleClass().addAll("color-rect", "color-rect-border");

        colorBar = new Pane();
        colorBar.getStyleClass().add("color-bar");
        colorBar.setBackground(new Background(new BackgroundFill(createHueGradient(), 
                CornerRadii.EMPTY, Insets.EMPTY)));

        colorBarIndicator = new Region();
        colorBarIndicator.setId("color-bar-indicator");
        colorBarIndicator.setMouseTransparent(true);
        colorBarIndicator.setCache(true);

        colorRectIndicator.layoutXProperty().bind(
            sat.divide(100).multiply(colorRect.widthProperty()));
        colorRectIndicator.layoutYProperty().bind(
            Bindings.subtract(1, bright.divide(100)).multiply(colorRect.heightProperty()));
        colorBarIndicator.layoutXProperty().bind(
            hue.divide(360).multiply(colorBar.widthProperty()));
        colorRectOpacityContainer.opacityProperty().bind(alpha.divide(100));

        EventHandler<MouseEvent> barMouseHandler = event -> {
            final double x = event.getX();
            hue.set(clamp(x / colorRect.getWidth()) * 360);
            updateHSBColor();
        };

        colorBar.setOnMouseDragged(barMouseHandler);
        colorBar.setOnMousePressed(barMouseHandler);

        newColorRect = new Pane();
        newColorRect.getStyleClass().add("color-new-rect");
        newColorRect.setId("new-color");
        newColorRect.backgroundProperty().bind(new ObjectBinding<Background>() {
            {
                bind(customColorProperty);
            }
            @Override protected Background computeValue() {
                return new Background(new BackgroundFill(customColorProperty.get(), CornerRadii.EMPTY, Insets.EMPTY));
            }
        });

        colorBar.getChildren().setAll(colorBarIndicator);
        colorRectOpacityContainer.getChildren().setAll(colorRectHue, colorRectOverlayOne, colorRectOverlayTwo);
        colorRect.getChildren().setAll(colorRectOpacityContainer, colorRectBlackBorder, colorRectIndicator);
        VBox.setVgrow(colorRect, Priority.SOMETIMES);
        box.getChildren().addAll(colorBar, colorRect, newColorRect);

        getChildren().add(box);

        if (currentColorProperty.get() == null) {
            currentColorProperty.set(Color.TRANSPARENT);
        }
        updateValues();

    }

    private void updateValues() {
        hue.set(getCurrentColor().getHue());
        sat.set(getCurrentColor().getSaturation()*100);
        bright.set(getCurrentColor().getBrightness()*100);
        alpha.set(getCurrentColor().getOpacity()*100);
        setCustomColor(Color.hsb(hue.get(), clamp(sat.get() / 100), 
                clamp(bright.get() / 100), clamp(alpha.get()/100)));
    }

    private void colorChanged() {
        hue.set(getCustomColor().getHue());
        sat.set(getCustomColor().getSaturation() * 100);
        bright.set(getCustomColor().getBrightness() * 100);
    }

    private void updateHSBColor() {
        Color newColor = Color.hsb(hue.get(), clamp(sat.get() / 100), 
                        clamp(bright.get() / 100), clamp(alpha.get() / 100));
        setCustomColor(newColor);
    }

    @Override 
    protected void layoutChildren() {
        super.layoutChildren();            
        colorRectIndicator.autosize();
    }

    static double clamp(double value) {
        return value < 0 ? 0 : value > 1 ? 1 : value;
    }

    private static LinearGradient createHueGradient() {
        double offset;
        Stop[] stops = new Stop[255];
        for (int x = 0; x < 255; x++) {
            offset = (double)((1.0 / 255) * x);
            int h = (int)((x / 255.0) * 360);
            stops[x] = new Stop(offset, Color.hsb(h, 1.0, 1.0));
        }
        return new LinearGradient(0f, 0f, 1f, 0f, true, CycleMethod.NO_CYCLE, stops);
    }

    public void setCurrentColor(Color currentColor) {
        this.currentColorProperty.set(currentColor);
        updateValues();
    }

    Color getCurrentColor() {
        return currentColorProperty.get();
    }

    final ObjectProperty<Color> customColorProperty() {
        return customColorProperty;
    }

    void setCustomColor(Color color) {
        customColorProperty.set(color);
    }

    Color getCustomColor() {
        return customColorProperty.get();
    }
}

这是color.css文件:

.context-menu{
    -fx-background-color: derive(#ececec,26.4%);
}
.menu-item:focused {
    -fx-background-color: transparent;
}

/* CUSTOM COLOR */

.my-custom-color {
    -fx-background-color: derive(#ececec,26.4%);
    -fx-padding: 1.25em;
    -fx-spacing: 1.25em;
    -fx-min-width: 20em;
    -fx-pref-width: 20em;
    -fx-max-width: 20em;
}

.my-custom-color:focused,
.my-custom-color:selected {
    -fx-background-color: transparent;
}

.my-custom-color > .color-rect-pane {
    -fx-spacing: 0.75em;
    -fx-pref-height: 16.666667em;
    -fx-alignment: top-left;
    -fx-fill-height: true;
}

.my-custom-color .color-rect-pane .color-rect {
    -fx-min-width: 16.666667em;
    -fx-min-height: 16.666667em;
}

.my-custom-color .color-rect-pane .color-rect-border {
    -fx-border-color: derive(#ececec, -20%);
}

.my-custom-color > .color-rect-pane #color-rect-indicator {
    -fx-background-color: null;
    -fx-border-color: white;
    -fx-border-radius: 0.4166667em;
    -fx-translate-x: -0.4166667em;
    -fx-translate-y: -0.4166667em;
    -fx-pref-width: 0.833333em;
    -fx-pref-height: 0.833333em;
    -fx-effect: dropshadow(three-pass-box, black, 2, 0.0, 0, 1);
}

.my-custom-color > .color-rect-pane > .color-bar {
    -fx-min-height: 1.666667em;
    -fx-min-width: 16.666667em;
    -fx-max-height: 1.666667em;
    -fx-border-color: derive(#ececec, -20%);
}

.my-custom-color > .color-rect-pane > .color-bar > #color-bar-indicator {
    -fx-border-radius: 0.333333em;
    -fx-border-color: white;
    -fx-effect: dropshadow(three-pass-box, black, 2, 0.0, 0, 1);
    -fx-pref-height: 2em;
    -fx-pref-width: 0.833333em;
    -fx-translate-y: -0.1666667em;
    -fx-translate-x: -0.4166667em;
}

.my-custom-color .transparent-pattern {
    -fx-background-image: url("pattern-transparent.png"); 
    -fx-background-repeat: repeat;
    -fx-background-size: auto;
}

.my-custom-color .color-new-rect {
    -fx-min-width: 10.666667em;
    -fx-min-height: 1.75em;
    -fx-pref-width: 10.666667em;
    -fx-pref-height: 1.75em;
    -fx-border-color: derive(#ececec, -20%);
}

可以找到该图像最后是我们的应用程序类.

And finally, our application class.

public class CustomColorContextMenu extends Application {

    private final ObjectProperty<Color> sceneColorProperty = 
        new SimpleObjectProperty<>(Color.WHITE);

    @Override
    public void start(Stage primaryStage) {

        Rectangle rect = new Rectangle(400,400);
        rect.fillProperty().bind(sceneColorProperty);

        Scene scene = new Scene(new StackPane(rect), 400, 400);
        scene.getStylesheets().add(getClass().getResource("color.css").toExternalForm());
        scene.setOnMouseClicked(e->{
            if(e.getButton().equals(MouseButton.SECONDARY)){
                MyCustomColorPicker myCustomColorPicker = new MyCustomColorPicker();
                myCustomColorPicker.setCurrentColor(sceneColorProperty.get());

                CustomMenuItem itemColor = new CustomMenuItem(myCustomColorPicker);
                itemColor.setHideOnClick(false);
                sceneColorProperty.bind(myCustomColorPicker.customColorProperty());
                ContextMenu contextMenu = new ContextMenu(itemColor);
                contextMenu.setOnHiding(t->sceneColorProperty.unbind());
                contextMenu.show(scene.getWindow(),e.getScreenX(),e.getScreenY());
            }
        });

        primaryStage.setTitle("Custom Color Selector");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

请注意使用CustomMenuItem允许在不关闭上下文菜单的情况下单击颜色选择器.要关闭它,只需在弹出窗口之外的任何地方单击即可.

Note the use of CustomMenuItem to allow clicking on the color selectors without closing the context menu. To close it just click anywhere outside the popup window.

它是这样的:

基于此自定义对话框,您可以对其进行改进并添加您可能需要的功能.

Based on this custom dialog, you can improve it and add the functionality you may need.

这篇关于直接显示自定义颜色对话框-JavaFX ColorPicker的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆