According to this code, button("enlarge") will trigger an action which is calling the method enlarge(), which will do: circle.setRadius(circle.getRadius() + 2);
which is just merely changing the radius value. what I don't understand is that how merely changing the radius will somehow make the program redraw the entire circle.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class ControlCircle extends Application {
private CirclePane1 circlePane = new CirclePane1();
#Override
public void start(Stage primaryStage) {
HBox hBox = new HBox();
hBox.setSpacing(10);
hBox.setAlignment(Pos.CENTER);
Button btEnlarge = new Button("Enlarge");
Button btShrink = new Button("Shrink");
hBox.getChildren().add(btEnlarge);
hBox.getChildren().add(btShrink);
btEnlarge.setOnAction(new EnlargeHandler());
btShrink.setOnAction(new ShrinkHandler());
BorderPane borderPane = new BorderPane();
borderPane.setCenter(circlePane);
borderPane.setBottom(hBox);
BorderPane.setAlignment(hBox, Pos.CENTER);
Scene scene = new Scene(borderPane, 200, 150);
primaryStage.setTitle("ControlCircle");
primaryStage.setScene(scene);
primaryStage.show();
}
class EnlargeHandler implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent e) {
circlePane.enlarge();
}
}
class ShrinkHandler implements EventHandler<ActionEvent>{
#Override
public void handle(ActionEvent e) {
circlePane.shrink();
}
}
}
class CirclePane1 extends StackPane{
private Circle circle = new Circle(50);
public CirclePane1() {
getChildren().add(circle);
circle.setStroke(Color.BLACK);
circle.setFill(Color.WHITE);
}
public void enlarge() {
circle.setRadius(circle.getRadius() + 2);
}
public void shrink() {
circle.setRadius(circle.getRadius() > 2 ? circle.getRadius() - 2
: circle.getRadius());
}
}
Looking at the source code of Circle you can see how the radius property is defined:
private final DoubleProperty radius = new DoublePropertyBase() {
#Override
public void invalidated() {
NodeHelper.markDirty(Circle.this, DirtyBits.NODE_GEOMETRY);
NodeHelper.geomChanged(Circle.this);
}
#Override
public Object getBean() {
return Circle.this;
}
#Override
public String getName() {
return "radius";
}
};
When you change the value of the radius property its invalidated() method is called. And this is where the "magic" happens, it calls these two methods:
NodeHelper.markDirty(Circle.this, DirtyBits.NODE_GEOMETRY);
NodeHelper.geomChanged(Circle.this);
Now, I don't know what these methods do specifically - nor do I have the time/desire to study it - but they tell the JavaFX runtime that the Circle needs to be redrawn. This means that the next time a rendering pulse occurs, which may be triggered by these methods, the Circle will be drawn with its new radius.
This is all very optimized and will cause a redraw only when applicable (such as only when part of ascene-graph).
Related
I'm building a JavaFX application with multiple Scenes. I have a problem with scope of variable when changing scenes within setOnAction event. This is my code:
Stage myStage;
public Scene logInScene(){
... all the buttons / textFields
createAccountButton.setOnAction(new EventHandler<ActionEvent>(){
public void handle(ActionEvent t){
**this.getStage().allScene(createAccountPane1);**
}
}
}
public Stage getStage(){
return this.myStage;
}
public void allScene(Pane p){
this.myStage.setScene(p);
}
I'm getting an error within the setOnAction function. "Cannot Find Symbol" getStage(). I know this must be a scope problem and it doesn't recognize any variables / functions outside of that scope. How do I make it so that I can change within? I've tried passing through the variable but that will just make my code messy and I wish there was a simpler way. Thanks guys!
Your code works as long as you keep consistency:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Test extends Application{
private Stage stage;
#Override
public void start(Stage primaryStage) throws Exception {
stage = primaryStage;
Scene scene = logInScene();
primaryStage.setScene(scene);
primaryStage.show();
}
public Scene logInScene(){
Pane root = new Pane();
Button createAccountButton = new Button("create account");
createAccountButton.setOnAction(new EventHandler<ActionEvent>(){
public void handle(ActionEvent t){
stage.setScene(CreateAccountScene());
}
});
root.getChildren().add(createAccountButton);
return new Scene(root);
}
protected Scene CreateAccountScene() {
VBox root = new VBox();
Label userLabel = new Label("Insert the username:");
final TextField userField = new TextField();
Button createAccountButton = new Button("create account");
createAccountButton.setOnAction(new EventHandler<ActionEvent>(){
public void handle(ActionEvent t){
System.out.println("Account for user " + userField.getText() + " was created succesfully");
}
});
root.getChildren().addAll(userLabel,userField,createAccountButton);
return new Scene(root);
}
public static void main(String[] args) {
launch(args);
}
}
This question has already been solved, but I think it's worth clarifying that your line fails because the this keyword refers to the anonymous EventHandler you are implementing. In Java, you reference the outer class instance with OuterClass.this. So OuterClass.this.getStage().allScene(createAccountPane1); will work.
If you are looking for a prettier solution, some coders like to define a local variable that points to the outer class instance:
final OuterClass self = this;
createAccountButton.setOnAction(new EventHandler<ActionEvent>(){
public void handle(ActionEvent t){
self.getStage().allScene(createAccountPane1);
}
}
I'm trying to create image gallery and use some image animations. Problem is with ImageView. I would like to play() RotateTransition from some method and call this method any time but it's not working at all. There should be some issue with threads but even if it is called from new thread nothing is happening. Is there any solution how to work with ImageView and Transitions generally?
public class ImageGallery extends ImageView{
RotateTransition rt;
public ImageGallery() {
setImage(new Image("/img/01.jpg"));
setPreserveRatio(true);
rt = new RotateTransition(Duration.millis(800), this);
rt.setByAngle(90);
//this works but not what I need
//fitWidthProperty().addListener(e -> rt.play());
}
public void rotateRight(){
rt.play(); //nothing
//run later is not working too
//Platform.runLater(new ViewTransition(this));
}
}
Thanks
As per user comments in the question, adding a MCVE
Main.java
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
ImageGallery gallery = new ImageGallery();
VBox box= new VBox(gallery);
box.setAlignment(Pos.CENTER);
Scene scene = new Scene(box, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
gallery.rotateRight();
}
public static void main(String[] args){
launch(args);
}
}
ImageGallery.java
import javafx.animation.RotateTransition;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.util.Duration;
public class ImageGallery extends ImageView{
RotateTransition rt;
public ImageGallery() {
setImage(new Image("http://jaxenter.com/wp-content/uploads/2013/03/javafx.1.png"));
setPreserveRatio(true);
rt = new RotateTransition(Duration.millis(800), this);
rt.setByAngle(90);
}
public void rotateRight(){
rt.play();
}
}
I'm working with RichTextFx's CodeArea to highlight custom mini language code.
Now while executing this code I want to show a small arrow in front of current executed line. I know the specific line number but can't get anything to happen with the line number label.
Since github project claims showing line numbers or breakpoint toggles as a feature this can't be very difficult. But can't get anything to work...
Thanks in advance
To show any graphic in front of the line, you need to set the "paragraph graphic factory" of the CodeArea. This graphic factory is just a function int -> Node: given the line number, it returns a Node that will be displayed in front of the line.
Here is a graphic factory that produces a green triangle pointing at the line. It will only be shown when the line is equal to the given integer property shownLine.
class ArrowFactory implements IntFunction<Node> {
private final ObservableValue<Integer> shownLine;
ArrowFactory(ObservableValue<Integer> shownLine) {
this.shownLine = shownLine;
}
#Override
public Node apply(int lineNumber) {
Polygon triangle = new Polygon(0.0, 0.0, 10.0, 5.0, 0.0, 10.0);
triangle.setFill(Color.GREEN);
ObservableValue<Boolean> visible = Val.map(
shownLine,
sl -> sl == lineNumber);
triangle.visibleProperty().bind(visible.conditionOnShowing(triangle));
return triangle;
}
}
Each graphic (i.e. little green triangle) you create will be observing the given shownLine property to decide whether it should be visible. As lines, and therefore line graphics, come and go, it is important to remove the listener of shownLine when the graphic is no longer used. visible.conditionOnShowing(triangle) is a new property that will stop observing the visible property (and automatically also the shownLine property, thanks to ReactFX's lazy binding semantics) and reset to constant false whenever the triangle is not part of a showing window. So we don't cause memory or CPU leaks due to uncleaned listeners.
Here is a complete runnable demo that uses this ArrowFactory combined with the LineNumberFactory provided by RichTextFX to show both line numbers and a little triangle. This demo uses the CodeArea's current line as the shownLine property. You will want to substitute it for a property that contains the current line of execution.
import java.util.function.IntFunction;
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.stage.Stage;
import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.LineNumberFactory;
import org.reactfx.value.Val;
public class CodeAreaWithLineIndicator extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
CodeArea codeArea = new CodeArea();
IntFunction<Node> numberFactory = LineNumberFactory.get(codeArea);
IntFunction<Node> arrowFactory = new ArrowFactory(codeArea.currentParagraphProperty());
IntFunction<Node> graphicFactory = line -> {
HBox hbox = new HBox(
numberFactory.apply(line),
arrowFactory.apply(line));
hbox.setAlignment(Pos.CENTER_LEFT);
return hbox;
};
codeArea.setParagraphGraphicFactory(graphicFactory);
primaryStage.setScene(new Scene(new StackPane(codeArea), 600, 400));
primaryStage.show();
}
}
class ArrowFactory implements IntFunction<Node> {
private final ObservableValue<Integer> shownLine;
ArrowFactory(ObservableValue<Integer> shownLine) {
this.shownLine = shownLine;
}
#Override
public Node apply(int lineNumber) {
Polygon triangle = new Polygon(0.0, 0.0, 10.0, 5.0, 0.0, 10.0);
triangle.setFill(Color.GREEN);
ObservableValue<Boolean> visible = Val.map(
shownLine,
sl -> sl == lineNumber);
triangle.visibleProperty().bind(visible.conditionOnShowing(triangle));
return triangle;
}
}
And this is the result:
Working example
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.stage.Stage;
import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.LineNumberFactory;
import org.reactfx.value.Val;
import java.util.function.IntFunction;
public class CodeAreaWithLineIndicator extends Application {
CodeArea codeArea;
TextField textField;
public static final IntegerProperty lineValue = new SimpleIntegerProperty(-1) ;
/* public final int getValue() {
return value.get();
}*/
/* public final void setValue(int value) {
this.value.set(value);
}*/
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
codeArea = new CodeArea();
codeArea.replaceText(0,0,"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
codeArea.setPrefHeight(400);
IntFunction<Node> numberFactory = LineNumberFactory.get(codeArea);
IntFunction<Node> arrowFactory = new ManualArrowFactory(lineValue);
IntFunction<Node> graphicFactory = line -> {
HBox hbox = new HBox(
numberFactory.apply(line),
arrowFactory.apply(line));
hbox.setAlignment(Pos.CENTER_LEFT);
return hbox;
};
codeArea.setParagraphGraphicFactory(graphicFactory);
VBox vbox = new VBox();
textField = new TextField();
textField.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
try {
lineValue.setValue(Integer.parseInt(textField.getText()));
} catch (NumberFormatException e) {
}
}
});
Button button = new Button("MoveIt");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
try {
lineValue.setValue(Integer.parseInt(textField.getText()));
} catch (NumberFormatException e) {
}
}
});
vbox.getChildren().addAll(textField, button, codeArea);
Scene scene = new Scene(vbox, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
class ManualArrowFactory implements IntFunction<Node> {
private final IntegerProperty shownLine;
public ManualArrowFactory(IntegerProperty shownLine) {
this.shownLine = shownLine;
}
#Override
public Node apply(int lineNumber) {
Polygon triangle = new Polygon(0.0, 0.0, 10.0, 5.0, 0.0, 10.0);
triangle.setFill(Color.GREEN);
ObservableValue<Boolean> visible = Val.map(shownLine, sl -> sl.intValue()-1 == lineNumber);
triangle.visibleProperty().bind(
Val.flatMap(triangle.sceneProperty(), scene -> {
return scene != null ? visible : Val.constant(false);
}));
return triangle;
}
}
}
For multiline implementation:
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.LineNumberFactory;
import org.reactfx.value.Val;
import java.util.function.IntFunction;
public class CodeAreaWithLineIndicator extends Application {
CodeArea codeArea;
TextField textField;
public static final IntegerProperty lineValue = new SimpleIntegerProperty(-1) ;
public static final ObservableList<Integer> olistValue = FXCollections.observableArrayList();
public static final ListProperty<Integer> listValue = new SimpleListProperty<Integer>(olistValue);
/* public final int getValue() {
return value.get();
}*/
/* public final void setValue(int value) {
this.value.set(value);
}*/
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
codeArea = new CodeArea();
codeArea.replaceText(0,0,"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
codeArea.setPrefHeight(400);
IntFunction<Node> numberFactory = LineNumberFactory.get(codeArea);
IntFunction<Node> arrowFactory = new MultiBreakPointFactory(listValue);
IntFunction<Node> graphicFactory = line -> {
HBox hbox = new HBox(
numberFactory.apply(line),
arrowFactory.apply(line));
hbox.setAlignment(Pos.CENTER_LEFT);
return hbox;
};
codeArea.setParagraphGraphicFactory(graphicFactory);
VBox vbox = new VBox();
textField = new TextField();
textField.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
int newValue = Integer.parseInt(textField.getText());
olistValue.add(newValue);
}
});
Button button = new Button("Clear");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
olistValue.clear();
}
});
vbox.getChildren().addAll(textField, button, codeArea);
Scene scene = new Scene(vbox, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
class MultiBreakPointFactory implements IntFunction<Node> {
private final ListProperty<Integer> shownLines;
public MultiBreakPointFactory(ListProperty<Integer> shownLine) {
this.shownLines = shownLine;
}
#Override
public Node apply(int lineIndex) {
StackPane stackPane = new StackPane();
Circle circle = new Circle(10.0, 10.0, 6.0, Color.RED);
Rectangle rectangle = new Rectangle(20,20);
rectangle.setFill(Color.TRANSPARENT);
rectangle.setCursor(Cursor.HAND);
rectangle.setOnMouseClicked(me->{
if (!olistValue.contains(lineIndex+1)){
olistValue.add(lineIndex+1);
}
});
stackPane.getChildren().addAll(rectangle, circle);
circle.setOnMouseClicked(me->{
int index = olistValue.indexOf(lineIndex+1);
if (index>-1)
olistValue.remove(index);
});
circle.setCursor(Cursor.HAND);
ObservableValue<Boolean> visible = Val.map(shownLines, sl -> sl.contains(lineIndex+1));
circle.visibleProperty().bind(
Val.flatMap(circle.sceneProperty(), scene -> {
return scene != null ? visible : Val.constant(false);
}));
return stackPane;
}
}
}
Enter a number to textfield and click enter. Now only changing oListValue will show breakpoint lines on the codearea.
I'm building a JavaFX application with multiple Scenes. I have a problem with scope of variable when changing scenes within setOnAction event. This is my code:
Stage myStage;
public Scene logInScene(){
... all the buttons / textFields
createAccountButton.setOnAction(new EventHandler<ActionEvent>(){
public void handle(ActionEvent t){
**this.getStage().allScene(createAccountPane1);**
}
}
}
public Stage getStage(){
return this.myStage;
}
public void allScene(Pane p){
this.myStage.setScene(p);
}
I'm getting an error within the setOnAction function. "Cannot Find Symbol" getStage(). I know this must be a scope problem and it doesn't recognize any variables / functions outside of that scope. How do I make it so that I can change within? I've tried passing through the variable but that will just make my code messy and I wish there was a simpler way. Thanks guys!
Your code works as long as you keep consistency:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Test extends Application{
private Stage stage;
#Override
public void start(Stage primaryStage) throws Exception {
stage = primaryStage;
Scene scene = logInScene();
primaryStage.setScene(scene);
primaryStage.show();
}
public Scene logInScene(){
Pane root = new Pane();
Button createAccountButton = new Button("create account");
createAccountButton.setOnAction(new EventHandler<ActionEvent>(){
public void handle(ActionEvent t){
stage.setScene(CreateAccountScene());
}
});
root.getChildren().add(createAccountButton);
return new Scene(root);
}
protected Scene CreateAccountScene() {
VBox root = new VBox();
Label userLabel = new Label("Insert the username:");
final TextField userField = new TextField();
Button createAccountButton = new Button("create account");
createAccountButton.setOnAction(new EventHandler<ActionEvent>(){
public void handle(ActionEvent t){
System.out.println("Account for user " + userField.getText() + " was created succesfully");
}
});
root.getChildren().addAll(userLabel,userField,createAccountButton);
return new Scene(root);
}
public static void main(String[] args) {
launch(args);
}
}
This question has already been solved, but I think it's worth clarifying that your line fails because the this keyword refers to the anonymous EventHandler you are implementing. In Java, you reference the outer class instance with OuterClass.this. So OuterClass.this.getStage().allScene(createAccountPane1); will work.
If you are looking for a prettier solution, some coders like to define a local variable that points to the outer class instance:
final OuterClass self = this;
createAccountButton.setOnAction(new EventHandler<ActionEvent>(){
public void handle(ActionEvent t){
self.getStage().allScene(createAccountPane1);
}
}
I am digging the documentation to see if there's a remove method, I just get this link whenever I google
http://www.coderanch.com/t/580998/JavaFX/java/remove-node
there's a simple remove option
Eg : .getChildren().remove(object)
It does not seem to work for me !
The code which you've provided works fine with me.
Add circle with ALT+Click, and remove them by simply clicking on them.
The reason I've used the ALT key for adding the circles is because in the below code, both the scene and the circles handle mouse clicks. Thus, the code has to know from where the event is coming from. This is just an example, of course.
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class ChangeListenerSample extends Application {
public static void main(final String[] args) {
launch(args);
}
#Override
public void start(final Stage primaryStage) throws Exception {
final Group root = new Group();
primaryStage.setResizable(false);
final Scene scene = new Scene(root, 400,80);
primaryStage.setScene(scene);
scene.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override public void handle(final MouseEvent event)
{
if (!event.isAltDown())
return;
final Circle circle = new Circle(event.getSceneX(), event.getSceneY(),30);
circle.setFill(Color.YELLOW);
root.getChildren().add(circle);
circle.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override public void handle(final MouseEvent arg0)
{
root.getChildren().remove(circle);
}
});
}
});
primaryStage.show();
}
}