JavaFx circle with two colors - javafx

Could someone help me? I Need draw a circle in javaFX. It should be Partially filled (bottom part). Top part should be transparent. Level of filled part I should able to change in runtime. Also, it could be circle filled with two colors
Thanks

You could just use a rectangle and a circle as its clipping shape. By moving the rectangle up and down while keeping the clipping in place you could simulate the filling level.

You could use a Linear Gradient to do this inside a method where you must each time indicate a color :
private void changeColor(String color){
if(color == null){
color = "transparent";
}
circle.setStyle("-fx-fill:linear-gradient( from 100.0% 100.0% to 100.0% 0.0%, rgb(77,102,204) 0.5," + color +" 0.5);");
}
Here's a demo :
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class Launcher extends Application{
private Pane root = new Pane();
private Scene scene;
private Circle circle = new Circle(200, 200, 100);
private Button btn = new Button("Change");
private boolean change = false;
#Override
public void start(Stage stage) throws Exception {
btn.setOnAction(evt->{
if(change){
change = !change;
changeColor("red");
}else{
change = !change;
changeColor("transparent");
}
});
changeColor("green");
root.getChildren().addAll(btn,circle);
scene = new Scene(root,400,400);
stage.setScene(scene);
stage.show();
}
private void changeColor(String color){
if(color == null || color.isEmpty()){
color = "transparent";
}
circle.setStyle("-fx-fill:linear-gradient( from 100.0% 100.0% to 100.0% 0.0%, rgb(77,102,204) 0.5," + color +" 0.5);");
}
public static void main(String[] args) {
launch(args);
}
}
It's just a simple method you can find better and more powerful example I think, good luck !

Related

JavaFX drag thin line on chart

I have a horizontal line drawn on a chart. When hovering over it the cursor changes to CursorType.S_RESIZE. That indicates the user can start to drag. As the line is very thin you have to place the cursor very accurate. For a better user experience I would like to add a margin above and below the line to enter the draggable zone easier.
Is there a way to make the line “thicker” so the setOnMouseMoved() event fires already when approaching?
import javafx.application.Application;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class DragLine extends Application {
public void start(Stage stage) {
ChartWithLine chartWithLine = new ChartWithLine(new NumberAxis(), new NumberAxis());
stage.setScene(new Scene(chartWithLine, 500, 400));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class ChartWithLine<X, Y> extends LineChart {
public ChartWithLine(Axis axis, Axis axis2) {
super(axis, axis2);
line = new Line();
line.setOnMouseMoved(event -> line.setCursor(Cursor.S_RESIZE));
getPlotChildren().add(line);
}
private Line line;
public void layoutPlotChildren() {
super.layoutPlotChildren();
double yPos = getYAxis().getDisplayPosition(55);
line.setStartX(0);
line.setEndX(getBoundsInLocal().getWidth());
line.setStartY(yPos);
line.setEndY(yPos);
}
}
Here is my work around. I am plotting a second, thicker line at the same position where I plot the thin line. The thicker line is set to transparent so it is not visible. The dragging functionality is set to the thicker line. When it is dragged, both lines are plotted to the dragged location.
This solves the issue that it is hard to grab a thin line with the mouse. But I do not really like it for two reasons. First there is a second line I do not need at all actually. And second if you hover over the middle of the thicker line where the thin visible line is located, the mouse changes back to not draggable cursor. I would have to implement dragability for the thinner line now too to avoid this. But this is pretty much overkill.
Again, is there some kind of way to set the line thicker without making it look thicker?
import javafx.application.Application;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.input.MouseEvent;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class DragLine extends Application {
public void start(Stage stage) {
ChartWithLine chartWithLine = new ChartWithLine(new NumberAxis(), new NumberAxis());
stage.setScene(new Scene(chartWithLine, 500, 400));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
//ChartWithLine
class ChartWithLine<X, Y> extends LineChart {
public ChartWithLine(Axis axis, Axis axis2) {
super(axis, axis2);
draggableLine = new DraggableLine(this);
getPlotChildren().addAll(draggableLine.lineToShow, draggableLine.lineToDrag);
}
private DraggableLine draggableLine;
public void layoutPlotChildren() {
super.layoutPlotChildren();
updateLinePlot();
}
//updateLinePlot called when line was dragged
public void updateLinePlot() {
//mouse position after drag
double yPos = draggableLine.mousePosY;
System.out.println("Line dragged to: " + getYAxis().getValueForDisplay(yPos));
//plot lines accordingly to new mouse position
Line line = draggableLine.lineToDrag;
line.setStartX(0);
line.setEndX(getBoundsInLocal().getWidth());
line.setStartY(yPos);
line.setEndY(yPos);
line = draggableLine.lineToShow;
line.setStartX(0);
line.setEndX(getBoundsInLocal().getWidth());
line.setStartY(yPos);
line.setEndY(yPos);
}
}
//DraggableLine
class DraggableLine {
public DraggableLine(ChartWithLine chart) {
this.chart = chart;
//lineToShow is thin line plotted visible on chart
lineToShow = new Line();
//lineToDrag is plotted at same position on chart as thin visible line.
lineToDrag = new Line();
//set transparent to make it not visible
lineToDrag.setStyle("-fx-stroke: transparent;");
//set line to drag stroke width very broad so it is easy to grab
lineToDrag.setStrokeWidth(20.0);
lineToDrag.setOnMouseMoved(this::mouseOver);
lineToDrag.setOnMouseDragged(event -> onMouseDragged(event.getY()));
lineToDrag.setOnMousePressed(this::onMousePressed);
lineToDrag.setOnMouseReleased(event -> onMouseReleased());
}
private ChartWithLine chart;
public Line lineToShow;
public Line lineToDrag;
boolean isDragging = false;
public double mousePosY = 55;
//change cursor
protected void mouseOver(MouseEvent event) {
if (isDragZone(event)) {
lineToDrag.setCursor(Cursor.S_RESIZE);
} else {
lineToDrag.setCursor(Cursor.DEFAULT);
}
}
//mouse pressed over draggable zone
void onMousePressed(MouseEvent event) {
if (isDragZone(event))
isDragging = true;
}
//mouse released
void onMouseReleased() {
isDragging = false;
}
//change values when mouse is dragging
void onMouseDragged(double y) {
if (!isDragging) return;
mousePosY = y;
chart.updateLinePlot();
}
//check if mouse is in draggable zone
protected boolean isDragZone(MouseEvent event) {
return event.getY() > (lineToDrag.getStartY()) || event.getY() < (lineToDrag.getStartY());
}
}

How to get rid of extra-space (padding) between piechart and its border in JavaFX / TornadoFX?

I have a simple piechart without name, labels, legend. I need only a circle itself. But I can't get rid of that padding between borders and content. I've tried these all and received no result (TornadoFX CSS):
diagram {
padding = box(0.px)
labelPadding = box(0.px)
borderImageInsets += box(0.px)
borderInsets += box(0.px)
backgroundInsets += box(0.px)
maxWidth = 25.px
maxHeight = 25.px
labelLineLength = 0.px
borderColor += box(Color.GREEN)
}
I want to get rid of this extra-space between a circle and green borders. Does anybody know any Java / CSS / TornadoFX solutions/options here ?
You can use a negative value for e. g. padding. Please have a look at this small example (JavaFX):
package sample;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage stage) {
VBox vBox = new VBox();
ObservableList<PieChart.Data> pieChartData =
FXCollections.observableArrayList(
new PieChart.Data("", 75),
new PieChart.Data("", 25));
final PieChart chart = new PieChart(pieChartData);
chart.setLegendVisible(false);
// Negative value for padding:
chart.setStyle("-fx-padding: -35; -fx-border-color: green; -fx-border-width: 3;");
vBox.getChildren().addAll(chart);
stage.setScene(new Scene(vBox));
stage.show();
//chart.setMaxWidth(400d); // careful
}
public static void main(String[] args) {
launch(args);
}
}
You can also quickly set a maximum width for the chart to make the green box a square but be careful please, it could mess with the overall layout:

Javafx darken background

I have FXML application with 10 circles in AnchorPane. I want to hover mouse on one circle and make other 9 and background to darken.
The best I could do was some basic FadeTransition, which only made them disappear, not darken, plus I cant figure out how to select all children of node except one that I have mouse on. Selecting all children except one manually seems not really efficient for more objects.
I tried to google it up, but I just cant find anything.
Please, post a link to thread related to similar problem or sample code. Any help would be really appreciated.
You can use the following sample. Please note that there are some assumptions made, such as every node in the scene graph is a Shape object and that every shape has a Color object associated with the fill. The sample code is sufficient to derive other solutions related specifically to your use case.
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
public class SelectionApp extends Application {
private Pane root = new Pane();
private Parent createContent() {
root.setPrefSize(800, 600);
root.getChildren().add(new Rectangle(800, 600, Color.AQUA));
for (int i = 0; i < 10; i++) {
Circle circle = new Circle(25, 25, 25, Color.GREEN);
// just place them randomly
circle.setTranslateX(Math.random() * 700);
circle.setTranslateY(Math.random() * 500);
circle.setOnMouseEntered(e -> select(circle));
circle.setOnMouseExited(e -> deselect(circle));
root.getChildren().add(circle);
}
return root;
}
private void select(Shape node) {
root.getChildren()
.stream()
.filter(n -> n != node)
.map(n -> (Shape) n)
.forEach(n -> n.setFill(darker(n.getFill())));
}
private void deselect(Shape node) {
root.getChildren()
.stream()
.filter(n -> n != node)
.map(n -> (Shape) n)
.forEach(n -> n.setFill(brighter(n.getFill())));
}
private Color darker(Paint c) {
return ((Color) c).darker().darker();
}
private Color brighter(Paint c) {
return ((Color) c).brighter().brighter();
}
#Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(createContent());
primaryStage.setTitle("Darken");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Make JavaFX wait and continue with code

Basically I am trying to make a short effect using JavaFX. I have the shape of a heart (added together from two circles and a polygon) that I can vary in size using the double value p. "Standart Size" would be p = 1.0;.
I am trying to add a pumping effect to the heart. I have the method pumpOnce():
public void pumpOnce(){
p = p + 1;
initHeart();
//Here goes what ever it takes to make stuff working!!
p = p - 1;
initHeart();
}
initHeart() draws the heart based on p.
I have found out that Thread.sleep(); or similar methods will not work due to the thread philosophy in JavaFX.
But what can I use instead?
The JavaFX animations are probably the way to go, but the "thread philosophy" in JavaFX isn't hard to work with if you want to roll your own, or do other, more complicated things in background threads.
The following code will pause and change the value in a label (full disclosure, I'm reusing code I wrote for another question):
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javax.xml.datatype.Duration;
public class DelayWithTask extends Application {
private static Label label;
public static void main(String[] args) { launch(args); }
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
label = new Label();
label.setText("Waiting...");
StackPane root = new StackPane();
root.getChildren().add(label);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
delay(5000, () -> label.setText("Hello World"));
}
public static void delay(long millis, Runnable continuation) {
Task<Void> sleeper = new Task<Void>() {
#Override
protected Void call() throws Exception {
try { Thread.sleep(millis); }
catch (InterruptedException e) { }
return null;
}
};
sleeper.setOnSucceeded(event -> continuation.run());
new Thread(sleeper).start();
}
}
The basic JavaFX background tool is the Task, any JavaFX application that actually does anything will probably be littered with these all over. Learn how to use them.
Dave's solution is great for general purpose off thread based work in JavaFX.
If you wish to use the animation facilities of JavaFX, the solutions below demonstrate this using a Timeline or a ScaleTransition. The timeline implements a discrete scale of the UI element, so every quarter of a second the UI element is scaled larger or back to it's original size. The scale transition implements a smooth scale of the UI element, so the UI element gradually gets larger then smaller using an interpolated scale factor with the default easing interpolator.
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class BeatingHeart extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage stage) {
ImageView heart = new ImageView(HEART_IMAGE_LOC);
animateUsingTimeline(heart);
// animateUsingScaleTransition(heart);
StackPane layout = new StackPane(heart);
layout.setPrefWidth(heart.getImage().getWidth() * 2);
layout.setPrefHeight(heart.getImage().getHeight() * 2);
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
private void animateUsingTimeline(ImageView heart) {
DoubleProperty scale = new SimpleDoubleProperty(1);
heart.scaleXProperty().bind(scale);
heart.scaleYProperty().bind(scale);
Timeline beat = new Timeline(
new KeyFrame(Duration.ZERO, event -> scale.setValue(1)),
new KeyFrame(Duration.seconds(0.5), event -> scale.setValue(1.1))
);
beat.setAutoReverse(true);
beat.setCycleCount(Timeline.INDEFINITE);
beat.play();
}
private void animateUsingScaleTransition(ImageView heart) {
ScaleTransition scaleTransition = new ScaleTransition(
Duration.seconds(1), heart
);
scaleTransition.setFromX(1);
scaleTransition.setFromY(1);
scaleTransition.setFromZ(1);
scaleTransition.setToX(1.1);
scaleTransition.setToY(1.1);
scaleTransition.setToZ(1.1);
scaleTransition.setAutoReverse(true);
scaleTransition.setCycleCount(Animation.INDEFINITE);
scaleTransition.play();
}
private static final String HEART_IMAGE_LOC =
"http://icons.iconarchive.com/icons/mirella-gabriele/valentine/128/Heart-red-icon.png";
// icon obtained from: http://www.iconarchive.com/show/valentine-icons-by-mirella-gabriele/Heart-red-icon.html
// icon license: Free for non-commercial use, commercial use not allowed.
}

JavaFX: How can I best place a Label centered in a Shape?

Let's say I already have a Shape on the screen. For example:
Circle circle = new Circle(x, y, radius);
circle.setFill(Color.YELLOW);
root.getChildren().add(circle);
I would like to create a Label "over" that Circle such that the Label is centered in the Circle, the font size is maximized to fit inside the Circle, etc.
I can see how this could be accomplished via binding, but that seems needlessly complicated if the position/size of these things will never change during runtime.
Thank you in advance for your help! I'm very new to JavaFX and not all that experienced at programming in the first place, so I apologize if I should've been able to find this out via my research.
Use a StackPane to automatically center the text on top of the shape.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.*;
import javafx.stage.Stage;
// java 8 code.
public class Circular extends Application {
public static void main(String[] args) throws Exception {
launch(args);
}
#Override
public void start(final Stage stage) throws Exception {
Text text = createText("Xyzzy");
Circle circle = encircle(text);
StackPane layout = new StackPane();
layout.getChildren().addAll(
circle,
text
);
layout.setPadding(new Insets(20));
stage.setScene(new Scene(layout));
stage.show();
}
private Text createText(String string) {
Text text = new Text(string);
text.setBoundsType(TextBoundsType.VISUAL);
text.setStyle(
"-fx-font-family: \"Times New Roman\";" +
"-fx-font-style: italic;" +
"-fx-font-size: 48px;"
);
return text;
}
private Circle encircle(Text text) {
Circle circle = new Circle();
circle.setFill(Color.ORCHID);
final double PADDING = 10;
circle.setRadius(getWidth(text) / 2 + PADDING);
return circle;
}
private double getWidth(Text text) {
new Scene(new Group(text));
text.applyCss();
return text.getLayoutBounds().getWidth();
}
}
Related
how to put a text into a circle object to display it from circle's center?
The answer to the related question discusses different bounds types for text (such as Visual bounds), in case you need that.
StackPane stackPane = new StackPane();
Circle circle = new Circle();
Label label = new Label("Hi");
circle.setFill(Color.GOLD);
circle.setStroke(Color.GRAY);
circle.radiusProperty().bind(label.widthProperty());
stackPane.getChildren().addAll(circle, label);

Resources