JavaFX Treeview - how to create a parent node with "two" graphics - javafx

I have a node with several children and want to keep the collapse/expand arrow that is the default icon of the node, but I have another graphic that I would like to put right next to the arrow. Does the JavaFX treeview allow a way to do this?

Either just pass the graphic to the TreeItem:
TreeItem<String> root = new TreeItem<>("Root", new Rectangle(16, 16, Color.CORAL));
etc,
or use a cell factory:
TreeView<String> tree = new TreeView<>();
tree.setCellFactory(tv -> new TreeCell<String>() {
private final Node graphic = new Rectangle(16, 16, Color.CORAL);
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setGraphic(empty ? null : graphic);
setText(empty ? null : item);
}
});
Note that for large trees, the second technique has the potential to be far more efficient, as it only creates graphics (two nodes in this case) for each cell, whereas the first technique creates graphics for every item in the tree (whether or not it is displayed). Arguably (I would strongly argue), the second technique has better separation of concerns (in the first solution the graphic is part of data, which is just plain wrong).
SSCCE for first technique:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class TreeGraphicTest extends Application {
#Override
public void start(Stage primaryStage) {
TreeItem<String> root = new TreeItem<>("Root", new Rectangle(16, 16, Color.CORAL));
for (int i = 1 ; i <= 3 ; i++) {
root.getChildren().add(new TreeItem<>("Child "+i, new Rectangle(16, 16, Color.CORNFLOWERBLUE)));
}
TreeView<String> tree = new TreeView<>(root);
primaryStage.setScene(new Scene(tree));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
SSCCE for second technique:
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class TreeGraphicTest extends Application {
#Override
public void start(Stage primaryStage) {
TreeItem<String> root = new TreeItem<>("Root");
for (int i = 1 ; i <= 3 ; i++) {
root.getChildren().add(new TreeItem<>("Child "+i));
}
TreeView<String> tree = new TreeView<>(root);
tree.setCellFactory(tv -> new TreeCell<String>() {
private final Node rootGraphic = new Rectangle(16, 16, Color.CORAL) ;
private final Node childGraphic = new Rectangle(16, 16, Color.CORNFLOWERBLUE) ;
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setGraphic(empty ? null : getTreeItem() == root ? rootGraphic : childGraphic);
setText(empty ? null : item);
}
});
primaryStage.setScene(new Scene(tree));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Screenshot (for either):

Related

Select first item shown into a treeTableView in JavaFX 8

I have a TreeTableView control with several items. The user can move with the side scroll bar to any part of the table. How can I know which is the first item that the table shows? I mean, the first item we see. I have been going through the methods offered by this control and I have seen that scrollTo shows an item according to an order number that we specify but I have not found anything that is something like getFirstItemShown. I imagine that for a TableView control it should work the same, right? I'm using JavaFX 8.
Just for fun:
(note that if there are cut lines it can choose the next row, and the first row is sometimes hidden under the Column)
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableColumn.CellDataFeatures;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableView;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Tree View Sample");
TreeItem<String> rootItem = new TreeItem<String> ("Inbox");
rootItem.setExpanded(true);
for (int i = 1; i < 888; i++) {
TreeItem<String> item = new TreeItem<String> ("Message" + i);
rootItem.getChildren().add(item);
}
TreeTableColumn<String, String> column = new TreeTableColumn<>("Column");
column.setPrefWidth(150);
column.setCellValueFactory((CellDataFeatures<String, String> p) -> new ReadOnlyStringWrapper(
p.getValue().getValue()));
TreeTableView<String> treeTableView = new TreeTableView<>(rootItem);
treeTableView.getColumns().add(column);
primaryStage.setScene(new Scene(treeTableView, 300, 250));
primaryStage.show();
Platform.runLater(()->{
ScrollBar verticalBar = (ScrollBar) treeTableView.lookup(".scroll-bar");
verticalBar.valueProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
int currentRow = 0;
if(newValue.doubleValue()<1){
currentRow =(int)((newValue.doubleValue()*treeTableView.getExpandedItemCount())-(newValue.doubleValue()*verticalBar.getVisibleAmount())*treeTableView.getExpandedItemCount());
}else {
currentRow =(int)(treeTableView.getExpandedItemCount()-verticalBar.getVisibleAmount()*treeTableView.getExpandedItemCount());
}
System.out.println(currentRow);
}
});
});
}
public static void main(String[] args) {
launch(args);
}
}

is there any way to add button into dropdown list with textfields using controlfx jar

i have a textfield with autocomplete and i want to show button near each item appeared into that list
this is my code
List<String> s = ms.getUsernames(ms.getUser(7).getListamis());
TextFields.bindAutoCompletion(txtsearch, s);
this is the method getusernames
public List<String> getUsernames(String list_amis) {
List<String> ls = new ArrayList<>();
String [] idmember = list_amis.split("/");
for (String i : idmember) {
ls.add(getUser(Integer.parseInt(i)).getUsername());
}
return ls;
}
this is my output
i want to add a button near testing as a result i can get its ID
This is the solution I came up with. There certainly is a better way that is more elegant and works better, but I guess this would work for many situations. You would have to do something about the size of the ListView, though, and I didn't test this in an environment in which the appearing ListView might change something about the design of the rest of the UI. I suggest putting the whole thing into a PopOver or something.
import java.util.function.Predicate;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class NewFXMain extends Application {
#Override
public void start(Stage primaryStage) {
ObservableList<String> list = FXCollections.observableArrayList("one","two","three");
FilteredList<String> filteredList = new FilteredList<>(list);
VBox box = new VBox();
TextField textField = new TextField();
textField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
filteredList.setPredicate(new Predicate<String>() {
#Override
public boolean test(String s){
return s.toLowerCase().contains(newValue.toLowerCase());
}
});
}
});
ListView listView = new ListView();
listView.setItems(filteredList);
listView.visibleProperty().bind(textField.textProperty().isNotEmpty());
listView.setCellFactory(new Callback() {
#Override
public Object call(Object param) {
ListCell cell = new ListCell(){
#Override
public void updateItem(Object item, boolean empty){
if(item != null && !empty){
super.updateItem(item, empty);
HBox contentBox = new HBox();
Label label = new Label(item.toString());
Button button = new Button("delete");
HBox separator = new HBox();
HBox.setHgrow(separator, Priority.ALWAYS);
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println(item);
}
});
contentBox.getChildren().addAll(label, separator, button);
setGraphic(contentBox);
}else{
setText(null);
setGraphic(null);
}
}
};
return cell;
}
});
box.getChildren().addAll(textField,listView);
StackPane root = new StackPane();
root.getChildren().add(box);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Autocomplete");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

How to add two different attributes to one line of a combobox in javafx? [duplicate]

I have a int value which I want to use for configuration. It can have 2 values - 0 for active and 1 for Blocked. I want to display this into friendly combo box:
import javafx.application.Application;
import static javafx.application.Application.launch;
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 MainApp extends Application
{
#Override
public void start(Stage stage) throws Exception
{
int state = 0;
ObservableList<String> options = FXCollections.observableArrayList(
"Active",
"Blocked"
);
ComboBox comboBox = new ComboBox(options);
BorderPane bp = new BorderPane(comboBox);
bp.setPrefSize(800, 800);
Scene scene = new Scene(bp);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
It's not clear for me how I have to implement this into JavaFX Combobox.
When I have 0 I want to display this as Active and when I have 1 I want to display Blocked and also when I change the ComboBox value to update also int state value.
There are different ways to solve this problem. I have listed three of the solutions below. You can use any one of the below solutions which you feel is apt for your scenario.
Using a custom class
Create a custom class KeyValuePair, for storing the string and its corresponding value. Exposed the getters for the required fields.
Later, I have used the setCellFactory() of the comboxbox to show the required data. Use StringConverter to show the key in place of the object.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception
{
KeyValuePair keyValuePair1 = new KeyValuePair("Active", 0);
KeyValuePair keyValuePair2 = new KeyValuePair("Blocked", 1);
ObservableList<KeyValuePair> options = FXCollections.observableArrayList();
options.addAll(keyValuePair1, keyValuePair2);
ComboBox<KeyValuePair> comboBox = new ComboBox<>(options);
// show the correct text
comboBox.setCellFactory((ListView<KeyValuePair> param) -> {
final ListCell<KeyValuePair> cell = new ListCell<KeyValuePair>(){
#Override
protected void updateItem(KeyValuePair t, boolean bln) {
super.updateItem(t, bln);
if(t != null){
setText(String.valueOf(t.getKey()));
}else{
setText(null);
}
}
};
return cell;
});
comboBox.setConverter(new StringConverter<KeyValuePair>() {
#Override
public String toString(KeyValuePair object) {
return object.getKey();
}
#Override
public KeyValuePair fromString(String string) {
return null; // No conversion fromString needed.
}
});
// print the value
comboBox.valueProperty().addListener((ov, oldVal, newVal) -> {
System.out.println(newVal.getKey() + " - " + newVal.getValue());
});
BorderPane bp = new BorderPane(comboBox);
bp.setPrefSize(800, 800);
Scene scene = new Scene(bp);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
class KeyValuePair {
private final String key;
private final int value;
public KeyValuePair(String key, int value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public int getValue() {
return value;
}
}
}
Without using an extra class
As suggested by #kleopatra, you can even do this without using an extra class.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
ObservableList<Integer> options = FXCollections.observableArrayList();
options.addAll(1, 0);
ComboBox<Integer> comboBox = new ComboBox<>(options);
// show the correct text
comboBox.setCellFactory((ListView<Integer> param) -> {
final ListCell<Integer> cell = new ListCell<Integer>(){
#Override
protected void updateItem(Integer t, boolean bln) {
super.updateItem(t, bln);
if(t != null){
setText(t == 1 ? "Active" : "Blocked");
}else{
setText(null);
}
}
};
return cell;
});
comboBox.setConverter(new StringConverter<Integer>() {
#Override
public String toString(Integer object) {
return object == 1 ? "Active" : "Blocked";
}
#Override
public Integer fromString(String string) {
return null;
}
});
// print the value
comboBox.valueProperty().addListener((ov, oldVal, newVal) -> {
System.out.println("Changed from " + oldVal + " to " + newVal);
});
BorderPane bp = new BorderPane(comboBox);
bp.setPrefSize(800, 800);
Scene scene = new Scene(bp);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Using Bindings
You can also use Bindings if you don't want to take the pain of creating a new class and you will always have two elements i.e. Active and Blocked.
Just bind the valueProperty() of your combobox to the state, which is supposed to store the value i.e. 0 or 1.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
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 Main extends Application {
#Override
public void start(Stage stage) throws Exception {
IntegerProperty state = new SimpleIntegerProperty();
ObservableList options = FXCollections.observableArrayList("Active", "Blocked");
ComboBox<String> comboBox = new ComboBox<>(options);
state.bind(Bindings.when(comboBox.valueProperty().isEqualTo("Active")).then(0).otherwise(1));
BorderPane bp = new BorderPane(comboBox);
bp.setPrefSize(800, 800);
Scene scene = new Scene(bp);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Here is another solution:
declare state as BooleanProperty:
private BooleanProperty state = new SimpleBooleanProperty(false);
bind state property to the valueProperty of comboBox:
comboBox.valueProperty().bind(new When(state).then("Active").otherwise("Blocked"));
complete example:
public class ComboboxTest extends Application {
private BooleanProperty state = new SimpleBooleanProperty(false);
private Button button;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
ObservableList<String> options = FXCollections.observableArrayList(
"Active",
"Blocked"
);
ComboBox comboBox = new ComboBox(options);
button = new Button("false");
button.setOnAction(e -> setSate());
button.textProperty().bind(state.asString());
BorderPane bp = new BorderPane(comboBox);
StackPane stackpane = new StackPane(button);
stackpane.setAlignment(Pos.CENTER);
bp.setTop(stackpane);
bp.setPrefSize(800, 800);
Scene scene = new Scene(bp);
stage.setScene(scene);
stage.show();
comboBox.valueProperty().bind(new When(state).then("Active").otherwise("Blocked"));
}
public void setSate() {
if (state.get()) {
state.set(false);
} else {
state.set(true);
}
}
}

JavaFx, add tooltip for tableview that shows cell contents [duplicate]

javaFX: TableView's cellvalue is not enough to display in columns, it will be clipped end of '...', how to show a tip on such cell?
I need a tip because sometimes i can't drag the colunm's head. So i can't see the whole content of the cell.
Below a subclass of TableCell is used to set the ToolTip for a cell. It does not distinguish between cells in which the text fits and those where the text is too large. It is used in the cell factory for the table column.
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
public class SO extends Application {
static class XCell extends TableCell<String, String> {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
this.setText(item);
this.setTooltip(
(empty || item==null) ? null : new Tooltip(item));
}
}
#Override
public void start(Stage primaryStage) throws Exception {
StackPane pane = new StackPane();
Scene scene = new Scene(pane, 300, 100);
primaryStage.setScene(scene);
ObservableList<String> list = FXCollections.observableArrayList(
"Short",
"This is quite long - almost very long");
TableView<String> tv = new TableView<>(list);
TableColumn<String, String> col = new TableColumn<>("Column");
col.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<String,String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<String, String> param) {
return new ReadOnlyStringWrapper(param.getValue());
}
});
col.setMaxWidth(50);
col.setCellFactory(new Callback<TableColumn<String,String>, TableCell<String,String>>() {
#Override
public TableCell<String, String> call(TableColumn<String, String> param) {
return new XCell();
}
});
tv.getColumns().add(col);
pane.getChildren().add(tv);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The result will look like this:
This should ACTUALLY solve your problem.
column.setCellFactory(col -> new TableCell<Object, String>()
{
#Override
protected void updateItem(final String item, final boolean empty)
{
super.updateItem(item, empty);
setText(item);
TableColumn tableCol = (TableColumn) col;
if (item != null && tableCol.getWidth() < new Text(item + " ").getLayoutBounds().getWidth())
{
tooltipProperty().bind(Bindings.when(Bindings.or(emptyProperty(), itemProperty().isNull())).then((Tooltip) null).otherwise(new Tooltip(item)));
} else
{
tooltipProperty().bind(Bindings.when(Bindings.or(emptyProperty(), itemProperty().isNull())).then((Tooltip) null).otherwise((Tooltip) null));
}
}
});

javaFX: TableView's cellvalue is not enough to display in columns, it will be clipped, how to show a tip on such cell?

javaFX: TableView's cellvalue is not enough to display in columns, it will be clipped end of '...', how to show a tip on such cell?
I need a tip because sometimes i can't drag the colunm's head. So i can't see the whole content of the cell.
Below a subclass of TableCell is used to set the ToolTip for a cell. It does not distinguish between cells in which the text fits and those where the text is too large. It is used in the cell factory for the table column.
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
public class SO extends Application {
static class XCell extends TableCell<String, String> {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
this.setText(item);
this.setTooltip(
(empty || item==null) ? null : new Tooltip(item));
}
}
#Override
public void start(Stage primaryStage) throws Exception {
StackPane pane = new StackPane();
Scene scene = new Scene(pane, 300, 100);
primaryStage.setScene(scene);
ObservableList<String> list = FXCollections.observableArrayList(
"Short",
"This is quite long - almost very long");
TableView<String> tv = new TableView<>(list);
TableColumn<String, String> col = new TableColumn<>("Column");
col.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<String,String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<String, String> param) {
return new ReadOnlyStringWrapper(param.getValue());
}
});
col.setMaxWidth(50);
col.setCellFactory(new Callback<TableColumn<String,String>, TableCell<String,String>>() {
#Override
public TableCell<String, String> call(TableColumn<String, String> param) {
return new XCell();
}
});
tv.getColumns().add(col);
pane.getChildren().add(tv);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The result will look like this:
This should ACTUALLY solve your problem.
column.setCellFactory(col -> new TableCell<Object, String>()
{
#Override
protected void updateItem(final String item, final boolean empty)
{
super.updateItem(item, empty);
setText(item);
TableColumn tableCol = (TableColumn) col;
if (item != null && tableCol.getWidth() < new Text(item + " ").getLayoutBounds().getWidth())
{
tooltipProperty().bind(Bindings.when(Bindings.or(emptyProperty(), itemProperty().isNull())).then((Tooltip) null).otherwise(new Tooltip(item)));
} else
{
tooltipProperty().bind(Bindings.when(Bindings.or(emptyProperty(), itemProperty().isNull())).then((Tooltip) null).otherwise((Tooltip) null));
}
}
});

Resources