Move viewpoint of ScrollPane on Button click - javafx

There is concept of my JavaFX applictions:
All screens in one ScrollPane.
For example, if user click on button "Options" in loginScreen i want to animate Y-value of ViewPort to move it to optionsScreen.
How can i programmatically move viewport of ScrollPane with smooth animation?
Or you can offer better idea?

How can I programmatically move viewport of ScrollPane with smooth animation?
You can use a combination of Timeline and vvalueProperty of ScrollPane to perform a smooth animation of scrolling.
Here is a simple application where you I have three sections
Top
Center
Bottom
I am changing the vvalue of the ScrollPane through a Timeline on the action of the button.
The vvalue can have a value between 0.0 to 1.0, so you may have to do your own calculations to find the exact value which you want to be assigned to it.
The KeyValue performs the operation on scrollRoot.vvalueProperty().
The KeyFrame for the complete timeline is set at 500 milliseconds. You may increase or decrease it depending on the time you want the animation to run.
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
// For just adjusting the center rectangle w.r.t ScrollPane
private final int ADJUSTMENT_RATIO = 175;
#Override
public void start(Stage stage) {
Rectangle topRegion = new Rectangle(300, 300, Color.ALICEBLUE);
StackPane top = new StackPane(topRegion, new Label("Top"));
Rectangle centerRegion = new Rectangle(300, 300, Color.GOLDENROD);
StackPane center = new StackPane(centerRegion, new Label("center"));
Rectangle bottomRegion = new Rectangle(300, 300, Color.BISQUE);
StackPane bottom = new StackPane(bottomRegion, new Label("bottom"));
Button topButton = new Button("Top");
Button centerButton = new Button("Center");
Button bottomButton = new Button("Bottom");
HBox buttonBox = new HBox(15, topButton, centerButton, bottomButton);
buttonBox.setPadding(new Insets(20));
buttonBox.setAlignment(Pos.CENTER);
final VBox root = new VBox();
root.setSpacing(10);
root.setPadding(new Insets(10, 10, 10, 10));
root.getChildren().addAll(top, center, bottom);
ScrollPane scrollRoot = new ScrollPane(root);
Scene scene = new Scene(new VBox(buttonBox, scrollRoot));
stage.setTitle("Market");
stage.setWidth(350);
stage.setHeight(400);
stage.setScene(scene);
stage.show();
topButton.setOnAction(event -> {
final Timeline timeline = new Timeline();
final KeyValue kv = new KeyValue(scrollRoot.vvalueProperty(), 0.0);
final KeyFrame kf = new KeyFrame(Duration.millis(500), kv);
timeline.getKeyFrames().add(kf);
timeline.play();
});
centerButton.setOnAction(event -> {
final Timeline timeline = new Timeline();
final KeyValue kv = new KeyValue(scrollRoot.vvalueProperty(), (top.getBoundsInLocal().getHeight() + ADJUSTMENT_RATIO) / root.getHeight());
final KeyFrame kf = new KeyFrame(Duration.millis(500), kv);
timeline.getKeyFrames().add(kf);
timeline.play();
});
bottomButton.setOnAction(event -> {
final Timeline timeline = new Timeline();
final KeyValue kv = new KeyValue(scrollRoot.vvalueProperty(), 1.0);
final KeyFrame kf = new KeyFrame(Duration.millis(500), kv);
timeline.getKeyFrames().add(kf);
timeline.play();
});
}
public static void main(String[] args) {
launch(args);
}
}
Output

Related

I'm trying to figure out how to make my VBox inside a BorderPane expand with the BorderPane/Stage, but still stay within a certain bounds

I am trying to figure out how to make my VBox, only go to the size of 300 pixel's wide, but i would like to have it at say 250 pixels wide when the program is initialized, then when the user clicks full screen, I want it to expand, but not necessarily with the entire space it would have. I want it to only go to 300 pixels (and have the 3 buttons inside do the same thing) but I'm not sure how to do that. I'm having trouble determining PrefSize and CompSize actual meanings and uses. Any help would be great.
I am also having kind of the same problem with the Label, inside the HBox, that is inside a SplitPane, that is inside a BorderPane. Any explanation of why what you are suggesting will work, will help me with future problems like this. Thank you
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class MainStarUI extends Application {
#Override
public void start(Stage primaryStage){
MenuBar mainMenuOne = addMenuBar();
VBox leftVBoxOne = addVbox();
//AnchorPane midPaneOne = addAnchorPane();
//HBox topHBoxOne = addHBox();
SplitPane midSplitPane = addSplitPane();
BorderPane mainPane = new BorderPane();
mainPane.setTop(mainMenuOne);
mainPane.setLeft(leftVBoxOne);
mainPane.setCenter(midSplitPane);
primaryStage.setMinWidth(1440);
primaryStage.setMinHeight(900);
Scene mainScene = new Scene(mainPane);
primaryStage.setScene(mainScene);
primaryStage.show();
}
public MenuBar addMenuBar(){
Menu menuOne = new Menu("File");
Menu menuTwo = new Menu("Edit");
Menu menuThree = new Menu("Help");
Menu menuFour = new Menu("Exit");
MenuItem menuItemOne = new MenuItem("File");
MenuItem menuItemTwo = new MenuItem("Open");
MenuItem menuItemThree = new MenuItem("Exit");
menuOne.getItems().add(menuItemOne);
menuOne.getItems().add(menuItemTwo);
menuFour.getItems().add(menuItemThree);
MenuBar mainMenuOne = new MenuBar();
mainMenuOne.getMenus().add(menuOne);
mainMenuOne.getMenus().add(menuTwo);
mainMenuOne.getMenus().add(menuThree);
mainMenuOne.getMenus().add(menuFour);
mainMenuOne.maxHeight(25);
mainMenuOne.minHeight(25);
return mainMenuOne;
}
public VBox addVbox(){
VBox leftVBox = new VBox();
leftVBox.setMinWidth(300);
leftVBox.setPrefWidth(300);
leftVBox.setPadding(new Insets(15));
leftVBox.setSpacing(20);
leftVBox.setStyle("-fx-background-color: #336699;");
Button firstButton = new Button("Ships, Components, Items & Weaponry");
firstButton.setMinSize(270, 270);
firstButton.setMaxSize(270, 270);
Button secondButton = new Button("Trading, Mining, Refining & Commodities");
secondButton.setMinSize(270, 270);
secondButton.setMaxSize(270,270);
Button thirdButton = new Button("Star Systems, Planets, Moons & Locations");
thirdButton.setMinSize(270,270);
thirdButton.setMaxSize(270, 270);
leftVBox.getChildren().addAll(firstButton, secondButton, thirdButton);
return leftVBox;
}
public HBox addHBox(){
Image logoImage = new Image("SCImages/TaktikalLogo1.jpg");
ImageView logoImageView = new ImageView();
logoImageView.setImage(logoImage);
logoImageView.setPreserveRatio(false);
logoImageView.setFitWidth(160);
logoImageView.setFitHeight(160);
logoImageView.setSmooth(true);
logoImageView.setCache(true);
Label topLabel = new Label("STAR CITIZEN INFONET & DATABASE");
topLabel.setFont(new Font("Arial", 48));
topLabel.setTextFill(Color.WHITE);
topLabel.setMinHeight(160);
topLabel.setMaxHeight(160);
HBox topHBox = new HBox();
topHBox.setStyle("-fx-background-color: black");
topHBox.setMinHeight(180);
topHBox.setMaxHeight(180);
topHBox.setPrefWidth(1090);
topHBox.getChildren().addAll(logoImageView, topLabel);
topHBox.setPadding(new Insets(10));
topHBox.setSpacing(10);
return topHBox;
}
public SplitPane addSplitPane(){
HBox topHBoxOne = addHBox();
AnchorPane anchorSplitPane = new AnchorPane();
SplitPane mainSplitPane = new SplitPane();
mainSplitPane.setOrientation(Orientation.VERTICAL);
mainSplitPane.setDividerPosition(1, 200);
mainSplitPane.setPrefSize(1090, 850);
mainSplitPane.getItems().addAll(topHBoxOne, anchorSplitPane);
return mainSplitPane;
}
public static void main(String[] args) {
launch(args);
}
}
I actually put my VBox inside an AnchorPane, and attached it to the anchors, and everything worked perfectly after I set my preferred height and width.

Why aren't Bindings working with Transitions

I have a Circle and its centerX property is bound to the text property of a label. This is for viewing the position of the object on screen. The binding seems to stop working whenever I apply a transition on the circle. This is a snippet from the code.
//ERRONEOUS PART OF CODE
Circle circle = new Circle(50, 20, 20);
Label posLabel = new Label();
//binding
StringBinding binding = new StringBinding(){
{bind(circle.centerXProperty());}
#Override public String computeValue(){
return Double.toString(circle.getCenterX());
}
};
posLabel.textProperty().bind(binding);
//translation
TranslateTransition transition = new TranslateTransition(Duration.seconds(3), circle);
transition.setByX(250);
transition.play();
I would like to know why bindings don't work with transitions, and if possible, a workaround to the problem.
P.S. A complete minimum reproducible example: (I am saying that the binding doesn't work because the labels value is stuck at 50.0)
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Group;
import javafx.scene.control.Label;
import javafx.scene.shape.Circle;
import javafx.animation.TranslateTransition;
import javafx.util.Duration;
import javafx.beans.binding.StringBinding;
public class TransitionError extends Application
{
public void start(Stage stage) throws Exception
{
//instantiating all objects
Circle circle = new Circle(50, 20, 20);
Label posLabel = new Label();
Group group = new Group(circle, posLabel);
Scene scene = new Scene(group, 300,100);
stage.setTitle("JavaFX Example");
stage.setFullScreen(true);
stage.setScene(scene);
stage.show();
//ERRONEOUS PART OF CODE
//binding
StringBinding binding = new StringBinding(){
{bind(circle.centerXProperty());}
#Override public String computeValue(){
return Double.toString(circle.getCenterX());
}
};
posLabel.textProperty().bind(binding);
//translation
TranslateTransition transition = new TranslateTransition(Duration.seconds(3), circle);
transition.setByX(250);
transition.play();
}
}
The TranslateTransition doesn't change the value of centerX; it changes the value of translateX.
So you could do:
StringBinding binding = new StringBinding(){
{bind(circle.centerXProperty(), circle.translateXProperty());}
#Override public String computeValue(){
return Double.toString(circle.getCenterX()+circle.getTranslateX());
}
};
Or you could use your original binding and animate the centerX property instead (this may be preferable, since you seem to be relying on centerX to change):
// TranslateTransition transition = new TranslateTransition(Duration.seconds(3), circle);
// transition.setByX(250);
// transition.play();
Timeline timeline = new Timeline(new KeyFrame(
Duration.seconds(3),
new KeyValue(circle.centerXProperty(), circle.getCenterX()+250)
));
timeline.play();

JavaFX ScrollPane and Scaling of the Content

I would like to show a photo as an ImageView in a ScrollPane with an ZoomIn and ZoomOut Function. But if I reduce by means of scale the imageview, an undesirable empty edge is created in the ScrollPane. How can you make sure that the ScrollPane is always the size of the scaled ImageView?
See the following example. For simplicity, I replaced the ImageView with a rectangle.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ScrollPaneDemo extends Application {
double scale;
Pane contPane = new Pane();
#Override
public void start(Stage primaryStage) {
BorderPane pane = new BorderPane();
ScrollPane sp = new ScrollPane();
sp.setContent(contPane);
sp.setVvalue(0.5);
sp.setHvalue(0.5);
Rectangle rec = new Rectangle(2820, 1240,Color.RED);
scale = 0.2;
contPane.setScaleX(scale);
contPane.setScaleY(scale);
contPane.getChildren().add(rec);
Button but1 = new Button("+");
but1.setOnAction((ActionEvent event) -> {
scale*=2;
contPane.setScaleX(scale);
contPane.setScaleY(scale);
});
Button but2 = new Button("-");
but2.setOnAction((ActionEvent event) -> {
scale/=2;
contPane.setScaleX(scale);
contPane.setScaleY(scale);
});
HBox buttons = new HBox(but1, but2);
pane.setTop(buttons);
pane.setCenter(sp);
Scene scene = new Scene(pane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
contPane scaled by using transform don't change its layoutBounds automatically. If you want not to make empty space in contPane, you'd better wrap the node in Group.
See this post. Layout using the transformed bounds
sp.setContent(new Group(contPane));
In addition, if you don't want to make empty space in ScrollPane, limit minimum scale to rate which width or height of the content fits viewport's one.
Button but1 = new Button("+");
but1.setOnAction((ActionEvent event) -> {
updateScale(scale * 2.0d);
});
Button but2 = new Button("-");
but2.setOnAction((ActionEvent event) -> {
updateScale(scale / 2.0d);
});
HBox buttons = new HBox(but1, but2);
pane.setTop(buttons);
pane.setCenter(sp);
Scene scene = new Scene(pane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
updateScale(0.2d);
private void updateScale(double newScale) {
scale = Math.max(newScale, Math.max(sp.getViewportBounds().getWidth() / rec.getWidth(), sp.getViewportBounds().getHeight() / rec.getHeight()));
contPane.setScaleX(scale);
contPane.setScaleY(scale);
}
Consider a case of the image is smaller than ScrollPane's viewport. Because for showing no empty space, this code will stretch contents when it doesn't have enough size.
In a case of huge images, TravisF's comment helps you.

How can I stop a MouseEvent in JavaFX?

(Sorry for my poor English)
I don't know how I can stop a Mouse Event in JavaFX.
This code generates a small image into a large rectangle when I press a button and then pressed the large rectangle, but if I press again the big rectangle is rebuilt a new image.
I dont want to generate a new image, how Can I do that?
button.setOnAction((ActionEvent t) -> {
rectangle.setOnMouseClicked((MouseEvent me) -> {
Rectangle asdf = new Rectangle(48, 48, Color.TRANSPARENT);
StackPane imageContainer = new StackPane();
ImageView image = new ImageView("firefox-icono-8422-48.png");
imageContainer.getChildren().addAll(asdf, image);
imageContainer.setTranslateX(me.getX());
imageContainer.setTranslateY(me.getY());
enableDragging(imageContainer);
rootGroup.getChildren().add(imageContainer);
myList2.add(imageContainer);
});
});
Thanks
PS: t.consume() and me.consume(); don't anything.
I'm not sure I have interpreted your question correctly, but if you want to "turn off" the mouse click handler on the rectangle, you can just call
rectangle.setOnMouseClicked(null);
Complete example:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ActivateRectangleWithButton extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle border = new Rectangle(100, 100, Color.TRANSPARENT);
Rectangle rect = new Rectangle(80, 80, Color.CORNFLOWERBLUE);
StackPane stack = new StackPane(border, rect);
Button button = new Button("Activate");
button.setOnAction(evt -> {
border.setFill(Color.BLUE);
rect.setOnMouseClicked(me -> {
System.out.println("Active rectangle was clicked!");
// de-activate:
border.setFill(Color.TRANSPARENT);
rect.setOnMouseClicked(null);
});
});
VBox root = new VBox(20, stack, button);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 300, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Fade Effect with JavaFX

Im trying to realize a special FadeTransition effect. But I have no idea how I can manage it. For some node I would like to increase the opacity from left to right (for example in Powerpoint, you can change the slides with such an effect). Here is an easy example for rectangles. But the second one should fadeIn from left to right (the opacity should increase on the left side earlier as on the right side). With timeline and KeyValues/KeyFrames I found also no solution.
Thanks in advance.
Rectangle rec2;
#Override
public void start(Stage stage) {
Group root = new Group();
Scene scene = new Scene(root, 400, 300, Color.BLACK);
stage.setTitle("JavaFX Scene Graph Demo");
Pane pane = new Pane();
Button btnForward = new Button();
btnForward.setText(">");
btnForward.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
FadeTransition ft = new FadeTransition(Duration.millis(2000), rec2);
ft.setFromValue(0.);
ft.setToValue(1.);
ft.play();
}
});
Rectangle rec1 = new Rectangle(0, 0, 300,200);
rec1.setFill(Color.RED);
rec2 = new Rectangle(100, 50, 100,100);
rec2.setFill(Color.GREEN);
rec2.setOpacity(0.);
pane.getChildren().addAll(rec1,rec2);
root.getChildren().add(pane);
root.getChildren().add(btnForward);
stage.setScene(scene);
stage.show();
}
Define the fill of the rectangle using css with a linear gradient which references looked-up colors for the left and right edges of the rectangle. (This can be inline or in an external style sheet.)
Define a couple of DoublePropertys representing the opacities of the left and right edge.
Define the looked-up colors on the rectangle or one of its parents using an inline style bound to the two double properties.
Use a timeline to change the values of the opacity properties.
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class FadeInRectangle extends Application {
#Override
public void start(Stage primaryStage) {
Group root = new Group();
Scene scene = new Scene(root, 400, 300, Color.BLACK);
primaryStage.setTitle("JavaFX Scene Graph Demo");
Pane pane = new Pane();
Rectangle rec1 = new Rectangle(0, 0, 300,200);
rec1.setFill(Color.RED);
Rectangle rec2 = new Rectangle(100, 50, 100,100);
rec2.setStyle("-fx-fill: linear-gradient(to right, left-col, right-col);");
final DoubleProperty leftEdgeOpacity = new SimpleDoubleProperty(0);
final DoubleProperty rightEdgeOpacity = new SimpleDoubleProperty(0);
root.styleProperty().bind(
Bindings.format("left-col: rgba(0,128,0,%f); right-col: rgba(0,128,0,%f);", leftEdgeOpacity, rightEdgeOpacity)
);
Button btnForward = new Button();
btnForward.setText(">");
btnForward.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(leftEdgeOpacity, 0)),
new KeyFrame(Duration.ZERO, new KeyValue(rightEdgeOpacity, 0)),
new KeyFrame(Duration.millis(500), new KeyValue(rightEdgeOpacity, 0)),
new KeyFrame(Duration.millis(1500), new KeyValue(leftEdgeOpacity, 1)),
new KeyFrame(Duration.millis(2000), new KeyValue(rightEdgeOpacity, 1)),
new KeyFrame(Duration.millis(2000), new KeyValue(leftEdgeOpacity, 1))
);
timeline.play();
}
});
pane.getChildren().addAll(rec1,rec2);
root.getChildren().add(pane);
root.getChildren().add(btnForward);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Resources