1

I want you to be able to see how many times the button was pressed and I would like to make these ads with colors. Short explanation light green the button was pressed once and dark red the button was pressed very often.

I have solved this solution of the problem with a simple if.  

If so (clicksCounter == 1) then green

But this solution is not very elegant and I would need many if queries from light green to dark red. Now my question is someone knows a way how I can change the color of light green to dark red fluently?

enter image description here

By fluent I mean that it takes from light green to dark green and the "values in between" to make it look more beautiful.

And not, as in my example, only green, yellow and red, but that the color change should be smooth.

enter image description here enter image description here enter image description here


Main.java

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 600, 400));
        primaryStage.show();
    }


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

Controller.java

package sample;


import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.DateCell;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;

import javax.security.auth.callback.Callback;
import java.net.URL;
import java.time.LocalDate;
import java.util.Date;
import java.util.ResourceBundle;

public class Controller implements Initializable {

    private int clicksCounter = 0;
    private String date = String.valueOf(LocalDate.now().getDayOfMonth());

    @FXML
    private Button button2;

    @FXML
    private DatePicker datePicker;

    @FXML
    private Label lbColor;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        button2.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                System.out.println(clicksCounter);
                clicksCounter++;
                if(clicksCounter == 1 ) {
                    lbColor.setStyle("-fx-background-color: #04b404;");
                }
                else if(clicksCounter == 2) {
                    lbColor.setStyle("-fx-background-color: #ffff13;");
                }
                else {
                    lbColor.setStyle("-fx-background-color: #df0101;");
                }

            }
        });

        javafx.util.Callback<DatePicker, DateCell> set = new javafx.util.Callback<DatePicker, DateCell>() {
            @Override
            public DateCell call(final DatePicker datePicker) {
                return new DateCell() {
                    @Override public void updateItem(LocalDate item, boolean empty) {
                        super.updateItem(item, empty);
                        //if today, change text and style
                        if (item.equals(LocalDate.now())) {
                            setText(date +"/" + clicksCounter);
                            if(clicksCounter == 1 ) {
                                setStyle("-fx-background-color: #04b404;");
                            }
                            else if(clicksCounter == 2) {
                                setStyle("-fx-background-color: #ffff13;");
                            }
                            else {
                                setStyle("-fx-background-color: #df0101;");
                            }
                        }
                    }
                };
            }
        };
        datePicker.setDayCellFactory(set);


    }
}

sample.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.DatePicker?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
   <children>
      <Button fx:id="button2" layoutX="280.0" layoutY="48.0" mnemonicParsing="false" text="Click" />
      <DatePicker fx:id="datePicker" layoutX="45.0" layoutY="56.0" />
      <Label fx:id="lbColor" layoutX="191.0" layoutY="155.0" text="Color" />
   </children>
</AnchorPane>

Thank you in advance.

Joe Mau
  • 59
  • 6

2 Answers2

2

Color implements the Interpolatable<Color> interface, so the API can do the interpolation for you.

public class Controller implements Initializable {
    private static final Color GREEN = Color.web("#04b404");
    private static final Color YELLOW = Color.web("#ffff13");
    private static final Color RED = Color.web("#df0101");

    private static final double INCREMENT_STEP = 0.3d; // Adjust this

    private final DoubleProperty step = new SimpleDoubleProperty();
    private final ObjectBinding<Color> backgroundColor;

    public Controller {
        backgroundColor = Bindings.createObjectBinding(() -> {
            final double value = step.get() % 3.0; // We get modulus so that 0 <= value < 3

            switch((int) value) { // We will round down the value
                case 0:
                    return GREEN.interpolate(YELLOW, value % 1.0); // Get an interpolate value of 0.0-1.0
                case 1:
                    return YELLOW.interpolate(RED, value % 1.0);
                case 2:
                    return RED.interpolate(GREEN, value % 1.0);
                default:
                    return null; // Shouldn't happen at all
            }
        }, step);
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        button2.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                step.set(step.get() + INCREMENT_STEP);
            }
        });

        javafx.util.Callback<DatePicker, DateCell> set = new javafx.util.Callback<DatePicker, DateCell>() {
            @Override
            public DateCell call(final DatePicker datePicker) {
                return new DateCell() {
                    @Override public void updateItem(LocalDate item, boolean empty) {
                        super.updateItem(item, empty);
                        //if today, change text and style
                        if (item.equals(LocalDate.now())) {
                            setText(date +"/" + clicksCounter); // Not sure if you still need the click count
                            backgroundProperty().bind(Bindings.when(
                                    backgroundColor.isNotNull())
                                .then(Bindings.createObjectBinding(() -> 
                                    new Background(new BackgroundFill(
                                        backgroundColor.get(),
                                        CornerRadii.EMPTY,
                                        Insets.EMPTY
                                    )), backgroundColor)
                                ).otherwise(Background.EMPTY)
                            );
                        }
                    }
                };
            }
        };
        datePicker.setDayCellFactory(set);
    }
}
Jai
  • 8,165
  • 2
  • 21
  • 52
  • Many Thanks. But I still have a concern, if I keep pushing it goes from dark red to brown and then turns green again. And the color starts with "normal" green and not with a bright green. How could I fix this? – Joe Mau May 30 '18 at 11:55
  • @JoeMau It's your logic. You can loop from 0 to 3, or reverse it, or keep it at max. You just have to do the check in the button handler. – Jai May 30 '18 at 11:57
1

You can implement a simple solution based on rgb values of those colors :

green  in rgb is (0,  255, 0)
yellow in rgb is (255,255, 0)
red in rgb is (255, 0 , 0)

So you start with green, increment red until you get yellow, and then decrement green until you get red:

public class Controller implements Initializable {

    //defines the incremental change in color 
    private static final int COLOR_INCREMENT = 30;
    //initial rgb values (green) 
    private int red = 0, green = 255, blue = 0;
    @FXML
    private Button button2;

    @FXML
    private DatePicker datePicker;

    @FXML
    private Label lbColor;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        button2.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                lbColor.setStyle("-fx-background-color:"+ getColorString()+";");
            }

        });
        //Cell factory omitted. Not needed to demonstrate the question nor the answer 
    }

    //increment color by COLOR_INCREMENT to step from green to yellow to red 
    //and return string representation of it 
    //green is rgb is (0, 255, 0)
    //yellow rgb is (255, 255, 0)
    //red in rgb is (255, 0, 0)
    private String getColorString() {

        if((green == 255) && (red < 255)) {
            red = (red + COLOR_INCREMENT) > 255 ?  255 : (red + COLOR_INCREMENT) ;
        }else if( (red == 255) && (green > 0)){
            green = (green - COLOR_INCREMENT) < 0 ?  0 : (green - COLOR_INCREMENT) ;
        }

        StringBuilder sb = new StringBuilder("rgb(");
        sb.append(red).append(",")
        .append(green).append(",")
        .append(blue).append(")");

        return sb.toString();
    }
}

Note that the number of changes is limited, and defined by the size of COLOR_INCREMENT. After 2 times (255/COLOR_INCREMENT) the color remains red.
It can be further improved overcome this limit. It can also benefit from dynamic (changing) COLOR_INCREMENT.

Edit you can of course use the same method to change a DateCell color:

@Override
public void initialize(URL location, ResourceBundle resources) {

    button2.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
            lbColor.setStyle("-fx-background-color:"+ getColorString()+";");
        }

    });

    javafx.util.Callback<DatePicker, DateCell> set = new javafx.util.Callback<DatePicker, DateCell>() {
        @Override
        public DateCell call(final DatePicker datePicker) {
            return new DateCell() {
                @Override public void updateItem(LocalDate item, boolean empty) {
                    super.updateItem(item, empty);
                    //if today, change text and style
                    if (item.equals(LocalDate.now())) {
                        setText(date +"/" + clicksCounter);
                        setStyle("-fx-background-color: "+ getColorString () +"; ");
                    }
                }
            };
        }
    };
    datePicker.setDayCellFactory(set);
}

Note that calling getColorString () twice (once by the button handler and once by the cell factory) causes the color to change twice.
To prevent it you can set a color field. The field is updated by the button handler and used by the Label as well as the cell factory:

public class Controller implements Initializable {

    protected static final int COLOR_INCREMENT = 30;

    @FXML
    private Button button2;

    @FXML
    private DatePicker datePicker;

    @FXML
    private Label lbColor;

    private int red = 0, green = 255, blue = 0;
    private String colorAsString; //string representation of rgb color 
    private int clicksCounter = 0;
    private String date = String.valueOf(LocalDate.now().getDayOfMonth());

    @Override
    public void initialize(URL location, ResourceBundle resources) {

        button2.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                colorAsString = getColorString();
                lbColor.setStyle("-fx-background-color:"+ colorAsString +";");
            }
        });

        javafx.util.Callback<DatePicker, DateCell> set = new javafx.util.Callback<DatePicker, DateCell>() {
            @Override
            public DateCell call(final DatePicker datePicker) {
                return new DateCell() {
                    @Override public void updateItem(LocalDate item, boolean empty) {
                        super.updateItem(item, empty);
                        //if today, change text and style
                        if (item.equals(LocalDate.now())) {
                            setText(date +"/" + clicksCounter);
                            setStyle("-fx-background-color: "+ colorAsString +"; ");
                        }
                    }
                };
            }
        };
        datePicker.setDayCellFactory(set);
    }

    //increment color by COLOR_INCREMENT to step from green to yellow to red
    //and return string representation of it
    //green is rgb is (0, 255, 0)
    //yellow rgb is (255, 255, 0)
    //red in rgb is (255, 0, 0)
    private String getColorString() {

        if((green == 255) && (red < 255)) {
            red = (red + COLOR_INCREMENT) > 255 ?  255 : (red + COLOR_INCREMENT) ;
        }else if( (red == 255) && (green > 0)){
            green = (green - COLOR_INCREMENT) < 0 ?  0 : (green - COLOR_INCREMENT) ;
        }

        StringBuilder sb = new StringBuilder("rgb(");
        sb.append(red).append(",")
        .append(green).append(",")
        .append(blue).append(")");

        return sb.toString();
    }
}
c0der
  • 18,467
  • 6
  • 33
  • 65
  • Many thanks. I just wanted to change the color of the DatePicker `setStyle (" - fx-background-color: "+ getColorString () +"; ");` I can only change the color of the DatePicker if I change the color of the label , If I use the `lbColor.setStyle (" - fx-background-color: "+ getColorString () +"; ");` the DatePicker will remain green. Why is that? And with DatePicker I mean again today's date in the calendar. :D – Joe Mau May 31 '18 at 20:31
  • Use `-fx` and not `- fx` – c0der Jun 01 '18 at 03:06
  • Sorry, I used `setStyle("-fx-background-color:"+ getColorString()+";");` The space came only through the copy. – Joe Mau Jun 01 '18 at 06:51
  • Is there still a possibility that the color green only after the first click is displayed and then the color changes the more often one presses? If nothing is clicked, the "default" color should be. – Joe Mau Jun 01 '18 at 14:02
  • Try to work it out. If you need help please post a new question and message me here so I'll know you have. Each post should be about one concise problem. – c0der Jun 01 '18 at 16:30