Basically, I have managed to create a comboBoxTableCell within my tableView. The only problem is that I cannot type in any user input and without this function, the comboBoxTableCell is no different from a Choice box table cell to me.
Now from looking at this, if I can set setComboBoxEditable(true)for comboBoxTableCell, then I think my problem will be sorted. Question is how do I do this in the following line of code?
public TableColumn<Trade,String> tableColumnX;
tableColumnX.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
tableColumnX.setCellFactory(ComboBoxTableCell.forTableColumn(obsList));
I have taken a second attempt on this using what I saw on this post,
tableColumnX.setCellFactory(new Callback<TableColumn<Product, String>,ComboBoxTableCell<Product,String>>() {
#Override
public ComboBoxTableCell<Product, String> call(TableColumn<Product, String> param) {
ComboBoxTableCell ct= new ComboBoxTableCell<>();
ct.setComboBoxEditable(true);
return ct;
}});
This time I am getting an error:
The method setCellFactory(Callback<TableColumn<Product,String>,TableCell<Product,String>>) in the type TableColumn<Product,String> is not applicable for the arguments (new Callback<TableColumn<Product,String>,ComboBoxTableCell<Product,String>>(){})
If I am not clear on any parts of the question, please let me know. I can add in more details for clarification.
Use Callback<TableColumn<Product, String>,TableCell<Product,String>>() instead of Callback<TableColumn<Product, String>,ComboBoxTableCell<Product,String>>()
Try this :
tableColumnX.setCellFactory(new Callback<TableColumn<Product, String>,TableCell<Product,String>>() {
#Override
public TableCell<Product, String> call(TableColumn<Product, String> param) {
ComboBoxTableCell<Product, String> ct= new ComboBoxTableCell<>();
ct.setComboBoxEditable(true);
return ct;
}});
Here is an Example
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
/**
*
* #author
*/
public class Javafx_test extends Application {
#Override
public void start(Stage stage) {
TableColumn<String, String> tableColumn = new TableColumn<>("Column");
tableColumn.setCellValueFactory(cellData
-> {
return new SimpleStringProperty(cellData.getValue());
}
);
tableColumn.setCellFactory(tableCol -> {
ComboBoxTableCell<String, String> ct = new ComboBoxTableCell<>();
ct.getItems().addAll("1", "2");
ct.setComboBoxEditable(true);
return ct;
});
TableView<String> tableView = new TableView<>();
tableView.setEditable(true);
tableView.getColumns().add(tableColumn);
tableView.getItems().addAll("4", "5");
BorderPane root = new BorderPane(tableView);
Scene scene = new Scene(root, 200, 100);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Related
I am using ControlFx Textfields binding which works find. The thing is that the Suggestion popup values that Contains the inputed value. Like if i type in "M" values will be "Emma", "max", "rosemary", "Mathew". So my main Question is how to make the Suggestion to only bring out values Starting with "M" like "max",""Mathew"
This is my code. It select name from database and add them to an ArrayList
PreparedStatement ps=db.DataBase.getCon().prepareStatement("select name from STUDENTINFO");
ResultSet res=ps.executeQuery();
List list=new ArrayList();
while(res.next()){
list.add(res.getString("name"));
}
TextFields.bindAutoCompletion(textfieldSearch,list);
Here is a sample app that demos your question.
This snippet searches the original list for a substring and returns every item in the list that starts with the substring.
this snippet should work with your code.
Key code:
TextFields.bindAutoCompletion(textFieldSearch, t -> {
return list.stream().filter(elem ->
{
return elem.toLowerCase().startsWith(t.getUserText().toLowerCase());
}).collect(Collectors.toList());
});
Full app:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.controlsfx.control.textfield.TextFields;
/**
*
* #author blj0011
*/
public class JavaFXApplication193 extends Application
{
#Override
public void start(Stage primaryStage)
{
List<String> list = new ArrayList();
list.add("Max");
list.add("moon");
list.add("am");
list.add("two");
TextField textFieldSearch = new TextField();
TextFields.bindAutoCompletion(textFieldSearch, t -> {
return list.stream().filter(elem
-> {
return elem.toLowerCase().startsWith(t.getUserText().toLowerCase());
}).collect(Collectors.toList());
});
StackPane root = new StackPane(textFieldSearch);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
so I'm writing a javafx app and I need to be able to select the cells from the list view (for copy paste purposes) but I don't want to make it editable, I mean, the content cannot be changed unless I want to (allowing it through a button, for example).
So I have the following code:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
List<String> contacts = new ArrayList<>(Arrays.asList("968787522","3424234234","2343234324"));
ListView<String> contactsList = new ListView();
contactsList.setItems(FXCollections.observableArrayList(contacts));
//this gives me the ability to edit the row as text field but I want this text field to not be editable
contactsList.setCellFactory(TextFieldListCell.forListView());
StackPane pane = new StackPane();
pane.getChildren().add(contactsList);
primaryStage.setScene(new Scene(pane, 300, 275));
primaryStage.show(); }
public static void main(String[] args) {
launch(args);
}
}
and if I set 'contactsList' as not editable, I'm not able to edit, neither select.
As you can see (image bellow),I'm editing the cell, but I want to be able to select the text(not the item), but I don't want to be able to delete characters (text selectable but not editable).
so after breaking my head off, lots of research and API reading, I came up with a solution. This does EXACTLY what I wanted to do. Here is the demo if someone needs it ;)
So the idea is, each time we want to select the content of a row we need to select the row, get the textField and set the editing to true or false, (every time).
So in the demo that I made, I placed a button so you can toggle the editing to true or false to be sure that's is working, and how is working.
Cheers.
I commented some of the code for better understanding, if you have any questions about this just let me know.
package sample;
import com.sun.javafx.scene.control.skin.VirtualFlow;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main extends Application {
private boolean editable = false;
public static IndexedCell getCell(final Control control, final int index) {
return getVirtualFlow(control).getCell(index);
}
public static VirtualFlow<?> getVirtualFlow(Control control) {
Group group = new Group();
Scene scene = new Scene(group);
Stage stage = new Stage();
if(control.getScene() == null) {
group.getChildren().setAll(control);
stage.setScene(scene);
stage.show();
}
VirtualFlow<?>flow = (VirtualFlow<?>) control.lookup("#virtual-flow");
return flow;
}
public void setEditable(ListView contactsList){
//this needs to be done since we need to run our code after the text field was rendered
//so we need to invoke our code after this happens, if not it will throw a null pointer...
Platform.runLater(() -> {
//this is one of the most important guys because javafx api says that
//TextFieldListCell.forListView() allows editing of the cell content when the cell is double-clicked,
// or when {#link ListView#edit(int)} is called.
int rowIndex = contactsList.getSelectionModel().getSelectedIndex();
contactsList.edit(rowIndex);
ListCell rootCell = (ListCell) getCell(contactsList, rowIndex);
TextField textField = (TextField) rootCell.getGraphic();
textField.setEditable(editable);
});
}
#Override
public void start(Stage primaryStage) throws Exception{
List<String> contacts = new ArrayList<>(Arrays.asList("968787522","3424234234","2343234324"));
ListView<String> contactsList = new ListView();
contactsList.setItems(FXCollections.observableArrayList(contacts));
contactsList.setEditable(true);
//this gives me the ability to edit the row as text field but I want this text field to not be editable
contactsList.setCellFactory(TextFieldListCell.forListView());
contactsList.setOnEditStart(e -> {
setEditable(contactsList);
});
StackPane pane = new StackPane();
Button editBtn = new Button("Toggle edit");
editBtn.setOnAction(event -> {
editable = !editable;
editBtn.setText("Editing = " + editable);
//to cancel any editing that might be occuring
contactsList.getSelectionModel().clearSelection();
});
pane.getChildren().addAll(contactsList,editBtn);
primaryStage.setScene(new Scene(pane, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If I understand you correctly, it is not necessary to set the listview to 'not editable', as the default behaviour should suffice for your purpose. Take a look at this code, for example:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class NewFXMain extends Application {
#Override
public void start(Stage primaryStage) {
ListView listView = new ListView();
listView.getItems().addAll("one","two","three","four");
listView.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println(listView.getSelectionModel().getSelectedItem());
}
});
StackPane root = new StackPane();
root.getChildren().add(listView);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("ListView Example");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I changed nothing about the editable-property of the ListView, but I can select every item, without being able to edit it (in the sense of changing its value). You can easily add an EventHandler to the ListView to perform whatever operation you want to perform. You could also add an EventHandler to every cell of the ListView by manipulating the CellFactory, as shown in this answer: How to handle ListView item clicked action?
Here's what works for me:
TableView<DataBean> table = new TableView<>();
table.setItems(...); // list of some DataBean objects with dataBeanField proprty
table.setEditable(true);
TableColumn<DataBean, String> column = new TableColumn<>("SomeData");
column.setCellValueFactory(new PropertyValueFactory<DataBean, String>("dataBeanField"));
column.setCellFactory(new Callback<TableColumn<DataBean, String>, TableCell<DataBean, String>>() {
#Override
public TableCell<DataBean, String> call(TableColumn<DataBean, String> param) {
return new TextFieldTableCell<>(new DefaultStringConverter() {
private String defaultValue = "";
#Override
public String fromString(String newValue) {
return super.fromString(defaultValue);
}
#Override
public String toString(String value) {
return defaultValue = super.toString(value);
}
});
}
});
Basically my code is like this:
fileOpener.setOnAction(
new EventHandler<ActionEvent>() {
#Override
public void handle(final ActionEvent e) {
myFileList.add(openMusicTracks.showOpenDialog(window));
System.out.println(myFileList.getName(0)); //prints file name so I know this works
}
});
I want the add method (that's inside of the EventHandler) to actually edit the arraylist for everywhere else so that later when I reference it in
ObservableList<String> playList = FXCollections.observableArrayList ();
for(int i = 0; i < myFileList.size(); i++) {
playList.add(i, myFileList.get(i).getName());
System.out.println(myFileList.getName(0)); //doesn't print the file name, so I know this doesn't work.
}
the arraylist won't be empty. How do I do this? I'm sorry if there's a more elegant way to word this, but I have honestly no idea how to research this, I've tried. Thanks.
A simple example which shows how can an ArrayList be shared between methods.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
public class Main extends Application {
private List<String> list = new ArrayList<>();
#Override
public void start(Stage primaryStage) {
Button add = new Button("Add");
Button display = new Button("Show");
// Add Items
add.setOnAction(event -> list.add("Item"));
// Display Items
display.setOnAction(e -> {
printAndClear();
});
VBox root = new VBox(10, add, display);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 200, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
private void printAndClear() {
list.forEach(System.out::println);
list.clear();
}
public static void main(String[] args) {
launch(args);
}
}
I've written the following code.
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.geometry.Pos;
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.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
import javafx.util.Callback;
public class App extends Application {
private ListView<String> listView;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
List<String> friendList = new ArrayList<String>();
friendList.add("Alice");
friendList.add("Bob");
listView = new ListView<>(FXCollections.observableArrayList(friendList));
listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> p) {
ListCell<String> cell = new ListCell<String>() {
#Override
protected void updateItem(String t, boolean empty) {
super.updateItem(t, empty);
if (t != null) {
Label usernameLabel = new Label(t);
usernameLabel.setFont(Font.font("Arial", FontWeight.BOLD, 12));
Button callButton = new Button("Call");
callButton.setOnAction(e -> System.out.println("action")); // not working
callButton.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> System.out.println("entered"));
callButton.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> System.out.println("clicked")); // not working
HBox usernameBox = new HBox(5);
usernameBox.setAlignment(Pos.CENTER_LEFT);
usernameBox.getChildren().addAll(usernameLabel);
BorderPane borderPane = new BorderPane();
borderPane.setLeft(usernameBox);
borderPane.setRight(callButton);
VBox vbox = new VBox(3);
vbox.getChildren().addAll(borderPane);
setGraphic(vbox);
}
}
};
return cell;
}
});
stage.setScene(new Scene(listView));
stage.show();
}
}
If you look at the callButton, you see that it gets three different handlers. However, only the MOUSE_ENTERED event handler is really triggered. The other ones are completely ignored. What can be the problem?
EDIT: Added and removed some code, in order to make it runnable.
This is a known bug in JavaFX 8, which is fixed in the latest ea release (1.8.0_20).
As a workaround, create the controls once and register handlers with them, then just update their state in the updateItem(...) method:
listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> p) {
Label usernameLabel = new Label();
usernameLabel.setFont(Font.font("Arial", FontWeight.BOLD, 12));
Button callButton = new Button("Call");
HBox usernameBox = new HBox(5);
usernameBox.setAlignment(Pos.CENTER_LEFT);
usernameBox.getChildren().addAll(usernameLabel);
BorderPane borderPane = new BorderPane();
borderPane.setLeft(usernameBox);
borderPane.setRight(callButton);
VBox vbox = new VBox(3);
vbox.getChildren().addAll(borderPane);
ListCell<String> cell = new ListCell<String>() {
#Override
protected void updateItem(String t, boolean empty) {
super.updateItem(t, empty);
if (t != null) {
usernameLabel.setText(t);
setGraphic(vbox);
} else {
setGraphic(null); // you will have weird bugs without this: don't omit it
}
}
};
callButton.setOnAction(e -> System.out.println("action: "+cell.getItem()));
callButton.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> System.out.println("entered "+ cell.getItem()));
callButton.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> System.out.println("clicked "+ cell.getItem()));
return cell;
}
});
Note that this "workaround" is really the preferred approach anyway, and the one that was intended by the designers of the "virtualized" controls like ListView, TableView, etc. The point is that updateItem(...) is potentially called very frequently by the application, whereas cells are created very rarely. By creating new controls in the updateItem(...) method you potentially introduce performance issues. Create them once for the cell, and then just configure them in updateItem(...). Note also how I just registered the event handlers once, and had the handlers refer to cell.getItem() to see which item is currently represented by the cell.
One last thing: you have a bug in your code (which I fixed). Since cells can be reused, including for the case where a cell displaying an item is reused for an empty cell, it's important that you always handle the case where the item is null (typically by setting text and/or graphic to null).
Could you add the code of getIconAndResizeTo16( String s ). I guess the node you return there consumes mouse clicks.
Here is a runnable example that demonstrates the issue. It is just a guess though.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class App extends Application {
public static void main(String[] args) { launch(args); }
#Override
public void start(Stage stage) throws Exception {
Button callButton = new Button("", getIconAndResizeTo16("Phone"));
callButton.setOnAction(e -> System.out.println("clicked1")); // not working
callButton.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> System.out.println("entered"));
callButton.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> System.out.println("clicked")); // not working
Button chatButton = new Button("", getIconAndResizeTo16("Chat") );
chatButton.setOnAction(e -> System.out.println("clicked2")); // not working
HBox callIconBox = new HBox(3);
callIconBox.setAlignment(Pos.CENTER_RIGHT);
callIconBox.getChildren().addAll(callButton, chatButton);
stage.setScene(new Scene(callIconBox));
stage.show();
}
private Node getIconAndResizeTo16(String s) {
Label l = new Label("Consumes " + s + " Events");
l.addEventHandler(MouseEvent.MOUSE_PRESSED, e -> { e.consume(); });
l.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> { e.consume(); });
return l;
}
}
I want to create very useful and easy way to live update Pie chart. For example:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class MainApp extends Application {
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Imported Fruits");
stage.setWidth(500);
stage.setHeight(500);
ObservableList<PieChart.Data> pieChartData =
FXCollections.observableArrayList(
new PieChart.Data("Grapefruit", 13),
new PieChart.Data("Oranges", 25),
new PieChart.Data("Plums", 10),
new PieChart.Data("Pears", 22),
new PieChart.Data("Apples", 30));
final PieChart chart = new PieChart(pieChartData);
chart.setTitle("Imported Fruits");
final Label caption = new Label("");
caption.setTextFill(Color.DARKORANGE);
caption.setStyle("-fx-font: 24 arial;");
for (final PieChart.Data data : chart.getData()) {
data.getNode().addEventHandler(MouseEvent.MOUSE_PRESSED,
new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent e) {
caption.setTranslateX(e.getSceneX());
caption.setTranslateY(e.getSceneY());
caption.setText(String.valueOf(data.getPieValue())
+ "%");
}
});
}
((Group) scene.getRoot()).getChildren().addAll(chart, caption);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
When I display the chart I want to call Java Method and update the chart like this:
PieChartUpdate(valueOne, valueTwo, valueThree);
Can you show me how I can edit the code in order to make the live updates more easy to use?
As far as i could see, all classes that are used to establish a PieChart, like PieChart.Data and of course the ObservableList are already designed so that they will update the PieChart the moment something changes, be it the list itself or values inside the Data Objects. See the binding chapters how this is done. But you don't need to write your own bindings for the PieChart.
The code below should do what you want. Use addData(String name, double value) to create a new Data object for your pie chart, or update an existing one which has the same name like the first parameter of the method. The PieChart will automatically play a animation when changes are made to the list (new Data object added) or a Data object got changed.
//adds new Data to the list
public void naiveAddData(String name, double value)
{
pieChartData.add(new Data(name, value));
}
//updates existing Data-Object if name matches
public void addData(String name, double value)
{
for(Data d : pieChartData)
{
if(d.getName().equals(name))
{
d.setPieValue(value);
return;
}
}
naiveAddData(name, value);
}
Just in case someone feels extremely lost and isn't sure how to implement denhackl's answer, here is a working version of what he tried to explain.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class LivePie extends Application {
ObservableList<PieChart.Data> pieChartData;
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Imported Fruits");
stage.setWidth(500);
stage.setHeight(500);
this.pieChartData =
FXCollections.observableArrayList();
addData("Test", 5.1);
addData("Test2", 15.1);
addData("Test3", 3.1);
addData("Test1", 4.9);
addData("Test2", 15.1);
addData("Test3", 2.1);
addData("Test5", 20.1);
final PieChart chart = new PieChart(pieChartData);
chart.setTitle("Imported Fruits");
final Label caption = new Label("");
caption.setTextFill(Color.DARKORANGE);
caption.setStyle("-fx-font: 24 arial;");
((Group) scene.getRoot()).getChildren().addAll(chart, caption);
stage.setScene(scene);
stage.show();
}
public void naiveAddData(String name, double value)
{
pieChartData.add(new javafx.scene.chart.PieChart.Data(name, value));
}
//updates existing Data-Object if name matches
public void addData(String name, double value)
{
for(javafx.scene.chart.PieChart.Data d : pieChartData)
{
if(d.getName().equals(name))
{
d.setPieValue(value);
return;
}
}
naiveAddData(name, value);
}
public static void main(String[] args) {
launch(args);
}
}
Many thanks to the creator of the topic and the answers provided!
Here's a good introductory article on using properties and binding.
http://docs.oracle.com/javafx/2/binding/jfxpub-binding.htm