JavaFX NumberAxis displays doubles - javafx

I've create a small app which uses a live animated line chart to display the number of files in a certain folder every 2 seconds. All works well but the chart/yAxis keeps displaying double values after every update. The folder can contain between 0 and 30.000 files ...
#FXML
public LineChart<String, Integer> myChart;
#FXML
public CategoryAxis xAxis;
#FXML
public NumberAxis yAxis;
...
yAxis.setMinorTickVisible(false);
yAxis.setTickMarkVisible(false);
yAxis.setTickUnit(1);
How do I make sure the y-Axis only contains/uses integer values ... ?
The x,5 values/rows should never be used.

The tick unit is automatically set if autoranging is enabled (which it is by default). See the documentation.
So one way is to turn off autoranging, and set the range manually. This may need you to update the range when the data change, but the following demonstrates the idea:
public class ChartTest extends Application {
#Override
public void start(Stage stage) throws IOException {
CategoryAxis xAxis = new CategoryAxis();
NumberAxis yAxis = new NumberAxis();
yAxis.setTickUnit(1);
yAxis.setMinorTickVisible(false);
yAxis.setTickMarkVisible(false);
yAxis.setAutoRanging(false);
Random rng = new Random();
XYChart.Series<String, Integer> data = new XYChart.Series<>();
int max = Integer.MIN_VALUE;
for (String x : "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("")) {
int y = rng.nextInt(10);
if (y > max) max = y ;
data.getData().add(new XYChart.Data<>(x, y));
}
yAxis.setUpperBound(max + 1);
data.setName("Data");
LineChart<String, Integer> chart = new LineChart(xAxis, yAxis);
chart.getData().add(data);
Scene scene = new Scene(new BorderPane(chart), 600, 800);
stage.setTitle("Chart Test");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
If you want to retain autoranging, you can use a tick label formatter that returns an empty string for non-integer values:
public class ChartTest extends Application {
#Override
public void start(Stage stage) throws IOException {
CategoryAxis xAxis = new CategoryAxis();
NumberAxis yAxis = new NumberAxis();
yAxis.setMinorTickVisible(false);
yAxis.setTickMarkVisible(false);
yAxis.setTickLabelFormatter(new StringConverter<Number>() {
#Override
public String toString(Number number) {
double d = number.doubleValue();
if (d == Math.rint(d)) {
return Integer.toString(number.intValue());
}
return "" ;
}
// Not used
#Override
public Number fromString(String s) {
return null;
}
});
Random rng = new Random();
XYChart.Series<String, Integer> data = new XYChart.Series<>();
for (String x : "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("")) {
int y = rng.nextInt(10);
data.getData().add(new XYChart.Data<>(x, y));
}
data.setName("Data");
LineChart<String, Integer> chart = new LineChart(xAxis, yAxis);
chart.getData().add(data);
Scene scene = new Scene(new BorderPane(chart), 600, 800);
stage.setTitle("Chart Test");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}

Related

javafx : move slider node as per the draggable node moved

Scenario : I have two panel one of them has slider and another has drag gable pane .
Case I : if Pane(i.e Drag gable ) moved in forward or backward direction .node on slider moved with respective dimension in same direction of pane moved.
Case II : this functionality works vice versa .
(Drag gable pane with slider)
(slider with drag gable Pane).
so how i can achieve it..
Thank You!
You create a property for the horizontal direction. Then you change the property whenever the slider or the pane moves. Depending on the property changes you move the pane.
public class Main extends Application {
DoubleProperty xProperty = new SimpleDoubleProperty();
double min = 0;
double max = 100;
#Override
public void start(Stage primaryStage) {
try {
Pane root = new Pane();
// rectangle
Pane pane = new Pane();
pane.setStyle("-fx-background-color:blue");
pane.setPrefSize(50, 50);
pane.relocate(min, 50);
// make rectangle movable
MouseGestures mg = new MouseGestures();
mg.makeDraggable(pane);
// slider
Slider slider = new Slider(min, max, min);
slider.valueProperty().bindBidirectional( xProperty);
root.getChildren().addAll(slider, pane);
// move horizontally, clamp horizontal movement
xProperty.addListener(new ChangeListener<Number>() {
#Override
public void changed( ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
// clamp x
double x = newValue.doubleValue();
if( x < min) {
x = min;
}
if( x > max) {
x = max;
}
pane.relocate( x, pane.getBoundsInParent().getMinY());
}
});
Scene scene = new Scene(root,1024,768);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
public class MouseGestures {
final DragContext dragContext = new DragContext();
public void makeDraggable(final Node node) {
node.setOnMousePressed(onMousePressedEventHandler);
node.setOnMouseDragged(onMouseDraggedEventHandler);
}
EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
Node node = (Node) event.getSource();
dragContext.x = node.getBoundsInParent().getMinX() - event.getScreenX();
}
};
EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
double offsetX = event.getScreenX() + dragContext.x;
xProperty.set(offsetX);
}
};
class DragContext {
double x;
}
}
}

javafx adding all subtotal of all column in tableview

I have Tableview here that allows me to multiply the price and the quantity and put that into the subtotal column my question is i want to get the sum of all the subtotals for all the items in the table , thanks
private TableView<Product> table = new TableView<Product>();
private final ObservableList<Product> data =
FXCollections.observableArrayList(
new Product("Notebook", 10, 12),
new Product("Eraser", 20, 12),
new Product("Pencil", 30, 12),
new Product("Pen", 40, 12),
new Product("Glue", 50, 12));
final HBox hb = new HBox();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Book Store Sample");
stage.setWidth(650);
stage.setHeight(550);
final Label label = new Label("Book Store");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn name = new TableColumn("Name");
name.setMinWidth(100);
name.setCellValueFactory(
new PropertyValueFactory<Product, String>("name"));
name.setCellFactory(TextFieldTableCell.forTableColumn());
name.setOnEditCommit(
new EventHandler<CellEditEvent<Product, String>>() {
#Override
public void handle(CellEditEvent<Product, String> t) {
((Product) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setName(t.getNewValue());
}
}
);
TableColumn priceCol = new TableColumn("Price");
priceCol.setMinWidth(100);
priceCol.setCellValueFactory(
new PropertyValueFactory<Product, String>("price"));
priceCol.setCellFactory(TextFieldTableCell.<Product, Number>forTableColumn(new NumberStringConverter()));
priceCol.setOnEditCommit(
new EventHandler<CellEditEvent<Product, Number>>() {
#Override
public void handle(CellEditEvent<Product, Number> t) {
((Product) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setPrice(t.getNewValue().intValue());
}
}
);
TableColumn quantityCol = new TableColumn("Quantity");
quantityCol.setMinWidth(200);
quantityCol.setCellValueFactory(
new PropertyValueFactory<Product, Number>("quantity"));
quantityCol.setCellFactory(TextFieldTableCell.<Product, Number>forTableColumn(new NumberStringConverter()));
quantityCol.setOnEditCommit(
new EventHandler<CellEditEvent<Product, Number>>() {
#Override
public void handle(CellEditEvent<Product, Number> t) {
((Product) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setQuantity(t.getNewValue().intValue());
}
}
);
TableColumn subTotalCol = new TableColumn("Sub Total");
subTotalCol.setMinWidth(200);
subTotalCol.setCellValueFactory(
new PropertyValueFactory<Product, String>("subTotal"));
table.setItems(data);
table.getColumns().addAll(name, priceCol, quantityCol, subTotalCol);
final TextField addName = new TextField();
addName.setPromptText("Name");
addName.setMaxWidth(name.getPrefWidth());
final TextField addPrice = new TextField();
addPrice.setMaxWidth(priceCol.getPrefWidth());
addPrice.setPromptText("Price");
final TextField addQuantity = new TextField();
addQuantity.setMaxWidth(quantityCol.getPrefWidth());
addQuantity.setPromptText("Quantity");
final Button addButton = new Button("Add");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
data.add(new Product(
name.getText(),
Integer.parseInt(addPrice.getText()),
Integer.parseInt(addQuantity.getText())));
addName.clear();
addPrice.clear();
addQuantity.clear();
}
});
hb.getChildren().addAll(addName, addPrice, addQuantity, addButton);
hb.setSpacing(3);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table, hb);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Product {
private final SimpleStringProperty name;
private final SimpleIntegerProperty price;
private final SimpleIntegerProperty quantity;
private final SimpleIntegerProperty subTotal;
private Product(String name, int price, int quantity) {
this.name = new SimpleStringProperty(name);
this.price = new SimpleIntegerProperty(price);
this.quantity = new SimpleIntegerProperty(quantity);
this.subTotal = new SimpleIntegerProperty();
NumberBinding multiplication = Bindings.multiply(this.priceProperty(), this.quantityProperty());
this.subTotalProperty().bind(multiplication);
}
public String getName() {
return name.get();
}
public SimpleStringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public int getPrice() {
return price.get();
}
public SimpleIntegerProperty priceProperty() {
return price;
}
public void setPrice(int price) {
this.price.set(price);
}
public int getQuantity() {
return quantity.get();
}
public SimpleIntegerProperty quantityProperty() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity.set(quantity);
}
public int getSubTotal() {
return subTotal.get();
}
public SimpleIntegerProperty subTotalProperty() {
return subTotal;
}
public void setSubTotal(int subTotal) {
this.subTotal.set(subTotal);
}
}
}
You can create a binding that tracks the total of all the subtotal values:
IntegerBinding total = Bindings.createIntegerBinding(() ->
table.getItems().stream().collect(Collectors.summingInt(Product::getSubTotal)),
table.getItems());
The first parameter to this is a function that computes the total of the results of calling product.getSubTotal() on each element in table.getItems(). The second argument ensures that this binding is marked as invalid (so can be recomputed) any time table.getItems() is invalidated.
Using the default mechanism of constructing a list, as you do with ObservableList<Product> data = FXCollections.observableArrayList(...), the list will only be invalidated when items are added, removed, or replaced, but not if the subTotal of an existing item changes. To make this happen, you need to modify that line to use a extractor:
private final ObservableList<Product> data = FXCollections.observableList(Arrays.asList(
new Product("Notebook", 10, 12),
new Product("Eraser", 20, 12),
new Product("Pencil", 30, 12),
new Product("Pen", 40, 12),
new Product("Glue", 50, 12)),
product -> new Observable[] {product.subTotalProperty()});
Now you can do something like
Label totalLabel = new Label();
totalLabel.textProperty().bind(Bindings.format("Total: %d", total));
Update
Here is what the IntegerBinding code looks like without a lambda expression, though it is far less clear and I don't recommend doing this:
IntegerBinding total = Bindings.createIntegerBinding(new Callable<Integer>() {
#Override
public Integer call() {
return table.getItems().stream().collect(Collectors.summingInt(
new ToIntFunction<Product>() {
#Override
public int applyAsInt(Product product) {
return product.getSubTotal();
}
}));
}
}, table.getItems());

position xAxis values under the bars in JavaFx

I am creating a bar chart as you can see in photo. The problem is that the name of bars on XAxis are some how not match to the bar.
The tick label rotation is set to -45 in fxml. How can I shift them exactly under the bar?
public class MostComputerizedController {
#FXML
private BarChart<String, Number> barChart;
#FXML
private CategoryAxis orgNameAxis;
#FXML
private NumberAxis yAxis;
#FXML
private Label itsfField;
private ObservableList<String> orgNames = FXCollections
.observableArrayList();
private DataConstructor dc = new DataConstructor();
private int numberOfOrganizations;
private List<Double> sumOfOrgsITSF = new ArrayList<Double>();
/**
* sets the name of x axis, with the name of organizations
*/
#FXML
private void initialize() {
dc.findSortedAssignedOrg();
dc.orgFuncFuncType();
orgNames.addAll(dc.getOrgFuncFunctype().keySet());
orgNameAxis.setCategories(orgNames);
orgNameAxis.setLabel("Name of Organizations");
orgNameAxis.tickLabelFontProperty().set(Font.font(9));
yAxis.setLabel("Saturation");
numberOfOrganizations = dc.getSortedAssignedOrg().size();
}
/**
* sets organization and their saturation
*/
public void setOrgData() {
XYChart.Series<String, Number> seriesGreen = new XYChart.Series<>();
XYChart.Series<String, Number> seriesYellow = new XYChart.Series<>();
seriesGreen.setName("IT Saturation Satisfying");
seriesYellow.setName("IT Saturation not Satisfying");
for (Entry<String, List<Double>> entry : dc.getOrgFuncFunctype()
.entrySet()) {
sumOfOrgsITSF.add(entry.getValue().get(0));
if (entry.getValue().get(0) > 50) {
seriesGreen.getData().add(
new XYChart.Data<String, Number>(entry.getKey(), entry
.getValue().get(0)));
} else if ((entry.getValue().get(0) <= 50)) {
seriesYellow.getData().add(
new XYChart.Data<String, Number>(entry.getKey(), entry
.getValue().get(0)));
}
}
double value = sumOfOrgsITSF.stream().mapToDouble(Double::doubleValue)
.sum()
/ numberOfOrganizations;
itsfField.setText(String.format("%.0f", value) + "%");
barChart.setBarGap(1);
barChart.setCategoryGap(10);
barChart.getData().addAll(seriesGreen, seriesYellow);
}
Put all your data in a single series.
series1.getData().add(new Data<Number, String>(Integer1),(Title1)));
series1.getData().add(new Data<Number, String>(Integer2),(Title2)));
series1.getData().add(new Data<Number, String>(Integer3),(Title3)));
Results:

How to zoom-out javafx linechart after it was zoomed in?

I've got this code right now.
public class ScalableChart extends VBox implements Initializable{
#FXML
private LineChart<Number, Number> chart;
#FXML
private Rectangle zoomRect;
#FXML
private StackPane pane;
private boolean selectionGestureStarted = false;
private ObjectProperty<javafx.geometry.Point2D> mouseAnchor = new SimpleObjectProperty<>();;
public ScalableChart() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(
"hello.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
zoomRect.setManaged(true);
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
#FXML
protected void reset(MouseEvent event) {
System.out.println("Reset");
chart.getXAxis().setAutoRanging(true);
chart.getYAxis().setAutoRanging(true);
final NumberAxis xAxis = (NumberAxis)chart.getXAxis();
xAxis.setLowerBound(0);
xAxis.invalidateRange(chart.getData().);
xAxis.setUpperBound(Collections.max(chart.getData().));
final NumberAxis yAxis = (NumberAxis)chart.getYAxis();
yAxis.setLowerBound(0);
yAxis.setUpperBound(1000);
zoomRect.setWidth(0);
zoomRect.setHeight(0);
}
#FXML
protected void startSettingZoom(MouseEvent event) {
setSelectionGestureStarted(true);
mouseAnchor.set(new javafx.geometry.Point2D(event.getX(), event.getY()));
zoomRect.setWidth(0);
zoomRect.setHeight(0);
}
#FXML
protected void changeZoomSettings(MouseEvent event) {
if (isSelectionGestureStarted()) {
zoomRect.setX(Math.min(event.getX(), mouseAnchor.get().getX()));
zoomRect.setY(Math.min(event.getY(), mouseAnchor.get().getY()));
zoomRect.setWidth(Math.abs(event.getX() - mouseAnchor.get().getX()));
zoomRect.setHeight(Math.abs(event.getY() - mouseAnchor.get().getY()));
}
}
#FXML
protected void stopSettingZoom(MouseEvent event) {
chart.getXAxis().setAutoRanging(false);
chart.getYAxis().setAutoRanging(false);
doZoom(zoomRect, chart);
zoomRect.setWidth(0);
zoomRect.setHeight(0);
setSelectionGestureStarted(false);
}
private void doZoom(javafx.scene.shape.Rectangle zoomRect, LineChart<Number, Number> chart) {
javafx.geometry.Point2D zoomTopLeft = new javafx.geometry.Point2D(zoomRect.getX(), zoomRect.getY());
javafx.geometry.Point2D zoomBottomRight = new javafx.geometry.Point2D(zoomRect.getX() + zoomRect.getWidth(), zoomRect.getY() + zoomRect.getHeight());
final NumberAxis yAxis = (NumberAxis) chart.getYAxis();
javafx.geometry.Point2D yAxisInScene = yAxis.localToScene(0, 0);
final NumberAxis xAxis = (NumberAxis) chart.getXAxis();
javafx.geometry.Point2D xAxisInScene = xAxis.localToScene(0, 0);
double xOffset = zoomTopLeft.getX() - yAxisInScene.getX() ;
double yOffset = zoomBottomRight.getY() - xAxisInScene.getY();
double xAxisScale = xAxis.getScale();
double yAxisScale = yAxis.getScale();
xAxis.setLowerBound(xAxis.getLowerBound() + xOffset / xAxisScale);
xAxis.setUpperBound(xAxis.getLowerBound() + zoomRect.getWidth() / xAxisScale);
yAxis.setLowerBound(yAxis.getLowerBound() + yOffset / yAxisScale);
yAxis.setUpperBound(yAxis.getLowerBound() - zoomRect.getHeight() / yAxisScale);
System.out.println(yAxis.getLowerBound() + " " + yAxis.getUpperBound());
zoomRect.setWidth(0);
zoomRect.setHeight(0);
}
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
chart.setTitle("Title");
LineChart.Series<Number, Number> series = new LineChart.Series<Number, Number>();
series.getData().add(new XYChart.Data<Number, Number>(10, 10));
series.getData().add(new XYChart.Data<Number, Number>(20, 20));
chart.getData().add(series);
}
public boolean isSelectionGestureStarted() {
return selectionGestureStarted;
}
public void setSelectionGestureStarted(boolean selectionGestureStarted) {
this.selectionGestureStarted = selectionGestureStarted;
}
}
Also right now I can't see selection rectangle when it's on chart. I'm using the stackpane and I tried to add rectangle before the chart and also after the chart. None of this is working.
Make a transparent rectangle with red border:
zoomRect.setStyle("-fx-fill:Transparent; -fx-stroke:RED");
Add it to the Scene root and call setX() adn seY() to place the upper left corner in the right position.

JAVAFX game collision

I am making a school project in JAVAFX and I can't figure out how to collide with my 2 objects(bullet and enemy tank). Can someone tell me the right way to do it? I am trying more than 3 weeks... google everything but still not working.
public class TankGame extends Application {
private final static int WIDTH = 800;
private final static int HEIGHT = 600;
private final static Image BACKGROUND_IMAGE = new Image(TankGame.class.getResource("imgs/Tank_back.png").toString());
private final static Image PATRONA = new Image(TankGame.class.getResource("imgs/Tank_patrona.png").toString());
private Animation modelAnimacePatrony;
private Group patrona;
private double smerStrelyX, smerStrelyY;
private Otaceni otaceni = new Otaceni();
private TankHrac tankHrac = new TankHrac();
private TankProtivnik tankProtivnik = new TankProtivnik();
#Override
public void start(Stage primaryStage) {
final ImageView background = new ImageView(BACKGROUND_IMAGE);
final ImageView bullet = new ImageView(PATRONA);
patrona = new Group(bullet);
final Group root = new Group(background, tankHrac, tankProtivnik, patrona);//deti
patrona.setVisible(false);
Scene scene = new Scene(root, WIDTH, HEIGHT); //okno
tankHrac.setTranslateX(50);//defaultni vyskyt modelu
tankHrac.setTranslateY(50);//defaultni vyskyt modelu
tankProtivnik.setTranslateX(350);//defaultni vyskyt modeluProtivnika
tankProtivnik.setTranslateY(150);//defaultni vyskyt modeluProtivnika
smerStrelyX = tankHrac.getTranslateX();
smerStrelyY = tankHrac.getTranslateY()-250;
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent ke) {
/**
* Shooting
*/
if( ke.getCode() == KeyCode.SPACE ) {
if(!patrona.isVisible()){
//patrona.setVisible(true);
shooting(smerStrelyX,smerStrelyY, tankHrac);
}
}
}
});
primaryStage.setTitle("Tank 1.0");
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setResizable(false);
}
public void shooting(double smerStrelyX, double smerStrelyY, TankHrac jakyModelTanku){
patrona.setVisible(true);
modelAnimacePatrony = TranslateTransitionBuilder.create()
.node(patrona)
.fromX(jakyModelTanku.getTranslateX()+30)
.toX(smerStrelyX+30)
.fromY(jakyModelTanku.getTranslateY()+30)
.toY(smerStrelyY+30)
.duration(Duration.seconds(1))
.onFinished(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent t){
modelAnimacePatrony.stop();
patrona.setVisible(false);
}
})
.build();
modelAnimacePatrony.play();
}
here are all source files: https://www.dropbox.com/sh/1iq98jtxh8m06tt/7Y9LQSjfYs

Resources