Using this example I want to create TreeView with Nodes that I can drag in order to change their position.
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeItem.TreeModificationEvent;
import javafx.scene.control.TreeView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class MainApp extends Application {
private EventHandler<TreeModificationEvent<DynamicTreeNodeModel>> branchExpandedEventHandler;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Application.launch(MainApp.class, args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Example Dynamic Tree");
primaryStage.setResizable(true);
final VBox box = new VBox();
box.setFillWidth(false);
Scene scene = new Scene(box);
primaryStage.setScene(scene);
box.getChildren().add(this.getExampleTree());
primaryStage.show();
}
private TreeView<DynamicTreeNodeModel> getExampleTree() {
DynamicTreeNodeModel rootNode = new RandomDynamicTreeNodeModel(null, "Root Node");
TreeView<DynamicTreeNodeModel> treeView = new TreeView<DynamicTreeNodeModel>();
treeView.setCellFactory(new Callback<TreeView<DynamicTreeNodeModel>, TreeCell<DynamicTreeNodeModel>>() {
#Override
public TreeCell call(TreeView<DynamicTreeNodeModel> param) {
return new DnDCell(param);
}
});
treeView.setPrefSize(1000, 750);
TreeItem rootItem = new TreeItem(rootNode);
branchExpandedEventHandler = new EventHandler<TreeModificationEvent<DynamicTreeNodeModel>>() {
public void handle(TreeModificationEvent<DynamicTreeNodeModel> event) {
// System.out.println("handling event " + event);
TreeItem<DynamicTreeNodeModel> item = event.getTreeItem();
populateTreeItem(item);
}
};
rootItem.addEventHandler(TreeItem.branchExpandedEvent(), branchExpandedEventHandler);
treeView.setShowRoot(true);
treeView.setRoot(rootItem);
populateTreeItem(rootItem);
rootItem.setExpanded(true);
// treeView.setCellFactory(new LearningTreeCellFactory());
return treeView;
}
private void populateTreeItem(TreeItem<DynamicTreeNodeModel> item) {
DynamicTreeNodeModel node = item.getValue();
boolean isPopulated = node.isPopulated();
boolean areGrandChildrenPopulated = node.areChildenPopulated();
node.populateToDepth(2);
if (!isPopulated) {
for (DynamicTreeNodeModel childNode : node.getChildren()) {
TreeItem childItem = new TreeItem(childNode);
childItem.addEventHandler(TreeItem.branchExpandedEvent(), branchExpandedEventHandler);
item.getChildren().add(childItem);
}
}
if (!areGrandChildrenPopulated) {
int i = 0;
for (TreeItem childItem : item.getChildren()) {
// get cooresponding node in the model
DynamicTreeNodeModel childNode = node.getChildren().get(i);
i++;
for (DynamicTreeNodeModel grandChildNode : childNode.getChildren()) {
TreeItem grandChildItem = new TreeItem(grandChildNode);
grandChildItem.addEventHandler(TreeItem.branchExpandedEvent(), branchExpandedEventHandler);
childItem.getChildren().add(grandChildItem);
}
}
}
}
private static interface DynamicTreeNodeModel {
public String getName();
public void setName(String name);
public boolean isPopulated();
public boolean areChildenPopulated();
public List<DynamicTreeNodeModel> getChildren();
public void setChildren(List<DynamicTreeNodeModel> children);
public DynamicTreeNodeModel getParent();
public void setParent(DynamicTreeNodeModel parent);
public void populateToDepth(int depth);
#Override
public String toString();
}
private static class RandomDynamicTreeNodeModel implements DynamicTreeNodeModel {
private DynamicTreeNodeModel parent;
private String name;
private List<DynamicTreeNodeModel> children = null;
public RandomDynamicTreeNodeModel(DynamicTreeNodeModel parent, String name) {
this.parent = parent;
this.name = name;
}
#Override
public String getName() {
return name;
}
#Override
public void setName(String name) {
this.name = name;
}
#Override
public boolean isPopulated() {
if (children == null) {
return false;
}
return true;
}
#Override
public boolean areChildenPopulated() {
if (!this.isPopulated()) {
return false;
}
for (DynamicTreeNodeModel child : this.children) {
if (!child.isPopulated()) {
return false;
}
}
return true;
}
#Override
public List<DynamicTreeNodeModel> getChildren() {
return children;
}
#Override
public void setChildren(List<DynamicTreeNodeModel> children) {
this.children = children;
}
#Override
public DynamicTreeNodeModel getParent() {
return parent;
}
#Override
public void setParent(DynamicTreeNodeModel parent) {
this.parent = parent;
}
private static Random random = new Random();
#Override
public void populateToDepth(int depth) {
if (depth <= 0) {
return;
}
if (children == null) {
int num = random.nextInt(5);
System.out.println("got a random number " + num);
children = new ArrayList(num);
for (int i = 0; i < num; i++) {
children.add(new RandomDynamicTreeNodeModel(this, "child " + i));
}
}
int childDepth = depth - 1;
for (DynamicTreeNodeModel child : children) {
child.populateToDepth(childDepth);
}
}
#Override
public String toString() {
return this.name;
}
}
public class DnDCell extends TreeCell<DynamicTreeNodeModel> {
private TreeView<DynamicTreeNodeModel> parentTree;
public DnDCell(final TreeView<DynamicTreeNodeModel> parentTree) {
this.parentTree = parentTree;
// ON SOURCE NODE.
setOnDragDetected(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("Drag detected on " + item);
if (item == null) {
return;
}
Dragboard dragBoard = startDragAndDrop(TransferMode.MOVE);
ClipboardContent content = new ClipboardContent();
content.put(DataFormat.PLAIN_TEXT, item.toString());
dragBoard.setContent(content);
event.consume();
}
});
setOnDragDone(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent dragEvent) {
System.out.println("Drag done on " + item);
dragEvent.consume();
}
});
// ON TARGET NODE.
// setOnDragEntered(new EventHandler<DragEvent>() {
// #Override
// public void handle(DragEvent dragEvent) {
// System.out.println("Drag entered on " + item);
// dragEvent.consume();
// }
// });
setOnDragOver(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent dragEvent) {
System.out.println("Drag over on " + item);
if (dragEvent.getDragboard().hasString()) {
int valueToMove = Integer.parseInt(dragEvent.getDragboard().getString());
if (valueToMove != item) {
// We accept the transfer!!!!!
dragEvent.acceptTransferModes(TransferMode.MOVE);
}
}
dragEvent.consume();
}
});
// setOnDragExited(new EventHandler<DragEvent>() {
// #Override
// public void handle(DragEvent dragEvent) {
// System.out.println("Drag exited on " + item);
// dragEvent.consume();
// }
// });
setOnDragDropped(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent dragEvent) {
System.out.println("Drag dropped on " + item);
int valueToMove = Integer.parseInt(dragEvent.getDragboard().getString());
TreeItem<DynamicTreeNodeModel> itemToMove = search(parentTree.getRoot(), valueToMove);
TreeItem<DynamicTreeNodeModel> newParent = search(parentTree.getRoot(), item);
// Remove from former parent.
itemToMove.getParent().getChildren().remove(itemToMove);
// Add to new parent.
newParent.getChildren().add(itemToMove);
newParent.setExpanded(true);
dragEvent.consume();
}
});
}
private TreeItem<DynamicTreeNodeModel> search(final TreeItem<DynamicTreeNodeModel> currentNode, final int valueToSearch) {
TreeItem<DynamicTreeNodeModel> result = null;
if (currentNode.getValue() == valueToSearch) {
result = currentNode;
} else if (!currentNode.isLeaf()) {
for (TreeItem<DynamicTreeNodeModel> child : currentNode.getChildren()) {
result = search(child, valueToSearch);
if (result != null) {
break;
}
}
}
return result;
}
private DynamicTreeNodeModel item;
#Override
protected void updateItem(DynamicTreeNodeModel item, boolean empty) {
super.updateItem(item, empty);
this.item = item;
String text = (item == null) ? null : item.toString();
setText(text);
}
}
}
I tried to implement the example but I'm facing a problem here:
#Controller
public class FileUploadController {
if (valueToMove != item) {
// We accept the transfer!!!!!
dragEvent.acceptTransferModes(TransferMode.MOVE);
}
...............
if (currentNode.getValue() == valueToSearch) {
I'm getting incomparable types: DynamicTreeNodeModel and int
Can you help me to fix this issue?
please try this:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package test;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeItem.TreeModificationEvent;
import javafx.scene.control.TreeView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class MainApp extends Application {
private EventHandler<TreeModificationEvent<DynamicTreeNodeModel>> branchExpandedEventHandler;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Application.launch(MainApp.class, args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Example Dynamic Tree");
primaryStage.setResizable(true);
final VBox box = new VBox();
box.setFillWidth(false);
Scene scene = new Scene(box);
primaryStage.setScene(scene);
box.getChildren().add(this.getExampleTree());
primaryStage.show();
}
private TreeView<DynamicTreeNodeModel> getExampleTree() {
DynamicTreeNodeModel rootNode = new RandomDynamicTreeNodeModel(null, "Root Node");
TreeView<DynamicTreeNodeModel> treeView = new TreeView<DynamicTreeNodeModel>();
treeView.setCellFactory(new Callback<TreeView<DynamicTreeNodeModel>, TreeCell<DynamicTreeNodeModel>>() {
#Override
public TreeCell call(TreeView<DynamicTreeNodeModel> param) {
return new DnDCell(param);
}
});
treeView.setPrefSize(1000, 750);
TreeItem rootItem = new TreeItem(rootNode);
branchExpandedEventHandler = new EventHandler<TreeModificationEvent<DynamicTreeNodeModel>>() {
public void handle(TreeModificationEvent<DynamicTreeNodeModel> event) {
// System.out.println("handling event " + event);
TreeItem<DynamicTreeNodeModel> item = event.getTreeItem();
populateTreeItem(item);
}
};
rootItem.addEventHandler(TreeItem.branchExpandedEvent(), branchExpandedEventHandler);
treeView.setShowRoot(true);
treeView.setRoot(rootItem);
populateTreeItem(rootItem);
rootItem.setExpanded(true);
// treeView.setCellFactory(new LearningTreeCellFactory());
return treeView;
}
private void populateTreeItem(TreeItem<DynamicTreeNodeModel> item) {
DynamicTreeNodeModel node = item.getValue();
boolean isPopulated = node.isPopulated();
boolean areGrandChildrenPopulated = node.areChildenPopulated();
node.populateToDepth(2);
if (!isPopulated) {
for (DynamicTreeNodeModel childNode : node.getChildren()) {
TreeItem childItem = new TreeItem(childNode);
childItem.addEventHandler(TreeItem.branchExpandedEvent(), branchExpandedEventHandler);
item.getChildren().add(childItem);
}
}
if (!areGrandChildrenPopulated) {
int i = 0;
int size = node.getChildren().size();
for (TreeItem childItem : item.getChildren()) {
// get cooresponding node in the model
if (i < size) {
DynamicTreeNodeModel childNode = node.getChildren().get(i);
i++;
for (DynamicTreeNodeModel grandChildNode : childNode.getChildren()) {
TreeItem grandChildItem = new TreeItem(grandChildNode);
grandChildItem.addEventHandler(TreeItem.branchExpandedEvent(), branchExpandedEventHandler);
childItem.getChildren().add(grandChildItem);
}
}
}
}
}
private static interface DynamicTreeNodeModel {
public String getName();
public void setName(String name);
public boolean isPopulated();
public boolean areChildenPopulated();
public List<DynamicTreeNodeModel> getChildren();
public void setChildren(List<DynamicTreeNodeModel> children);
public DynamicTreeNodeModel getParent();
public void setParent(DynamicTreeNodeModel parent);
public void populateToDepth(int depth);
#Override
public String toString();
}
private static class RandomDynamicTreeNodeModel implements DynamicTreeNodeModel {
private DynamicTreeNodeModel parent;
private String name;
private List<DynamicTreeNodeModel> children = null;
public RandomDynamicTreeNodeModel(DynamicTreeNodeModel parent, String name) {
this.parent = parent;
this.name = name;
}
#Override
public String getName() {
return name;
}
#Override
public void setName(String name) {
this.name = name;
}
#Override
public boolean isPopulated() {
if (children == null) {
return false;
}
return true;
}
#Override
public boolean areChildenPopulated() {
if (!this.isPopulated()) {
return false;
}
for (DynamicTreeNodeModel child : this.children) {
if (!child.isPopulated()) {
return false;
}
}
return true;
}
#Override
public List<DynamicTreeNodeModel> getChildren() {
return children;
}
#Override
public void setChildren(List<DynamicTreeNodeModel> children) {
this.children = children;
}
#Override
public DynamicTreeNodeModel getParent() {
return parent;
}
#Override
public void setParent(DynamicTreeNodeModel parent) {
this.parent = parent;
}
private static Random random = new Random();
#Override
public void populateToDepth(int depth) {
if (depth <= 0) {
return;
}
if (children == null) {
int num = random.nextInt(5);
System.out.println("got a random number " + num);
children = new ArrayList(num);
for (int i = 0; i < num; i++) {
// children.add(new RandomDynamicTreeNodeModel(this, "child " + i));
children.add(new RandomDynamicTreeNodeModel(this, "child " + System.currentTimeMillis()));
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(MainApp.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
int childDepth = depth - 1;
for (DynamicTreeNodeModel child : children) {
child.populateToDepth(childDepth);
}
}
#Override
public String toString() {
return this.name;
}
}
public class DnDCell extends TreeCell<DynamicTreeNodeModel> {
private TreeView<DynamicTreeNodeModel> parentTree;
public DnDCell(final TreeView<DynamicTreeNodeModel> parentTree) {
this.parentTree = parentTree;
// ON SOURCE NODE.
setOnDragDetected(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("Drag detected on " + item);
if (item == null) {
return;
}
Dragboard dragBoard = startDragAndDrop(TransferMode.MOVE);
ClipboardContent content = new ClipboardContent();
content.put(DataFormat.PLAIN_TEXT, item.toString());
dragBoard.setContent(content);
event.consume();
}
});
setOnDragDone(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent dragEvent) {
System.out.println("Drag done on " + item);
dragEvent.consume();
}
});
// ON TARGET NODE.
// setOnDragEntered(new EventHandler<DragEvent>() {
// #Override
// public void handle(DragEvent dragEvent) {
// System.out.println("Drag entered on " + item);
// dragEvent.consume();
// }
// });
setOnDragOver(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent dragEvent) {
System.out.println("Drag over on " + item);
if (dragEvent.getDragboard().hasString()) {
String valueToMove = dragEvent.getDragboard().getString();
if (!valueToMove.matches(item.getName())) {
// We accept the transfer!!!!!
dragEvent.acceptTransferModes(TransferMode.MOVE);
}
}
dragEvent.consume();
}
});
// setOnDragExited(new EventHandler<DragEvent>() {
// #Override
// public void handle(DragEvent dragEvent) {
// System.out.println("Drag exited on " + item);
// dragEvent.consume();
// }
// });
setOnDragDropped(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent dragEvent) {
System.out.println("Drag dropped on " + item);
String valueToMove = dragEvent.getDragboard().getString();
TreeItem<DynamicTreeNodeModel> itemToMove = search(parentTree.getRoot(), valueToMove);
TreeItem<DynamicTreeNodeModel> newParent = search(parentTree.getRoot(), item.getName());
// Remove from former parent.
itemToMove.getParent().getChildren().remove(itemToMove);
// Add to new parent.
newParent.getChildren().add(itemToMove);
newParent.setExpanded(true);
dragEvent.consume();
}
});
}
private TreeItem<DynamicTreeNodeModel> search(final TreeItem<DynamicTreeNodeModel> currentNode, final String valueToSearch) {
TreeItem<DynamicTreeNodeModel> result = null;
if (currentNode.getValue().getName().matches(valueToSearch)) {
result = currentNode;
} else if (!currentNode.isLeaf()) {
for (TreeItem<DynamicTreeNodeModel> child : currentNode.getChildren()) {
result = search(child, valueToSearch);
if (result != null) {
break;
}
}
}
return result;
}
private DynamicTreeNodeModel item;
#Override
protected void updateItem(DynamicTreeNodeModel item, boolean empty) {
super.updateItem(item, empty);
this.item = item;
String text = (item == null) ? null : item.toString();
setText(text);
}
}
}
In here, I compare the names (same as Ithachi's opinion) and did some tweaks (check your populateTreeItem(..) function and modify it to suit your need - if currently it doesn't)..
to avoid duplicate names, I used system.currentTimemillis as part of the node's name..
hope it helps..
Related
I have a ListView with with a custom CheckBox Item called CheckBoxListItem. Everthing looks good but now I need to implement Drag and Drop for easier sorting.
I know that I have to use a custom CellFactory and set the Drag and Drop Events to the cell itself. My Problem is that I already have a CellFactory and dont know how to add the Events.
The commented code is what I think could be the way to do it but the updateItem method doesn't worked.
My main class:
public class Main extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
ListView<CheckBoxListItem> listView = new ListView<>();
ScrollPane scrollPane = new ScrollPane(listView);
scrollPane.setFitToHeight(true);
scrollPane.setFitToWidth(true);
for(int i=1; i<15; i++) {
listView.getItems().add(new CheckBoxListItem("Value"+i));
}
listView.setCellFactory(CheckBoxListCell.forListView(CheckBoxListItem::selectedProperty, new StringConverter< CheckBoxListItem>() {
#Override
public String toString(CheckBoxListItem object) {
return object.getName();
}
#Override
public CheckBoxListItem fromString(String string) {
return null;
}
}));
/*
ObjectProperty<CheckBoxListCell<CheckBoxListItem>> dragSource = new SimpleObjectProperty<>();
listView.setCellFactory(lv -> {
CheckBoxListCell<CheckBoxListItem> cell = new CheckBoxListCell<CheckBoxListItem>(){
#Override
public void updateItem(CheckBoxListItem item , boolean empty) {
super.updateItem(item, empty);
if (item == null) {
setGraphic(null);
}else {
setGraphic(?????);
}
}
};
cell.setOnDragDetected(event -> {
//TODO
});
cell.setOnDragOver(event -> {
//TODO
});
cell.setOnDragDropped(event -> {
//TODO
});
return cell ;
});
*/
Scene scene = new Scene(scrollPane, 350, 450);
stage.setScene(scene);
stage.show();
}
#Override
public void stop() {
System.exit(0);
}
}
and CheckBoxListItem.java:
public class CheckBoxListItem {
private ReadOnlyStringWrapper name = new ReadOnlyStringWrapper();
private BooleanProperty selected = new SimpleBooleanProperty(false);
public CheckBoxListItem(String name) {
this.name.set(name);
}
public CheckBoxListItem(String name, boolean selected) {
this.name.set(name);
this.selected.set(selected);
}
public String getName() {
return name.get();
}
public BooleanProperty selectedProperty() {
return selected;
}
public boolean isSelected() {
return selected.get();
}
public void setSelected(boolean selected) {
this.selected.set(selected);
}
}
The solution is something you have to combine both your approaches. You can merely mimic what CheckBoxListCell.forListView() internally does. i.e, creating a new CheckBoxListCell with the observableProperty and converter. I just remodified your demo to below and it works as expected.
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.cell.CheckBoxListCell;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
ListView<CheckBoxListItem> listView = new ListView<>();
ScrollPane scrollPane = new ScrollPane(listView);
scrollPane.setFitToHeight(true);
scrollPane.setFitToWidth(true);
for (int i = 1; i < 15; i++) {
listView.getItems().add(new CheckBoxListItem("Value" + i));
}
// listView.setCellFactory(CheckBoxListCell.forListView(CheckBoxListItem::selectedProperty, new StringConverter< CheckBoxListItem>() {
// #Override
// public String toString(CheckBoxListItem object) {
// return object.getName();
// }
//
// #Override
// public CheckBoxListItem fromString(String string) {
// return null;
// }
// }));
ObjectProperty<CheckBoxListCell<CheckBoxListItem>> dragSource = new SimpleObjectProperty<>();
listView.setCellFactory(lv -> {
CheckBoxListCell<CheckBoxListItem> cell = new CheckBoxListCell<>(CheckBoxListItem::selectedProperty, new StringConverter<CheckBoxListItem>() {
#Override
public String toString(CheckBoxListItem object) {
return object.getName();
}
#Override
public CheckBoxListItem fromString(String string) {
return null;
}
});
cell.setOnDragDetected(event -> {
System.out.println("Detected fired...");
});
cell.setOnDragOver(event -> {
System.out.println("DragOver fired...");
});
cell.setOnDragDropped(event -> {
System.out.println("DragDropped fired...");
});
return cell;
});
Scene scene = new Scene(scrollPane, 350, 450);
stage.setScene(scene);
stage.show();
}
#Override
public void stop() {
System.exit(0);
}
}
I want to reproduce that combobox in JavaFX
With swing there is a way by using a renderer where HTML code can be passed.
I haven't found a way yet to do it in JAVAFX. I saw the cellFactory but it seems to handle only one line of text.
I tried to use an observableList of WebView. I was able to display the information as desired, but then the onAction was never called. I tried by setting an event and a listener, but nothing worked. I tried the next examples separately.
cbOrderLine.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<WebView>() {
#Override
public void changed(ObservableValue<? extends WebView> arg0, WebView arg1, WebView arg2) {
int uasbdviadn = 0;
if (arg2 != null) {
System.out.println("Selected employee: " + arg2.getId() + " " + uasbdviadn);
}
}
});
cbOrderLine.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent ev) {
int index = cbOrderLine.getSelectionModel().getSelectedIndex();
initComboBox(index);
}
});
this is the code to create my dummy values
private List<WebView> createListFakeOrderLineItem() {
List<WebView> list = new ArrayList<>();
list.add(createFakeOrderLineItem(1));
list.add(createFakeOrderLineItem(2));
list.add(createFakeOrderLineItem(3));
list.add(createFakeOrderLineItem(4));
return list;
}
private WebView createFakeOrderLineItem(int id) {
WebView wv = new WebView();
wv.setPrefSize(684, 90);
ProductionOrderHeader header = new ProductionOrderHeader();
header.setPoNumber("PONumber1234");
ProductionOrderLine line = new ProductionOrderLine();
line.setId(3333);
ManufactureProduct product = new ManufactureProduct();
product.setId(1111);
product.setPartNumber("Product.PartNumber1111");
ManufacturePart part = new ManufacturePart();
part.setId(9999);
part.setPartNumber("Part.Partnumber9999");
part.setHardwareId(666666);
part.setHardwareName("Hardware Name");
OrderLineItem item = new OrderLineItem(id, header, line, part, product);
// return item;
wv.getEngine().loadContent(item.toString());
return wv;
}
This is my OrderLineItem
public class OrderLineItem {
Integer index;
ProductionOrderHeader header;
ProductionOrderLine line;
ManufacturePart part;
ManufactureProduct product;
public OrderLineItem(Integer index, ProductionOrderHeader header, ProductionOrderLine line, ManufacturePart part,
ManufactureProduct product) {
this.index = index;
this.header = header;
this.line = line;
this.part = part;
this.product = product;
}
public Integer getIndex() {
return index;
}
public void setIndex(Integer index) {
this.index = index;
}
public ProductionOrderHeader getHeader() {
return header;
}
public void setHeader(ProductionOrderHeader order) {
this.header = order;
}
public ProductionOrderLine getLine() {
return line;
}
public void setLine(ProductionOrderLine orderLine) {
this.line = orderLine;
}
public ManufacturePart getPart() {
return part;
}
public void setPart(ManufacturePart part) {
this.part = part;
}
public ManufactureProduct getProduct() {
return product;
}
public void setProduct(ManufactureProduct product) {
this.product = product;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("<html>");
sb.append("<table>");
sb.append("<tr>");
sb.append("<td valign=\"middle\" style=\"font-size: 32px; padding-right:5px;\">");
sb.append((index + 1));
sb.append("</td>");
sb.append("<td style=\"border: 1px solid #919191; width:616px; font-size:12px; padding:10px;\">");
sb.append("<b>");
sb.append(header.getPoNumber());
sb.append(" [");
sb.append(line.getId());
sb.append("] </b> -");
sb.append("<b>P/N: </b> #");
sb.append(product.getId());
sb.append("<b> ");
sb.append(product.getPartNumber());
sb.append("</b><br/>");
sb.append("Part: # ");
sb.append(part.getId());
sb.append("<b> ");
sb.append(part.getPartNumber());
sb.append("</b> H/W: # ");
sb.append(part.getHardwareId());
sb.append("<b> ");
sb.append(part.getHardwareName());
sb.append("</b>");
sb.append("</td>");
sb.append("</tr>");
sb.append("</table>");
sb.append("</html>");
return sb.toString();
}
}
I'm using JAVAFX 8.
Thanks
I saw the cellFactory but it seems to handle only one line of text.
Firstly your understanding of cellFactory is wrong. And also setting a WebView as your comboBox item is really a horrible idea :). Instead you can build a simple layout and set it as graphic in your cell factory.
And by the way, never set Nodes as items to your controls (ComboBox, ListView..etc), you can just pass your model and build the appropriate layout in cellFactories.
Below is a quick demo to give you an idea of how to build your desired comboBox. Hope this can help you to have a better understanding.
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MultiLineComboBoxDemo extends Application {
#Override
public void start(Stage stage) throws Exception {
VBox root = new VBox();
root.setSpacing(15);
root.setPadding(new Insets(25));
root.setAlignment(Pos.TOP_LEFT);
Scene sc = new Scene(root, 600, 600);
stage.setScene(sc);
stage.show();
final ObservableList<Person> items = FXCollections.observableArrayList();
for (int i = 1; i < 4; i++) {
items.add(new Person(i,"Name " + i, i + 30, "email" + i + "#test.com"));
}
final ComboBox<Person> comboBox = new ComboBox<>();
comboBox.setItems(items);
comboBox.setCellFactory(param -> new ListCell<Person>() {
#Override
protected void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setGraphic(buildLayout(item));
} else {
setGraphic(null);
}
}
});
comboBox.setButtonCell(new ListCell<Person>(){
#Override
protected void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setGraphic(buildLayout(item));
} else {
setGraphic(null);
}
}
});
comboBox.getSelectionModel().selectedItemProperty().addListener((obs,oldVal,selectedPerson)->{
System.out.println("Name : "+selectedPerson.getName());
// Do what you want..
});
root.getChildren().add(comboBox);
}
private HBox buildLayout(Person person) {
VBox layout = new VBox();
HBox.setHgrow(layout,Priority.ALWAYS);
layout.setStyle("-fx-border-width:1px;-fx-border-color:#444444;");
layout.setSpacing(5);
layout.setPadding(new Insets(2));
HBox topRow = new HBox();
topRow.setSpacing(5);
topRow.getChildren().addAll(getLabel("Name :","bold"),getLabel(person.getName(),"normal"), getLabel("Age :","bold"),getLabel(person.getAge()+"","normal"));
HBox bottomRow = new HBox();
bottomRow.setSpacing(5);
bottomRow.getChildren().addAll(getLabel("Email :","bold"),getLabel(person.getEmail(),"normal"));
layout.getChildren().addAll(topRow, bottomRow);
HBox pane = new HBox();
pane.setAlignment(Pos.CENTER_LEFT);
pane.setSpacing(5);
pane.setPadding(new Insets(2));
Label num = new Label(person.getId()+"");
num.setStyle("-fx-font-size:20px;-fx-font-weight:bold;-fx-text-fill:black;");
pane.getChildren().addAll(num,layout);
return pane;
}
private Label getLabel(String txt, String style){
Label lblName = new Label(txt);
lblName.setStyle("-fx-font-weight:"+style+";-fx-text-fill:black;");
return lblName;
}
public static void main(String[] args) {
Application.launch(args);
}
class Person {
IntegerProperty id = new SimpleIntegerProperty();
StringProperty name = new SimpleStringProperty();
IntegerProperty age = new SimpleIntegerProperty();
StringProperty email = new SimpleStringProperty();
public Person(int id,String name, int age, String email) {
setId(id);
setName(name);
setAge(age);
setEmail(email);
}
public int getId() {
return id.get();
}
public IntegerProperty idProperty() {
return id;
}
public void setId(int id) {
this.id.set(id);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public int getAge() {
return age.get();
}
public IntegerProperty ageProperty() {
return age;
}
public void setAge(int age) {
this.age.set(age);
}
public String getEmail() {
return email.get();
}
public StringProperty emailProperty() {
return email;
}
public void setEmail(String email) {
this.email.set(email);
}
}
}
I have Created a custom control call it ComboBoxTablePopup extending Comboboxbase class. I have used a tableview as a popup content. Everything works fine,
update value, show popup, hide popup. After switching the focus from ComboBoxTablePopup to another control like a TextField or Spinner, it updates it self with null value.
So, I don't know what makes this happens. So here is my implementation on self executable class.
import com.sun.javafx.scene.control.behavior.ComboBoxBaseBehavior;
import com.sun.javafx.scene.control.behavior.KeyBinding;
import com.sun.javafx.scene.control.skin.ComboBoxListViewSkin;
import com.sun.javafx.scene.control.skin.ComboBoxPopupControl;
import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.WeakInvalidationListener;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.WeakListChangeListener;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class TestComboboxTablePopup extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
ComboBoxTablePopupControl<dataModel> comboBoxTablePopup = new ComboBoxTablePopupControl<>();
TableColumn<dataModel, Integer> tcId = new TableColumn<>("Id");
TableColumn<dataModel, String> tcName = new TableColumn<>("Name");
tcId.setCellValueFactory(new PropertyValueFactory<dataModel, Integer>("id"));
tcName.setCellValueFactory(new PropertyValueFactory<dataModel, String>("name"));
comboBoxTablePopup.setColumns(FXCollections.observableArrayList(tcId, tcName));
comboBoxTablePopup.setItems(FXCollections.observableArrayList(
new dataModel(1, "Data Model object 1"),
new dataModel(2, "Data Model object 2"),
new dataModel(3, "Data Model object 3")
));
VBox vBox = new VBox(comboBoxTablePopup);
Scene scene = new Scene(vBox);
primaryStage.setScene(scene);
primaryStage.setWidth(400);
primaryStage.setHeight(300);
primaryStage.show();
}
public class dataModel {
private int id;
private String name;
public dataModel(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
private static <S> StringConverter<S> defaultStringConverter() {
return new StringConverter<S>() {
#Override
public String toString(S t) {
return t == null ? "abood fait" : t.toString();
}
#Override
public S fromString(String string) {
return null;
}
};
}
private class ComboBoxTablePopupControl<S> extends ComboBoxBase {
/***************************************************************************
* *
* Static properties and methods *
* *
**************************************************************************/
private static final String DEFAULT_STYLE_CLASS = "combobox-table-popup";
private ObjectProperty<ObservableList<S>> items = new SimpleObjectProperty<ObservableList<S>>(this, "items");
public final void setItems(ObservableList<S> value) {
itemsProperty().set(value);
}
public final ObservableList<S> getItems() {
return items.get();
}
public ObjectProperty<ObservableList<S>> itemsProperty() {
return items;
}
public ObjectProperty<StringConverter<S>> converterProperty() {
return converter;
}
private ObjectProperty<StringConverter<S>> converter =
new SimpleObjectProperty<StringConverter<S>>(this, "converter", defaultStringConverter());
public final void setConverter(StringConverter<S> value) {
converterProperty().set(value);
}
public final StringConverter<S> getConverter() {
return converterProperty().get();
}
// Editor
private ReadOnlyObjectWrapper<TextField> editor;
public final TextField getEditor() {
return editorProperty().get();
}
public final ReadOnlyObjectProperty<TextField> editorProperty() {
if (editor == null) {
editor = new ReadOnlyObjectWrapper<TextField>(this, "editor");
editor.set(new ComboBoxListViewSkin.FakeFocusTextField());
}
return editor.getReadOnlyProperty();
}
private
ObservableList<TableColumn<S, ?>> columns = FXCollections.observableArrayList();
public ObservableList<TableColumn<S, ?>> getColumns() {
return columns;
}
public void setColumns(ObservableList<TableColumn<S, ?>> columns) {
this.columns = columns;
}
/***************************************************************************
* *
* Constructors *
* *
**************************************************************************/
/**
* Creates a default ComboboxTablePopup instance with an empty
* {#link #itemsProperty() items} list and default
* {#link #selectionModelProperty() selection model}.
*/
public ComboBoxTablePopupControl() {
this(FXCollections.<S>emptyObservableList());
}
/**
* Creates a default ComboboxTablePopup instance with the provided items list and
* a default { selection model}.
*/
public ComboBoxTablePopupControl(ObservableList<S> items) {
setItems(items);
getStyleClass().add(DEFAULT_STYLE_CLASS);
setEditable(true);
valueProperty().addListener((observable, oldValue, newValue) -> {
System.out.println(newValue);
});
}
public ComboBoxTablePopupControl(ObservableList<S> items, ObservableList<TableColumn<S, ?>> columns) {
this(items);
this.columns = columns;
}
#Override
protected Skin<?> createDefaultSkin() {
return new ComboBoxTablePopupControlSkin<>(this);
}
}
public class ComboBoxTablePopupControlSkin<S> extends ComboBoxPopupControl {
private ComboBoxTablePopupControl comboBoxTablePopup;
private ObservableList<S> comboboxTablePopupItems;
private TableView<S> tableViewPopupContent;
private ObservableList<S> tableViewPopupItems;
private Predicate<S> predicate;
private final InvalidationListener itemsObserver;
private final ListChangeListener<S> tableViewItemsListener = new ListChangeListener<S>() {
#Override
public void onChanged(ListChangeListener.Change<? extends S> c) {
getSkinnable().requestLayout();
}
};
private final WeakListChangeListener<S> weakListViewItemsListener =
new WeakListChangeListener<S>(tableViewItemsListener);
public ComboBoxTablePopupControlSkin(ComboBoxTablePopupControl comboBoxTablePopup) {
super(comboBoxTablePopup, new ComboBoxBaseBehavior(comboBoxTablePopup, null));
this.comboBoxTablePopup = comboBoxTablePopup;
updateComboBoxTablePopupItems();
itemsObserver = observable -> {
updateComboBoxTablePopupItems();
updateTableViewItems();
};
this.comboBoxTablePopup.itemsProperty().addListener(new WeakInvalidationListener(itemsObserver));
tableViewPopupContent = createTableView();
tableViewPopupContent.setManaged(false);
getChildren().add(tableViewPopupContent);
updateTableViewItems();
registerChangeListener(comboBoxTablePopup.converterProperty(), "CONVERTER");
registerChangeListener(comboBoxTablePopup.itemsProperty(), "ITEMS");
registerChangeListener(comboBoxTablePopup.valueProperty(), "VALUE");
registerChangeListener(comboBoxTablePopup.editorProperty(), "EDITABLE");
}
private void updateTableViewItems() {
this.tableViewPopupItems = comboBoxTablePopup.getItems();
this.tableViewPopupContent.setItems(this.tableViewPopupItems);
if (tableViewPopupItems != null) {
tableViewPopupItems.removeListener(weakListViewItemsListener);
}
this.tableViewPopupItems = comboboxTablePopupItems;
tableViewPopupContent.setItems(tableViewPopupItems);
if (tableViewPopupItems != null) {
tableViewPopupItems.addListener(weakListViewItemsListener);
}
getSkinnable().requestLayout();
}
public void updateComboBoxTablePopupItems() {
comboboxTablePopupItems = comboBoxTablePopup.getItems();
comboboxTablePopupItems = comboboxTablePopupItems == null ? FXCollections.<S>emptyObservableList() : comboboxTablePopupItems;
}
private TableView<S> createTableView() {
final TableView<S> tableView = new TableView<>();
tableView.setId("table-view");
tableView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
tableView.setFocusTraversable(false);
for (TableColumn tblColumn : tableColumns()) {
tableView.getColumns().add(tblColumn);
}
tableView.getSelectionModel().selectedItemProperty().addListener(o -> {
S selectedItem = tableView.getSelectionModel().getSelectedItem();
comboBoxTablePopup.setValue(selectedItem);
});
tableView.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ENTER ||
e.getCode() == KeyCode.SPACE) {
S selectedItem = tableView.getSelectionModel().getSelectedItem();
comboBoxTablePopup.setValue(selectedItem);
comboBoxTablePopup.hide();
}
});
return tableView;
}
private ObservableList<TableColumn> tableColumns() {
return ((ComboBoxTablePopupControl) getSkinnable()).getColumns();
}
#Override
protected Node getPopupContent() {
return this.tableViewPopupContent;
}
#Override
protected TextField getEditor() {
return ((ComboBoxTablePopupControl) getSkinnable()).getEditor();
}
#Override
protected StringConverter<S> getConverter() {
return ((ComboBoxTablePopupControl) getSkinnable()).getConverter();
}
#Override
public Node getDisplayNode() {
Node displayNode;
displayNode = getEditableInputNode();
updateDisplayNode();
return displayNode;
}
#Override
protected void handleControlPropertyChanged(String p) {
if ("VALUE".equals(p)) {
updateDisplayNode();
System.out.println(comboBoxTablePopup.getValue());
comboBoxTablePopup.fireEvent(new ActionEvent());
} else if ("CONVERTER".equals(p)) {
updateDisplayNode();
System.out.println("Conveter peroptery");
} else if ("ITEMS".equals(p)) {
updateComboBoxTablePopupItems();
updateTableViewItems();
} else if ("EDITOR".equals(p)) {
getEditableInputNode();
} else
super.handleControlPropertyChanged(p);
}
}
}
If someone still has trouble with this issue, here is a workaround for a choicebox. I assume you already have a table with columns and an EventHandler. This example is not tested with any editable (isEditable(true)) object.
First set a custom Factory and an EventHandler for setOnEditStart
yourColumn.setCellFactory(value -> new CustomEditFactory(yourObsList));
In your CustomEditFactory class you have to extend TreeTableCell<KnotenObs, String> and #Override four methods
startEdit()
#Override
public void startEdit() {
super.startEdit();
choiceBox.getSelectionModel().select(getItem());
oldValue = choiceBox.getSelectionModel().getSelectedItem();
setGraphic(choiceBox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
commitEdit()
#Override
public void commitEdit(String newValue) {
if(newValue == null) {
super.commitEdit(oldValue);
} else {
super.commitEdit(newValue);
}
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
cancelEdit()
#Override
public void cancelEdit() {
super.cancelEdit();
if(getItem() == null) {
setText(oldValue);
} else {
setText(getItem());
}
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
updateItem()
#Override
public void updateItem(String item, boolean empty) {
if(item == null) {
super.updateItem(oldValue, empty);
setText(oldValue);
} else {
super.updateItem(item, empty);
setText(item);
}
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
I am working with Musical Jukebox program. I have two main classes: Song.java and Playlist.java. The Song.java is as follows:
public class Song {
String name;
String title;
double length;
public Song(String name,String title,double length)
{
this.name=name;
this.title=title;
this.length=length;
}
public void setArtist(String songname)
{
name=songname;
}
public String getArtist()
{
return name;
}
public void setTitle(String songtitle)
{
title=songtitle;
}
public String getTitle()
{
return title;
}
public void setLength(double songlength)
{
length=songlength;
}
public double getLength()
{
return length;
}
public String toString()
{
return "Title: " + getTitle() + ", Artist: " + getArtist()
+ ", Track Length: " + getLength();
}
And Playlist.java is as follows:
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
#SuppressWarnings("serial")
public class Playlist<E extends Song> extends java.util.Vector<E> {
java.util.Iterator<E> itr = this.iterator();
String name;
ArrayList<Song> songList;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ArrayList<Song> getSongList() {
return songList;
}
public void setSongList(ArrayList<Song> songList) {
this.songList = songList;
}
public void PlayList() {
name = "Untitled";
songList = new ArrayList<Song>();
}
public Playlist(String name) {
this.name = name;
songList = new ArrayList<Song>();
}
public Object getTitle()
{
return "Playlist Title";
}
public boolean addtoPlist(Song song1) {
songList.add(song1);
return true;
}
public Song getSong(int index) {
songList.trimToSize();
if(songList.size() >= index){
return songList.get(index);
}
else
return null;
}
public boolean hasTitle(String string) {
if( string.equals("Playlist Title"))
return true;
return false;
}
public boolean hasArtist(String string) {
if(string.equalsIgnoreCase("artist1"))
return true;
return false;
}
public Object numberOfSongs() {
// TODO Auto-generated method stub
return songList.size();
}
public Object numberOfArtists()
{
return 0;
}
public Object numberOfTitles()
{
return null;
}
public double playTime() {
return 0;
}
public Object findSong(Song song1) {
if(song1.equals("song1")&&song1.equals("song2")&&
song1.equals("song3")&&song1.equals("song4"))
itr.next();
return true;
}
public void sortByArtist()
{
}
public boolean removeFromPlist(Song str) {
songList.remove(str);
return true;
}
}
And this is playlisttest.java for unit testing:
import junit.framework.TestCase;
import java.util.Vector;
public class PlaylistTest extends TestCase {
private Playlist<Song> aPlaylist;
private Song song1, song2, song3, song4, duplicate_song, nullSong;
public void setUp() {
aPlaylist= new Playlist<Song>("Playlist Title");
song1 = new Song("Artist1", "AA", 6.00);
song2 = new Song("Artist1", "BB", 3.50);
song3 = new Song("Artist2", "BB", 3.00);
song4 = new Song("Artist2", "CC", 5.50);
duplicate_song = new Song("ARTIST1", "TITLE1", 5.00); // Same song with song 1
nullSong = null;
}
protected void tearDown() throws Exception
{
super.tearDown();
}
protected void fillPlaylist() {
aPlaylist.addtoPlist(song1);
aPlaylist.addtoPlist(song2);
aPlaylist.addtoPlist(song3);
aPlaylist.addtoPlist(song4);
}
public void test_Constructor() {
assertNotNull(aPlaylist);
assertTrue(aPlaylist instanceof Vector);
assertTrue(aPlaylist.isEmpty());
}
public void test_getTitle() {
assertTrue(aPlaylist.getTitle().equals("Playlist Title"));
}
public void test_addtoPList() {
assertTrue(aPlaylist.isEmpty());
assertTrue(aPlaylist.addtoPlist(song1));
assertEquals(1, aPlaylist.size());
assertTrue(aPlaylist.addtoPlist(song2));
assertTrue(aPlaylist.addtoPlist(song3));
assertEquals(3, aPlaylist.size());
assertFalse(aPlaylist.addtoPlist(nullSong));
assertEquals(3, aPlaylist.size());
assertFalse(aPlaylist.addtoPlist(duplicate_song));
assertEquals(3, aPlaylist.size());
}
public void test_removeSong() {
fillPlaylist();
int size = aPlaylist.size();
assertFalse(aPlaylist.removeFromPlist(nullSong));
assertEquals(size, aPlaylist.size());
assertFalse(aPlaylist.removeFromPlist(new Song("Artist1", "Title1", 1.00)));
assertEquals(size, aPlaylist.size());
assertTrue(aPlaylist.contains(duplicate_song));
assertTrue(aPlaylist.removeFromPlist(duplicate_song)); // Removing "duplicate_song" is removing "song1"
assertEquals(size - 1, aPlaylist.size());
}
public void test_getSong() {
fillPlaylist();
assertTrue(aPlaylist.getSong(0) instanceof Song);
assertEquals(song1, aPlaylist.getSong(0));
assertEquals(duplicate_song, aPlaylist.getSong(0));
assertEquals(song2, aPlaylist.getSong(1));
assertEquals(song3, aPlaylist.getSong(2));
assertEquals(song4, aPlaylist.getSong(3));
}
public void test_hasTitle() {
fillPlaylist();
assertTrue(aPlaylist.hasTitle("Playlist Title"));
assertFalse(aPlaylist.hasTitle("wrong title"));
}
public void test_hasArtist() {
fillPlaylist();
assertTrue(aPlaylist.hasArtist("artist1"));
assertFalse(aPlaylist.hasArtist("wrong artist"));
}
public void test_numberOfSongs() {
fillPlaylist();
assertEquals(4, aPlaylist.numberOfSongs());
}
public void test_numberOfArtists() {
fillPlaylist();
assertEquals(2, aPlaylist.numberOfArtists());
}
public void test_numberOfTitles() {
fillPlaylist();
assertEquals(3, aPlaylist.numberOfTitles());
}
public void test_playTime() {
fillPlaylist();
assertTrue(aPlaylist.playTime() == 19.00);
}
public void test_findElement() {
fillPlaylist();
assertEquals(0, aPlaylist.findSong(song1));
assertEquals(1, aPlaylist.findSong(song2));
assertEquals(2, aPlaylist.findSong(song3));
assertEquals(3, aPlaylist.findSong(song4));
//assertEquals(-1, aPlaylist.findSong(new Song("Not", "There", 0)));
}
public void test_sortByArtist() {
// TODO: Assignment 6 -- create new test case here: sort by artist
}
public void test_sortByTitle() {
}
}
I would like to implement numberOfTitles method that retrieves number of different titles but I really do not know how to take different titles and return the count (which is 3 in our case as it could be seen).If possible could you help me to solve this please? Your help is greatly appreciated!
You can store the unique titles in a Set and get the size of this once you're done:
Set<String> uniqueSongs = new Set<>();
for (Song song : songList)
{
uniqueSongs.add(song.getTitle());
}
Then you can use uniqueSongs.size() to get the number of unique songs.
You can also use streams if you're in Java 8 (thanks to Boris the Spider for pointing it out):
Set<String> uniqueSongs = songList.stream().map(Song::getTitle).collect(Collectors.toSet())
I have a Javafx TableView where I can add new Rows by double Click on an empty Row at the End of my "filled" / Textfield filled Rows.
My Problem is,if i add some Rows ,Java don't give me more of the empty Rows I could double click to add some Rows.
Edit:removed some unnessary log
To see what i mean, here is the Code:
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TextArea;
import javafx.util.Callback;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
interface inside_table
{
public void Select_Row_by_Col(int index);
}
public class Supermain extends Application {
ObservableList<myTextRow> data;
#Override
public void start(Stage primaryStage) {
ArrayList myindizes=new ArrayList();
final TableView<myTextRow> table = new TableView<>();
table.setEditable(true);
table.setStyle("-fx-text-wrap: true;");
//Table columns
TableColumn<myTextRow, String> clmID = new TableColumn<>("ID");
clmID.setMinWidth(160);
clmID.setCellValueFactory(new PropertyValueFactory<>("ID"));
TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
clmtext.setMinWidth(160);
clmtext.setCellValueFactory(new PropertyValueFactory<>("text"));
clmtext.setCellFactory(new TextFieldCellFactory("text"));
TableColumn<myTextRow, String> clmtext2 = new TableColumn<>("Text2");
clmtext2.setMinWidth(160);
clmtext2.setCellValueFactory(new PropertyValueFactory<>("text2"));
clmtext2.setCellFactory(new TextFieldCellFactory("text2"));
//Add data
data = FXCollections.observableArrayList(
new myTextRow(5, "Lorem","bla"),
new myTextRow(2, "Ipsum","bla")
);
table.getColumns().addAll(clmID, clmtext,clmtext2);
table.setItems(data);
table.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getButton().equals(MouseButton.PRIMARY)) {
if (mouseEvent.getClickCount() == 2 && mouseEvent.getY()>24) {
data.add(new myTextRow(td_get_biggest_ID() + 1,"",""));
table.selectionModelProperty().get().select(data.size()-1);
}
}
}
});
HBox hBox = new HBox();
hBox.setSpacing(5.0);
hBox.setPadding(new Insets(5, 5, 5, 5));
Button btn = new Button();
btn.setText("Get Data");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
for (myTextRow data1 : data) {
System.out.println("data:" + data1.getText2());
}
}
});
hBox.getChildren().add(btn);
BorderPane pane = new BorderPane();
pane.setTop(hBox);
pane.setCenter(table);
primaryStage.setScene(new Scene(pane, 640, 480));
primaryStage.show();
class I_table implements inside_table{
#Override
public void Select_Row_by_Col(int index) {
table.getSelectionModel().select(index);
}
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public static class TextFieldCellFactory
implements Callback<TableColumn<myTextRow, String>, TableCell<myTextRow, String>> {
private String ColumnName;
public TextFieldCellFactory(String ColumnName){
this.ColumnName=ColumnName;
}
#Override
public TableCell<myTextRow, String> call(TableColumn<myTextRow, String> param) {
TextFieldCell textFieldCell = new TextFieldCell(this.ColumnName);
return textFieldCell;
}
public static class TextFieldCell extends TableCell<myTextRow, String> {
private TextArea textField;
private StringProperty boundToCurrently = null;
private String last_text;
private String ColumnName;
public TextFieldCell(String cname) {
textField = new TextArea();
textField.setWrapText(true);
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
last_text="";
this.ColumnName=cname;
this.setGraphic(textField);
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if(this.ColumnName=="text2"){
if(isNowFocused){last_text=textField.getText();System.out.println("NOW focus "+last_text);}
if (! isNowFocused && ! isValid(textField.getText())) {
textField.setText(last_text);
//textField.setText("00:00:00:00");
textField.selectAll();
System.out.println("blur");
}
}
});
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
// Show the Text Field
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
// myindizes.add(getIndex());
// Retrieve the actual String Property that should be bound to the TextField
// If the TextField is currently bound to a different StringProperty
// Unbind the old property and rebind to the new one
ObservableValue<String> ov = getTableColumn().getCellObservableValue(getIndex());
SimpleStringProperty sp = (SimpleStringProperty) ov;
if (this.boundToCurrently == null) {
this.boundToCurrently = sp;
this.textField.textProperty().bindBidirectional(sp);
} else if (this.boundToCurrently != sp) {
this.textField.textProperty().unbindBidirectional(this.boundToCurrently);
this.boundToCurrently = sp;
this.textField.textProperty().bindBidirectional(this.boundToCurrently);
}
double height = real_lines_height(textField.getText(), this.getWidth(), 30, 22);
textField.setPrefHeight(height);
textField.setMaxHeight(height);
textField.setMaxHeight(Double.MAX_VALUE);
// if height bigger than the biggest height in the row
//-> change all heights of the row(textfields ()typeof textarea) to this height
// else leave the height as it is
//System.out.println("item=" + item + " ObservableValue<String>=" + ov.getValue());
//this.textField.setText(item); // No longer need this!!!
} else {
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}//update
private boolean isValid(String s){
String splitArray[] = s.split(":");
if (splitArray.length != 4) {
System.out.println("false");
return false;
}
for (int i = 0; i < splitArray.length; i++) {
if (splitArray[i].length() != 2) {
System.out.println("false");
return false;
}
if (!splitArray[i].substring(0, 1).matches("[0-9]")) {
System.out.println("no number1");
return false;
}
if (!splitArray[i].substring(1, 2).matches("[0-9]")) {
System.out.println("no number2");
return false;
}
if (i < 3) {
int itest = Integer.parseInt(splitArray[i]);
if (itest > 59) {
System.out.println(itest + " ist zu groß!");
return false;
}
} else {
int itest2 = Integer.parseInt(splitArray[i]);
if (itest2 > Math.floor(25)) {
System.out.println(itest2 + " ist zu groß!");
return false;
}
//framerate!!!!!
}
System.out.println("splits: " + splitArray[i]);
//if( el.charAt(0).)
}
return true;
}
}
}
public class myTextRow {
private final SimpleIntegerProperty ID;
private final SimpleStringProperty text;
private final SimpleStringProperty text2;
public myTextRow(int ID, String text,String text2) {
this.ID = new SimpleIntegerProperty(ID);
this.text = new SimpleStringProperty(text);
this.text2 = new SimpleStringProperty(text2);
}
//setter
public void setID(int id) {
this.ID.set(id);
}
public void setText(String text) {
this.text.set(text);
}
public void setText2(String text) {
this.text2.set(text);
}
//getter
public int getID() {
return ID.get();
}
public String getText() {
return text.get();
}
public String getText2() {
return text2.get();
}
//properties
public StringProperty textProperty() {
return text;
}
public StringProperty text2Property() {
return text2;
}
public IntegerProperty IDProperty() {
return ID;
}
}
private static double real_lines_height(String s, double width, double heightCorrector, double widthCorrector) {
HBox h = new HBox();
Label l = new Label("Text");
h.getChildren().add(l);
Scene sc = new Scene(h);
l.applyCss();
double line_height = l.prefHeight(-1);
int new_lines = s.replaceAll("[^\r\n|\r|\n]", "").length();
// System.out.println("new lines= "+new_lines);
String[] lines = s.split("\r\n|\r|\n");
// System.out.println("line count func= "+ lines.length);
int count = 0;
//double rest=0;
for (int i = 0; i < lines.length; i++) {
double text_width = get_text_width(lines[i]);
double plus_lines = Math.ceil(text_width / (width - widthCorrector));
if (plus_lines > 1) {
count += plus_lines;
//rest+= (text_width / (width-widthCorrector)) - plus_lines;
} else {
count += 1;
}
}
//count+=(int) Math.ceil(rest);
count += new_lines - lines.length;
return count * line_height + heightCorrector;
}
private static double get_text_width(String s) {
HBox h = new HBox();
Label l = new Label(s);
l.setWrapText(false);
h.getChildren().add(l);
Scene sc = new Scene(h);
l.applyCss();
// System.out.println("FXMLDocumentController.get_text_width(): "+l.prefWidth(-1));
return l.prefWidth(-1);
}
public int td_get_biggest_ID() {
int biggest = 0;
for (int i = 0; i < data.size(); i++) {
if (((myTextRow) data.get(i)).getID() > biggest) {
biggest = ((myTextRow) data.get(i)).getID();
}
}
return biggest;
}
}
Just click anywhere else on the TableView but make sure it's at least 24 pixels from the top; This will work since you've added the event handler is added to the TableView...
If you only want to use the last row, then use a custom rowFactory and handle the events there.
Add a placeholder item to the TableView items that marks the row that is used for adding new elements (for some reason the selection model doesn't like null):
final myTextRow addPlaceHolder = new myTextRow(Integer.MIN_VALUE, null, null);
...
//Add data
data = FXCollections.observableArrayList(
new myTextRow(5, "Lorem", "bla"),
new myTextRow(2, "Ipsum", "bla"),
addPlaceHolder
);
make sure your TextFieldCells treat null values as empty rows:
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
// Show the Text Field
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
...
make sure the first column does not display anything for the placeholder
//Table columns
TableColumn<myTextRow, Number> clmID = new TableColumn<>("ID");
clmID.setMinWidth(160);
clmID.setCellValueFactory(cdf -> {
myTextRow item = cdf.getValue();
return item == addPlaceHolder ? Bindings.createObjectBinding(() -> null) : item.IDProperty();
});
and use the following rowFactory to handle adding the items (you don't need the updateItem part unless you need to add a style class to the TableRow; you need not extend TableRow in this case)
table.setRowFactory(tv -> new TableRow<myTextRow>() {
{
setOnMouseClicked(mouseEvent -> {
if (mouseEvent.getButton() == MouseButton.PRIMARY
&& mouseEvent.getClickCount() == 2
&& !isEmpty()
&& getItem() == addPlaceHolder) {
data.add(data.size() - 1, new myTextRow(td_get_biggest_ID() + 1, "", ""));
table.selectionModelProperty().get().select(data.size() - 1);
mouseEvent.consume();
}
});
}
#Override
protected void updateItem(myTextRow item, boolean empty) {
super.updateItem(item, empty);
// add style class for row containing addPlaceHolder
List<String> classes = getStyleClass();
final String clazz = "add-row";
if (item == addPlaceHolder) {
if (!classes.contains(clazz)) {
classes.add(clazz);
}
} else {
classes.remove(clazz);
}
}
});