Sort TreeView by name - javafx

I have this very simple example of TreeView.
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableColumn.CellDataFeatures;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableView;
import javafx.stage.Stage;
public class TreeTableViewSample extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
stage.setTitle("Tree Table View Samples");
final Scene scene = new Scene(new Group(), 200, 400);
Group sceneRoot = (Group)scene.getRoot();
//Creating tree items
final TreeItem<String> childNode1 = new TreeItem<>("Child Node 1");
final TreeItem<String> childNode2 = new TreeItem<>("Child Node 2");
final TreeItem<String> childNode3 = new TreeItem<>("Child Node 3");
//Creating the root element
final TreeItem<String> root = new TreeItem<>("Root node");
root.setExpanded(true);
//Adding tree items to the root
root.getChildren().setAll(childNode1, childNode2, childNode3);
//Creating a column
TreeTableColumn<String,String> column = new TreeTableColumn<>("Column");
column.setPrefWidth(150);
//Defining cell content
column.setCellValueFactory((CellDataFeatures<String, String> p) ->
new ReadOnlyStringWrapper(p.getValue().getValue()));
//Creating a tree table view
final TreeTableView<String> treeTableView = new TreeTableView<>(root);
treeTableView.getColumns().add(column);
treeTableView.setPrefWidth(152);
treeTableView.setShowRoot(true);
sceneRoot.getChildren().add(treeTableView);
stage.setScene(scene);
stage.show();
}
}
I'm interested how I can sort the tree nodes by name?
Is this functionality already implemented in JavaFX or I need to implement custom tree cell?
Is there any example that I can use?

By default, the items on every TableColumn can be sorted just by clicking on its header, once or twice, to get the default sort order (ascending or descending by default).
The default comparator is String.compareTo, which compares two strings lexicographically.
But you can implement your own. For instance, this will sort by the length of the strings:
// compare by length of the strings
column.setComparator(Comparator.comparing(String::length));
And this one will sort first by length, then in case of equal length, by name:
// compare by length first, and then lexicographically
column.setComparator(Comparator.comparing(String::length).thenComparing(String::compareTo));
EDIT: Since the example refers to a TreeTableView, but the OP asks for a TreeView, this is how the items can be sorted:
1) Since we are adding a collection of items, we can sort it before adding the children to the root
#Override
public void start(Stage stage) {
stage.setTitle("Tree Table View Samples");
final Scene scene = new Scene(new Group(), 200, 400);
Group sceneRoot = (Group)scene.getRoot();
//Creating tree items
final TreeItem<String> childNode1 = new TreeItem<>("Child Node 10");
final TreeItem<String> childNode2 = new TreeItem<>("Child Node Two");
final TreeItem<String> childNode3 = new TreeItem<>("Child Node 3");
//Creating the root element
final TreeItem<String> root = new TreeItem<>("Root node");
root.setExpanded(true);
List<TreeItem<String>> list = Arrays.asList(childNode1, childNode2, childNode3);
// sort by length of the item's names
list.sort(Comparator.comparing(t->t.getValue().length()));
//Adding tree items to the root
root.getChildren().setAll(list);
TreeView<String> tree = new TreeView<> (root);
sceneRoot.getChildren().add(tree);
stage.setScene(scene);
stage.show();
}
2) Once we have added the items to the root we can provide a Comparator to the root:
#Override
public void start(Stage stage) {
stage.setTitle("Tree Table View Samples");
final Scene scene = new Scene(new Group(), 200, 400);
Group sceneRoot = (Group)scene.getRoot();
//Creating tree items
final TreeItem<String> childNode1 = new TreeItem<>("Child Node 10");
final TreeItem<String> childNode2 = new TreeItem<>("Child Node Two");
final TreeItem<String> childNode3 = new TreeItem<>("Child Node 3");
//Creating the root element
final TreeItem<String> root = new TreeItem<>("Root node");
root.setExpanded(true);
//Adding tree items to the root
root.getChildren().setAll(childNode1, childNode2, childNode3);
TreeView<String> tree = new TreeView<> (root);
// sort by length of the item's names
root.getChildren().sort(Comparator.comparing(t->t.getValue().length()));
sceneRoot.getChildren().add(tree);
stage.setScene(scene);
stage.show();
}

Related

JAVAFX How can I create a method for setting objects visible?

I am new to Java and JavaFX so sorry if this question seems a bit obvious but it is doing my head in. I have tried googling solutions but haven't been able to find anything so far.
Basically I am trying to re-use some code in Java FX to hide / show some objects when a button is pressed. Rather than copy / paste the whole code again, I want to put it in a method (or something similar) that gets called on different occasions.
So here is a simple code example that I hope explains what I am trying to do:
package src;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class example extends Application
{
#Override
public void start(Stage stage)
{
Button b1 = new Button();
b1.setText("Show Label 1 Only");
Button b2 = new Button();
b2.setText("Show Label 2 Only");
Button b3 = new Button();
b3.setText("Show Label 2 and 3");
Label l1 = new Label();
l1.setText("Label 1");
Label l2 = new Label();
l2.setText("Label 2");
Label l3 = new Label();
l3.setText("Label 3");
b1.setOnAction(e ->
{
l1.setVisible(true);
l2.setVisible(false);
l3.setVisible(false);
});
b2.setOnAction(e ->
{
l1.setVisible(false);
l2.setVisible(true);
l3.setVisible(false);
});
b3.setOnAction(e ->
{
l1.setVisible(false);
l2.setVisible(true);
l3.setVisible(true);
});
VBox root = new VBox();
root.setSpacing(10);
root.setAlignment(Pos.CENTER);
l1.setVisible(false);
l2.setVisible(false);
l3.setVisible(false);
root.getChildren().addAll(b1, b2, b3, l1, l2, l3);
Scene scene = new Scene(root, 200, 200);
stage.setScene(scene);
stage.setTitle("Example");
stage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
As you can see, the first 2 lines of the code for B2 and B3 are the same, so it would be nice if I could just put these lines in a method that gets called in both scenarios. This is in essence what i am trying to achieve.
Any guidance would be much appreciated. Like I said, I'm new to JAVA / JAVAFX so sorry if it seems like an obvious question.
One simple way is to make the labels instance variables and just to define a method taking a boolean for each one, indicating if it should be visible:
public class Example extends Application {
private Label l1;
private Label l2;
private Label l3;
private void labelVisibility(boolean l1Visible, boolean l2Visible, boolean l3Visible) {
l1.setVisible(l1Visible);
l2.setVisible(l2Visible);
l3.setVisible(l3Visible);
}
#Override
public void start(Stage stage) {
Button b1 = new Button();
b1.setText("Show Label 1 Only");
Button b2 = new Button();
b2.setText("Show Label 2 Only");
Button b3 = new Button();
b3.setText("Show Label 2 and 3");
l1 = new Label();
l1.setText("Label 1");
l2 = new Label();
l2.setText("Label 2");
l3 = new Label();
l3.setText("Label 3");
b1.setOnAction(e -> labelVisibility(true, false, false));
b2.setOnAction(e -> labelVisibility(false, true, false));
b3.setOnAction(e -> labelVisibility(false, true, true));
VBox root = new VBox();
root.setSpacing(10);
root.setAlignment(Pos.CENTER);
labelVisibility(false, false, false);
root.getChildren().addAll(b1, b2, b3, l1, l2, l3);
Scene scene = new Scene(root, 200, 200);
stage.setScene(scene);
stage.setTitle("Example");
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If you don't want to expand the scope of the labels (and want a bit more generality), your method can take a list of all the labels, and a list of the ones you want visible:
public class Example extends Application {
private void labelVisibility(List<Node> allNodes, Node... visibleNodes) {
List<Node> visibleNodeList = Arrays.asList(visibleNodes);
for (Node node : allNodes) {
node.setVisible(visibleNodeList.contains(node));
}
}
#Override
public void start(Stage stage) {
Button b1 = new Button();
b1.setText("Show Label 1 Only");
Button b2 = new Button();
b2.setText("Show Label 2 Only");
Button b3 = new Button();
b3.setText("Show Label 2 and 3");
Label l1 = new Label();
l1.setText("Label 1");
Label l2 = new Label();
l2.setText("Label 2");
Label l3 = new Label();
l3.setText("Label 3");
List<Node> allLabels = List.of(l1, l2, l3);
b1.setOnAction(e -> labelVisibility(allLabels, l1));
b2.setOnAction(e -> labelVisibility(allLabels, l2));
b3.setOnAction(e -> labelVisibility(allLabels, l2, l3));
VBox root = new VBox();
root.setSpacing(10);
root.setAlignment(Pos.CENTER);
labelVisibility(allLabels);
root.getChildren().addAll(b1, b2, b3, l1, l2, l3);
Scene scene = new Scene(root, 200, 200);
stage.setScene(scene);
stage.setTitle("Example");
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Javafx ThreeView Manual Settings Selected

I have a view, using TreeView as a tree view display. I want to manually set a child node as the selected state. How should I set it?
TreeView selections work basically the same as ListView selections, as long as you ignore indices. Just pass the TreeItem you want to select to the select method of the selection model:
#Override
public void start(Stage primaryStage) throws Exception {
TreeItem<String> c1 = new TreeItem<>("child 1");
TreeItem<String> c2 = new TreeItem<>("child 2");
TreeItem<String> root = new TreeItem<>("root");
root.getChildren().addAll(c1, c2);
TreeView<String> tv = new TreeView<>(root);
// select c2
tv.getSelectionModel().select(c2);
Scene scene = new Scene(tv);
primaryStage.setScene(scene);
primaryStage.show();
}

change value in combobox ["example"] to "example" in javafx

i am using a combobox to get data from the database on stage.show(), so far i can retrieve the data and also implement my changelistener on the combobox.
the problem is that i am getting ["example"] from the database instead of "example". its my first time of using javafx and don;t know how the output is supposed to look like, but this one is strange to me.
below is a screenshot of it and also my code
[http://i.stack.imgur.com/QWawZ.png]
package libman;
import java.sql.*;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import javafx.stage.*;
import javafx.scene.control.*;
import javafx.scene.text.*;
import javafx.collections.*;
/**
/**
*
* #author kels
*/
public class BorrowMenu extends Application {
private ObservableList<ObservableList> data;
DBOperator login;
ResultSet rs;
Statement stat;
private String getval;
#Override
#SuppressWarnings({"static-access", "Convert2Lambda"})
public void start(Stage primaryStage){
//define the UI elements
Label lblID = new Label("Name: ");
Label lblTitle = new Label("Book Title: ");
Label lblauthor = new Label("Author: ");
Label lblissue = new Label("Issue Date: ");
Label lblreturn = new Label("Return Date: ");
ComboBox title = new ComboBox();
TextField txtid = new TextField();
TextField txtitle = new TextField();
TextField txtauthor = new TextField();
TextField txtissue = new TextField();
TextField txtreturn = new TextField();
//set prompt text
txtid.setPromptText("Enter Borrower's Name");
txtitle.setPromptText("Enter Book Title");
txtauthor.setPromptText("Enter Author's Name");
txtissue.setPromptText("Enter Issue Date");
txtreturn.setPromptText("Enter Return Date");
title.setPromptText("Fills books from database");
//ToolTip ttip = new ToolTip("Back Menu");
Button btn = new Button("Borrow Book");
Button btnexit = new Button("Menu>>");
btnexit.setTooltip(new Tooltip("Back to Menu"));
//set the gripane to add in components
GridPane gridpane = new GridPane();
gridpane.setPadding(new Insets(20));
gridpane.setHgap(5);
gridpane.setVgap(5);
//set components
gridpane.setHalignment(lblID, HPos.RIGHT);
gridpane.add(lblID, 0,0);
gridpane.setHalignment(txtid, HPos.RIGHT);
gridpane.add(txtid,1,0);
gridpane.setHalignment(lblTitle, HPos.RIGHT);
gridpane.add(lblTitle, 0,1);
gridpane.setHalignment(title, HPos.RIGHT);
gridpane.add(title, 1,1);
gridpane.setHalignment(lblauthor, HPos.RIGHT);
gridpane.add(lblauthor, 0,2);
gridpane.setHalignment(txtauthor, HPos.RIGHT);
gridpane.add(txtauthor, 1,2);
gridpane.setHalignment(lblissue, HPos.RIGHT);
gridpane.add(lblissue, 0,3);
gridpane.setHalignment(txtissue, HPos.RIGHT);
gridpane.add(txtissue, 1,3);
gridpane.setHalignment(lblreturn, HPos.RIGHT);
gridpane.add(lblreturn, 0,4);
gridpane.setHalignment(txtreturn, HPos.RIGHT);
gridpane.add(txtreturn, 1,4);
gridpane.setHalignment(btn, HPos.RIGHT);
gridpane.add(btn, 1,5);
gridpane.setHalignment(btnexit, HPos.RIGHT);
gridpane.add(btnexit, 2,5);
//display the values from db to combobox on windows launch
data = FXCollections.observableArrayList();
primaryStage.setOnShowing(new EventHandler<WindowEvent>(){
#Override
public void handle(WindowEvent event){
try{
login = new DBOperator();
stat = login.getStatement();
rs=stat.executeQuery("SELECT * FROM BOOKDB");
while(rs.next()){
ObservableList<String> row =FXCollections.observableArrayList();
row.add(rs.getString("Title"));
// row.add(rs.getString("Author"));
data.add(row);
}
title.setItems(data);
rs.close();
}
catch(SQLException ex){
System.out.println("Driver Not Found!!!" + ex);
System.exit(0);
}
}
});
//add the gridpane to the stackpane
StackPane root = new StackPane();
root.getChildren().add(gridpane);
//title.setO
Scene scene = new Scene(root, 380,220);
primaryStage.setTitle("Borrow Menu");
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
//System.out.println(row);
//activate the combo listener at selection
title.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Object>(){
#Override
public void changed(ObservableValue<? extends Object> observable, Object oldvalue, Object newvalue){
System.out.println(newvalue.toString());
// txtauthor.setText(newvalue.toString());
}
});
}
public static void main(String [] args){
launch(args);
}
}
Please help thanks
You are getting the correct result for what you are actually doing: ObservableList<ObservableList> data bundles two collections.
For each item of the comboBox you will have a collection, so that's why you see "[ ]" when it is rendered.
This minimum sample reproduces your case:
#Override
public void start(Stage primaryStage) {
ComboBox title = new ComboBox();
StackPane root = new StackPane();
root.getChildren().add(title);
ObservableList<ObservableList> data = FXCollections.observableArrayList();
List<String> test = Arrays.asList("Test", "Example");
for(String s : test) {
ObservableList<String> row =FXCollections.observableArrayList();
row.add(s);
data.add(row);
}
title.setItems(data);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
with the same result you have:
You need to simplify your collection: ObservableList<String> data.
#Override
public void start(Stage primaryStage) {
ComboBox<String> title = new ComboBox();
StackPane root = new StackPane();
root.getChildren().add(title);
ObservableList<String> data = FXCollections.observableArrayList();
List<String> test = Arrays.asList("Test", "Example");
for(String s : test) {
data.add(s);
}
title.setItems(data);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
And you will have the expected result:
EDIT
In the case you want to add several items from the database to each item of the combo, you just need to provide a way to render the data, using ComboBox.setCellFactory(), so you can override the default method that produces the string [item1, item2, ... ].

JavaFX Circle object not registering mouse events properly

I want the user to be able to drag-move the circles around the pane. The circles dont seem to register (almost) no mouse events (as defined in the end). I have the same exact code for an empty pane it works just fine. Also if I change
circle1.setOnMouseDragged
to
paneForCircles.setOnMouseDragged
it works just fine but its not what I want because I need to manipulate both circles. Any ideas ? I would appreciate it if you could also tell me how to hide the part of the circle that overlaps with the adjacent elements if its center is too close to the pane border.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class Ex168 extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Circle circle1 = new Circle(30);
Circle circle2 = new Circle(35);
circle1.setCenterX(100);
circle1.setCenterY(100);
circle2.setCenterX(150);
circle2.setCenterY(120);
circle1.setStroke(Color.BLACK);
circle1.setFill(null);
circle2.setStroke(Color.BLACK);
circle2.setFill(null);
VBox vBoxForScene = new VBox(5);
vBoxForScene.setPadding(new Insets(5));
vBoxForScene.setAlignment(Pos.TOP_CENTER);
Pane paneForCircles = new Pane();
paneForCircles.setStyle("-fx-border-color: black");
vBoxForScene.heightProperty().addListener(ov -> paneForCircles.setPrefHeight(vBoxForScene.heightProperty().divide(1.2).doubleValue()));
paneForCircles.setPrefHeight(300);
HBox hBoxForFields = new HBox(5);
hBoxForFields.setAlignment(Pos.CENTER);
hBoxForFields.setSpacing(5);
// VBofForLeftFields
VBox vBoxForLeftFields = new VBox(5);
vBoxForLeftFields.setAlignment(Pos.CENTER_LEFT);
Label lblCircle1 = new Label("Enter Circle 1 info");
lblCircle1.setAlignment(Pos.TOP_LEFT);
TextField tfCircle1CenterX = new TextField();
tfCircle1CenterX.textProperty().bind(circle1.centerXProperty().asString());
TextField tfCircle1CenterY = new TextField();
tfCircle1CenterY.textProperty().bind(circle1.centerYProperty().asString());
TextField tfCircle1Radius = new TextField();
tfCircle1Radius.textProperty().bind(circle1.radiusProperty().asString());
tfCircle1CenterX.setPrefColumnCount(5);
tfCircle1Radius.setPrefColumnCount(5);
tfCircle1CenterY.setPrefColumnCount(5);
Label lblCenterX = new Label("Center x:", tfCircle1CenterX);
Label lblCenterY = new Label("Center x:", tfCircle1CenterY);
Label lblCircle1Radius= new Label("Radius: ", tfCircle1Radius);
lblCenterX.setContentDisplay(ContentDisplay.RIGHT);
lblCenterY.setContentDisplay(ContentDisplay.RIGHT);
lblCircle1Radius.setContentDisplay(ContentDisplay.RIGHT);
//VBoxForRightFields
VBox vBoxForRightFields = new VBox(5);
Label lblCircle2 = new Label("Enter Circle 2 info");
TextField tfCircle2CenterX = new TextField();
TextField tfCircle2CenterY = new TextField();
TextField tfCircle2Radius = new TextField();
tfCircle2CenterX.setPrefColumnCount(5);
tfCircle2CenterX.textProperty().bind(circle2.centerXProperty().asString());
tfCircle2Radius.setPrefColumnCount(5);
tfCircle2Radius.textProperty().bind(circle2.radiusProperty().asString());
tfCircle2CenterY.setPrefColumnCount(5);
tfCircle2CenterY.textProperty().bind(circle2.centerYProperty().asString());
Label lblCenter2X = new Label("Center x:", tfCircle2CenterX);
Label lblCenter2Y = new Label("Center x:", tfCircle2CenterY);
Label lblCircle2Radius= new Label("Radius: ", tfCircle2Radius);
lblCenter2X.setContentDisplay(ContentDisplay.RIGHT);
lblCenter2Y.setContentDisplay(ContentDisplay.RIGHT);
lblCircle2Radius.setContentDisplay(ContentDisplay.RIGHT);
vBoxForRightFields.getChildren().addAll(lblCircle2, lblCenter2X, lblCenter2Y, lblCircle2Radius);
vBoxForLeftFields.getChildren().addAll(lblCircle1, lblCenterX, lblCenterY, lblCircle1Radius);
hBoxForFields.getChildren().addAll(vBoxForLeftFields, vBoxForRightFields);
Label lblResult = new Label("Do the two circles intersect?");
Button btReDrawCircles = new Button("Redraw Circles");
vBoxForScene.getChildren().addAll(lblResult, paneForCircles, hBoxForFields, btReDrawCircles);
circle1.setOnMouseDragged(e -> {
System.out.println(e.getX());
circle1.setCenterX(e.getX());
circle1.setCenterY(e.getY());
});
circle2.setOnMouseDragged(e -> {
circle2.setCenterX(e.getX());
circle2.setCenterY(e.getY());
});
paneForCircles.getChildren().addAll(circle1, circle2);
Scene scene = new Scene(vBoxForScene);
primaryStage.setScene(scene);
primaryStage.setMinHeight(400);
primaryStage.setMinWidth(340);
primaryStage.setAlwaysOnTop(true);
primaryStage.show();
circle1.requestFocus();
}
}
This code on the other hand, which is supposed to do the same thing, works perfectly
public class CircleDraggingSample extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
final double RADIUS=10;
Pane pane = new Pane();
pane.setPrefHeight(300);
pane.setPrefWidth(300);
Circle circle1 = new Circle(RADIUS);
circle1.setCenterX(30);
circle1.setCenterY(30);
Circle circle2 = new Circle(RADIUS);
circle2.setCenterX(100);
circle2.setCenterY(100);
Line line = new Line();
line.endXProperty().bind(circle2.centerXProperty());
line.endYProperty().bind(circle2.centerYProperty());
line.startXProperty().bind(circle1.centerXProperty());
line.startYProperty().bind(circle1.centerYProperty());
pane.getChildren().addAll(circle1, circle2, line);
circle2.setOnMouseDragged(e -> {
circle2.setCenterX(e.getX());
circle2.setCenterY(e.getY());
});
circle1.setOnMouseDragged(e -> {
circle1.setCenterX(e.getX());
circle1.setCenterY(e.getY());
});
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Even though you have posted an example, I'd rather show you a way with mine how it is done in general. There are several ways, this is one that works:
public class DragNodes extends Application {
public static List<Circle> circles = new ArrayList<Circle>();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Group root = new Group();
Circle circle1 = new Circle( 100, 100, 50);
circle1.setStroke(Color.GREEN);
circle1.setFill(Color.GREEN.deriveColor(1, 1, 1, 0.3));
Circle circle2 = new Circle( 200, 200, 50);
circle2.setStroke(Color.BLUE);
circle2.setFill(Color.BLUE.deriveColor(1, 1, 1, 0.3));
Line line = new Line();
line.setStrokeWidth(20);
// binding
line.startXProperty().bind(circle1.centerXProperty());
line.startYProperty().bind(circle1.centerYProperty());
line.endXProperty().bind(circle2.centerXProperty());
line.endYProperty().bind(circle2.centerYProperty());
MouseGestures mg = new MouseGestures();
mg.makeDraggable( circle1);
mg.makeDraggable( circle2);
mg.makeDraggable( line);
root.getChildren().addAll(circle1, circle2, line);
primaryStage.setScene(new Scene(root, 1024, 768));
primaryStage.show();
}
public static class MouseGestures {
class DragContext {
double x;
double y;
}
DragContext dragContext = new DragContext();
public void makeDraggable( Node node) {
node.setOnMousePressed( onMousePressedEventHandler);
node.setOnMouseDragged( onMouseDraggedEventHandler);
node.setOnMouseReleased(onMouseReleasedEventHandler);
}
EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if( event.getSource() instanceof Circle) {
Circle circle = ((Circle) (event.getSource()));
dragContext.x = circle.getCenterX() - event.getSceneX();
dragContext.y = circle.getCenterY() - event.getSceneY();
} else {
Node node = ((Node) (event.getSource()));
dragContext.x = node.getTranslateX() - event.getSceneX();
dragContext.y = node.getTranslateY() - event.getSceneY();
}
}
};
EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if( event.getSource() instanceof Circle) {
Circle circle = ((Circle) (event.getSource()));
circle.setCenterX( dragContext.x + event.getSceneX());
circle.setCenterY( dragContext.y + event.getSceneY());
} else {
Node node = ((Node) (event.getSource()));
node.setTranslateX( dragContext.x + event.getSceneX());
node.setTranslateY( dragContext.y + event.getSceneY());
}
}
};
EventHandler<MouseEvent> onMouseReleasedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
}
};
}
}
It shows how to drag circles and bind another node (the line) so that it gets modified as well when you drag the circles. You can also drag the line separately which is a Node and handled differently.
In case you still got problems let me know.
As a general note, it's always advisable to add this to a node in order to understand which events happen:
node.addEventFilter(Event.ANY, e -> System.out.println( e));
and then check the console output while you do something on screen.
Regarding your main problem: You mustn't set Fill to null. In that case the click event won't get registered. You should use Color.TRANSPARENT instead. You can verify the event difference with the above mentioned method.

Sort ObservableList from high to low - javafx

Is there any way to sort a ObservableList based on the values from high to low?
Say I have a
ObservableList<XYChart.Data> data;
containing a String and a Double. I want the list sorted based on the Double from highest to lowest values. The reason I want this is because charts look way better if their values are shown from the highest to the lowest.
I have something like this now:
sortedData = new SortedList<>(data);
sortedData.comparatorProperty().bind(mycomparatorProperty());
You can create a Comparator, compare by the Y value, and then reverse the order:
data.sort(Comparator.comparing(XYChart.Data<String,Double>::getYValue).reversed());
This will sort your collection as intented.
Or you can return a new collection:
List<XYChart.Data<String,Double>> sortedData =
data.stream()
.sorted(Comparator.comparing(XYChart.Data<String,Double>::getYValue).reversed())
.collect(Collectors.toList());
EDIT
For the sake of clarity, this is a full sample:
public class FXMain extends Application {
private final ObservableList<XYChart.Data<String,Double>> data =
FXCollections.observableArrayList(
new XYChart.Data("P1",200d),
new XYChart.Data("P2",150d),
new XYChart.Data("P3",250d));
#Override
public void start(Stage primaryStage) {
Button btn = new Button"Sort data!");
btn.setOnAction(e -> {
ObservableList<XYChart.Data<String,Double>> data2 =
data.stream()
.sorted(Comparator.
comparing(XYChart.Data<String,Double>::getYValue).reversed())
.peek(System.out::println)
.collect(Collectors.toCollection(()->FXCollections.observableArrayList()));
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Resources