JavaFX Slider : How to drag the thumb only by increments - javafx

I am trying to implement the Slider such that user can drag only by given increments. I tried in different ways by using the Slider API, but didnt get the desired results. Below is a quick demo of what I had tried. I am expecting to drag the thumb only in increments of 10 not with intermediate values. snapToTicks is doing what I required, but only after finishing the drag. I am trying to not move the thumb till the next desired block increment is reached.
Can anyone let me know how can i achieve this. Below is the screenshot while dragging.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class SliderDemo extends Application {
public static void main(String... args){
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception {
Label label = new Label();
label.setStyle("-fx-font-size:30px");
Slider slider = new Slider(5,240,5);
slider.setBlockIncrement(10);
slider.setMajorTickUnit(10);
slider.setMinorTickCount(0);
slider.setShowTickLabels(true);
slider.setShowTickMarks(true);
slider.setSnapToTicks(true);
slider.valueProperty().addListener((obs,old,val)->label.setText((int)Math.round(val.doubleValue())+""));
VBox root = new VBox(slider,label);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(20));
root.setSpacing(20);
Scene scene = new Scene(root,600,200);
stage.setScene(scene);
stage.show();
}
}

The solution is to set the value of the slider directly inside of the listener. The listener will not be called again
final ChangeListener<Number> numberChangeListener = (obs, old, val) -> {
final double roundedValue = Math.floor(val.doubleValue() / 10.0) * 10.0;
slider.valueProperty().set(roundedValue);
label.setText(Double.toString(roundedValue));
};
slider.valueProperty().addListener(numberChangeListener);
If you use Math.floor() instead of round you get a more intuatuive behavior of the thumb.

Related

How to use modal dialog in this case?

I have a question. I need to make a GridPane with a directory choose that will then lead me to a modal dialog showing photos. I cannot figure how to do the modal dialog that also has to be a GridPane or a HBox...so the question is , how do I get to show a Modal Dialog after selecting the Folder and pressing the "Show" Button... Thanks a lot!
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
public class FotoView extends Application {
#Override
public void start(Stage primaryStage) {
TextField tf = new TextField();
Button b1 = new Button("Search");
Button b2 = new Button("Show");
DirectoryChooser dc = new DirectoryChooser();
GridPane gp = new GridPane();
gp.add(tf, 0 , 0);
gp.add(b1, 1, 0);
gp.add(b2, 0, 1);
b1.setOnAction(e-> dc.showDialog(primaryStage));
primaryStage.setScene(new Scene(gp)) ;
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
} ```
Below is a quick example where a first window has a button that opens up a DirectoryChooser. Once a directory has been selected a second smaller window opens up with the Modality set to APPLICATION_MODAL. In this second window you could add the image(s) that you load and add them to the GridPane.
import java.io.File;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.DirectoryChooser;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(final String[] args) {
launch(args);
}
#Override
public void start(final Stage aStage) throws Exception {
final HBox root = new HBox();
final Button browseBtn = new Button("Click to open a Directory chooser");
root.getChildren().add(browseBtn);
browseBtn.setOnAction(e -> {
final DirectoryChooser chooser = new DirectoryChooser();
final File dir = chooser.showDialog(aStage);
openNewModalStage(aStage, dir);
});
final Scene scene = new Scene(root, 500, 500);
aStage.setScene(scene);
aStage.show();
}
private void openNewModalStage(final Stage aStage, final File aDirectory) {
final Stage stage = new Stage();
final GridPane grid = new GridPane();
final Scene scene = new Scene(grid);
grid.setStyle("-fx-background-color:black");
grid.setPrefWidth(400);
grid.setPrefHeight(400);
// get your images from 'aDirectory' and add them to your grid pane.
stage.setScene(scene);
// set the new windows Modality.
stage.initModality(Modality.APPLICATION_MODAL);
stage.show();
}
}
This way you would only need the one button and the dialog would show as soon as you've selected a directory. However, if you would still want a Search and Show button then just store the directory as a variable and add a listener on the 'show' button and move the openNewModalStage call to that one and remove the second argument.
Edit:
Also, depending on how many images and exactly what you want to display in the modal window, you might want to reconsider the GridPane and use a TilePane, or an hbox/vbox inside of a scroll pane. It's just a thought but I don't know what you will be doing with the GridPane.

How to recognize Node bounds change in Scene?

Lets say I have some TextField which is placed deep in the panes layout structure. I want add listener or recognize in some way that TextField changed its position (x, y) in Scene. The question is - how can I achive it in a proper, reusable way?
I provide some test code. To recreate please drag stage border, which will cause TextField position change.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class App extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
TextField textField = new TextField();
Button button = new Button("Button");
HBox hBox = new HBox(textField, button);
hBox.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
StackPane stackPane = new StackPane(hBox);
stackPane.setPrefSize(600., 400.);
Scene scene = new Scene(stackPane);
stage.setScene(scene);
stage.show();
}
}
Add a listener to the localToSceneTransform property of the node.
node.localToSceneTransformProperty().addListener((o, oldValue, newValue) -> {
System.out.println("transform may have changed");
});
Note that this
Yields some "false positives", i.e. it may notify you, if the position hasn't actually changed.
Registering too many of those listeners to nodes may decrease the performance of your application, since doing so involves listening to changes for all nodes in the hierarchy up to the root node.
In addition to this a listener to the boundsInLocal property may be needed, if you also want to be notified to the size of the node itself changing.

Java9 bug in javafx PieChart labels

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class Main extends Application {
#Override
public void start(Stage stage) {
try {
Scene pie;
Scene begin;
//pie scene
ObservableList<PieChart.Data> pieChartData=FXCollections.observableArrayList();
PieChart pieChart = new PieChart(pieChartData);
Button btBack = new Button("Back");
pieChart.setTitle("Test");
VBox container = new VBox(20);
container.getChildren().addAll(pieChart,btBack);
container.setAlignment(Pos.CENTER);
BorderPane pane = new BorderPane();
pane.setCenter(container);
pie =new Scene(pane,800,600);
//begin scene
VBox container2 = new VBox(20);
Button btPie = new Button("pie");
container2.getChildren().add(btPie);
BorderPane pane2 = new BorderPane();
container2.setAlignment(Pos.CENTER);
pane2.setCenter(container2);
begin=new Scene(pane2,50,50);
//handler
btPie.setOnAction(e->{
pieChartData.clear();
for(int i=0;i<5;++i)
pieChartData.add(new PieChart.Data(""+i, i));
stage.setScene(pie);
});
btBack.setOnAction(e->stage.setScene(begin));
stage.setScene(begin);
stage.show();
} catch (Exception e) {
e.printStackTrace(); // exception handling: print the error message on the console
}
}
public static void main(String[] args) {
launch(args);
}
}
With the above code, it first shows the stage with a button "pie". Clicking the button shows a pie chart with a button "back". The back button is used to go back to the initial screen.
The problem in this code is that after showing the pie chart for the second time, the pie chart's labels suddenly become crammed.
It can be seen with
1. click pie
2. click back
3. click pie -> problem shown
I can see that there is a problem, but I can't really see the reason. Furthermore, this problem only arise in java9; it works well in java8.
Can anyone find me the reason please?
The suggestion by JKostikiadis in the comment:
... you can fix the bug by adding container.layout(); after the for loop and before the stage.setScene(pie); in order to force the VBox to layout its children
helped me.

Javafx make combobox expand upward

I have a window which has a combobox at the bottom. When I click on it, not all the options are visible because they are not inside the screen area anymore. How can I make the drop-down list display upwards instead of downards?
I have the ComboBox defined in SceneBuilder. I define it in my code this way:
#FXML
ComboBox fontsComboBox;
In my initialize() method of the controller assigned to that window I have set some properties:
fontComboBox.setVisibleRowCount(3);
fontComboBox.getItems().addAll(fontList);
fontComboBox.setValue(fontList[0]);
I'm pretty sure I need to add something here in order to make the dropdown list fo up.
Thanks,
Serban
This was a known bug till Java 8u45. It is fixed in Java 8u60.
https://bugs.openjdk.java.net/browse/JDK-8092942 (fixed in 8u60)
You can download JDK/JRE 8u60ea (early access) here to give it a try: https://jdk8.java.net/download.html
Java 8 Update 60 is scheduled GA for August 2015.
This example shows the problem. Simply open the combobox, even if you move the window down to screen, it won't show the list above the box.
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class ComboBoxTester extends Application {
#Override
public void start(Stage primaryStage) {
List<Integer> values = new ArrayList<>();
for (int i = 1; i < 100; i++) {
values.add(i);
}
ObservableList<Integer> items = FXCollections.observableArrayList(values);
ComboBox<Integer> comboBox = new ComboBox<>(items);
comboBox.getSelectionModel().clearAndSelect(0);
comboBox.setVisibleRowCount(5);
BorderPane root = new BorderPane();
root.setBottom(comboBox);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Java 8 Update 45 behaviour
Java 8 Update 60 ea behaviour

JavaFX button.arm() not working. Are there other ways to?

I am trying to auto press a button in my application so that it triggers a function.
As per the java docs, the .arm() button should do this. But it does not.
I also tried .fire(), but with no luck.
Any ideas on how to get a button be clicked on its own?
button.fire() works and is the right method to use to trigger an automated button press.
Here is a short sample which demonstrates it's use:
A timeline is started which automatically fires a button every second. When the button is fired it increments the counter label below the button.
import javafx.animation.*;
import javafx.application.Application;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Clicker extends Application {
#Override
public void start(Stage stage) throws Exception {
Label counter = new Label("0");
Button clicker = new Button("Auto-clicked");
clicker.setOnAction(event ->
counter.setText(
(1 + Integer.parseInt(counter.getText())) + ""
)
);
Timeline ticker = new Timeline(
new KeyFrame(
Duration.seconds(1),
event -> clicker.fire()
)
);
ticker.setCycleCount(1_000_000);
ticker.play();
VBox layout = new VBox(10, clicker, counter);
layout.setAlignment(Pos.CENTER);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch(Clicker.class);
}
}

Resources