2014-11-27 2 views
3


Мне нужно показать «непрерывную» цветовую палитру для выбора цвета внутри ContextMenu. Аналогично CustomColorDialog, который появляется на ColorPicker.
Есть ли другой класс для этой цели или можно обойтись, расширив ColorPicker и показывая непосредственно CustomColorDialog вместо того, чтобы сначала показывать ColorPicker.Отображать пользовательский диалог цвета напрямую - JavaFX ColorPicker

ColorPicker JavaFX 8

ТИА

ответ

10

Для начала, com.sun.javafx.scene.control.skin.CustomColorDialog является частным API, и это не рекомендуется использовать, так как она может измениться в будущем без предварительного уведомления.

Кроме того, это Dialog, что означает, что вы не можете вставить его в ContextMenu, у него есть собственное окно и оно модальное.

Это краткий пример использования этого (очень большого, не настраиваемого) диалогового окна в вашем приложении без использования 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 пакете.

Таким образом, нам нужен другой способ реализовать наш пользовательский селектор цветов. Если вы посмотрите на исходный код CustomColorDialog, вы увидите, что это относительно простой элемент управления и, самое главное, почти на основе общедоступного API: панелей, областей и цвета.

Попытка положить все в ContextMenu может быть излишним, поэтому я придумал этот базовый пример, где я просто использую левую часть диалога, отображая центральную панель сверху. Большая часть кода принадлежит классу. Стиль CSS был также взят из modena.css (под custom-color-dialog CSS-селектором), но был настроен, поскольку некоторые из узлов были повернуты на 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%); 
} 

Изображение можно найти here.

И, наконец, наш класс приложений.

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, чтобы нажав на цветовые селекторы, не закрывая контекстное меню. Чтобы закрыть его, просто щелкните в любом месте вне всплывающего окна.

Вот как это выглядит:

Custom Color Selector

На основании этого пользовательского диалога, вы можете улучшить его и добавить функциональные возможности, которые могут понадобиться.

0

Вот как я использую com.sun.javafx.scene.control.skin.CustomColorDialog:

public Color showColorDialog(String title, Color initialColor) { 

CountDownLatch countDownLatch = new CountDownLatch(1); 

ObjectHolder<Color> selectedColorHolder = new ObjectHolder<>(); 

Platform.runLater(new Runnable() { 
    @Override 
    public void run() { 
    try { 
     final CustomColorDialog customColorDialog = new CustomColorDialog(getWindow()); 
     customColorDialog.setCurrentColor(initialColor); 

     // remove save button 
     VBox controllBox = (VBox) customColorDialog.getChildren().get(1); 
     HBox buttonBox = (HBox) controllBox.getChildren().get(2); 
     buttonBox.getChildren().remove(0); 

     Runnable saveUseRunnable = new Runnable() { 
     @Override 
     public void run() { 
      try { 
      Field customColorPropertyField = CustomColorDialog.class 
       .getDeclaredField("customColorProperty"); //$NON-NLS-1$ 
      customColorPropertyField.setAccessible(true); 
      @SuppressWarnings("unchecked") 
      ObjectProperty<Color> customColorPropertyValue = (ObjectProperty<Color>) customColorPropertyField 
       .get(customColorDialog); 
      selectedColorHolder.setObject(customColorPropertyValue.getValue()); 
      } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { 
      LOG.error(e, e); 
      } 
     } 
     }; 

     customColorDialog.setOnUse(saveUseRunnable); 

     customColorDialog.setOnHidden(new EventHandler<WindowEvent>() { 
     @Override 
     public void handle(WindowEvent event) { 
      countDownLatch.countDown(); 
     } 
     }); 

     Field dialogField = CustomColorDialog.class.getDeclaredField("dialog"); //$NON-NLS-1$ 
     dialogField.setAccessible(true); 
     Stage dialog = (Stage) dialogField.get(customColorDialog); 

     dialog.setTitle(title); 
     customColorDialog.show(); 
     dialog.centerOnScreen(); 
    } catch (Exception e) { 
     LOG.error(e, e); 
     countDownLatch.countDown(); 
    } 
    } 
}); 

try { 
    countDownLatch.await(); 
} catch (InterruptedException e) { 
    LOG.error(e, e); 
} 

return selectedColorHolder.getObject(); 
} 
Смежные вопросы