javafx : how to programmatically change items of ComboBox in TableCell? - combobox

as you can see already, i have a TableView which have two columns of ComboBoxTableCell.
these are the ancestor values
ObservableList<String> ancestors = FXCollections.observableArrayList("1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
ancestorColumn.setCellFactory(ComboBoxTableCell.forTableColumn(ancestors));
descendantColumn.setCellFactory(ComboBoxTableCell.forTableColumn());
Map<String, List<String>> descendantMap = new HashMap();
descendantMap.put("1", Arrays.asList("A", "B", "C"));
descendantMap.put("2", Arrays.asList("Z", "L", "C"));
descendantMap.put("3", Arrays.asList("A", "B", "R"));
descendantMap.put("4", Arrays.asList("C", "B", "E"));
descendantMap.put("5", Arrays.asList("A", "E", "C"));
descendantMap.put("6", Arrays.asList("M", "V", "T"));
descendantMap.put("7", Arrays.asList("A", "G", "F"));
descendantMap.put("8", Arrays.asList("J", "O", "N"));
descendantMap.put("9", Arrays.asList("X", "G", "E"));
descendantMap.put("10", Arrays.asList("H", "I", "J"));
now when i change selection of ancestor combobox, the respective descendant combobox should get filled with items in the map of that key. ie, if i change ancestor to "1" on a table row, the descendant combobox on the same row should get filled with ("A", "B", "C). how to achieve this?
EDIT:
hi, i've done it and it works. but im not sure if its the right way to do it. can someone tell me if this is the right way to do it?
descendantColumn.setCellFactory(param -> {
return new TableCell() {
ComboBox box = new ComboBox();
#Override
public void updateItem(String item, boolean empty) {
if(!empty) {
setGraphic(box);
ancestorColumn.getCellObservableValue(getIndex()).addListener((o, ov, nv) -> {
if(nv != null) {
box.setItems(descendantMap.get(nv));
}
});
} else {
setGraphic(null);
}
}
};
});

I've created an example of how I would do that. But please: I build this within half an hour or so - the code is far from perfect, ok?
Here is the full code...
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<!--
Do not edit this file it is generated by e(fx)clipse from ../src/application/TableTest.fxgraph
-->
<?import java.lang.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns:fx="http://javafx.com/fxml" fx:controller="application.TableTestController">
<center>
<TableView fx:id="tableView">
<columns>
<TableColumn fx:id="tableColumnText" text="Text" prefWidth="150"/>
<TableColumn fx:id="tableColumnFlag" text="Flag" prefWidth="50"/>
<TableColumn fx:id="tableColumnAncestor" text="Ancestor" prefWidth="75"/>
<TableColumn fx:id="tableColumnDescendant" text="Descendant" prefWidth="100"/>
<TableColumn fx:id="tableColumnPoints" text="Points" prefWidth="75"/>
</columns>
<BorderPane.margin>
<Insets top="5" left="5" right="5" bottom="5"/>
</BorderPane.margin>
</TableView>
</center>
</BorderPane>
Model data class (the one, that is representing a single line in the TableView):
package application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class TableTestObject {
private StringProperty text = new SimpleStringProperty("");
private BooleanProperty flag = new SimpleBooleanProperty(false);
private StringProperty ancestor = new SimpleStringProperty("");
private StringProperty descendant = new SimpleStringProperty("");
private IntegerProperty points = new SimpleIntegerProperty(0);
public final StringProperty textProperty() {
return this.text;
}
public final String getText() {
return this.textProperty().get();
}
public final void setText(final String text) {
this.textProperty().set(text);
}
public final BooleanProperty flagProperty() {
return this.flag;
}
public final boolean isFlag() {
return this.flagProperty().get();
}
public final void setFlag(final boolean flag) {
this.flagProperty().set(flag);
}
public final StringProperty ancestorProperty() {
return this.ancestor;
}
public final String getAncestor() {
return this.ancestorProperty().get();
}
public final void setAncestor(final String ancestor) {
this.ancestorProperty().set(ancestor);
}
public final StringProperty descendantProperty() {
return this.descendant;
}
public final String getDescendant() {
return this.descendantProperty().get();
}
public final void setDescendant(final String descendant) {
this.descendantProperty().set(descendant);
}
public final IntegerProperty pointsProperty() {
return this.points;
}
public final int getPoints() {
return this.pointsProperty().get();
}
public final void setPoints(final int points) {
this.pointsProperty().set(points);
}
}
Main class:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("TableTest.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root, 1200, 800);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
TableTestController (the one, that is doing the binding stuff):
package application;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
public class TableTestController {
#FXML
private TableView<TableTestObject> tableView;
#FXML
private TableColumn<TableTestObject, String> tableColumnText;
#FXML
private TableColumn<TableTestObject, TableTestObject> tableColumnFlag;
#FXML
private TableColumn<TableTestObject, TableTestObject> tableColumnAncestor;
#FXML
private TableColumn<TableTestObject, TableTestObject> tableColumnDescendant;
#FXML
private TableColumn<TableTestObject, TableTestObject> tableColumnPoints;
private Map<String, List<String>> descendantMap;
private ObservableList<String> ancestorList;
#FXML
protected void initialize() {
descendantMap = new HashMap<>();
descendantMap.put("1", Arrays.asList("A", "B", "C"));
descendantMap.put("2", Arrays.asList("Z", "L", "C"));
descendantMap.put("3", Arrays.asList("A", "B", "R"));
descendantMap.put("4", Arrays.asList("C", "B", "E"));
descendantMap.put("5", Arrays.asList("A", "E", "C"));
descendantMap.put("6", Arrays.asList("M", "V", "T"));
descendantMap.put("7", Arrays.asList("A", "G", "F"));
descendantMap.put("8", Arrays.asList("J", "O", "N"));
descendantMap.put("9", Arrays.asList("X", "G", "E"));
descendantMap.put("10", Arrays.asList("H", "I", "J"));
ancestorList = FXCollections.observableArrayList(descendantMap.keySet());
ObservableList<TableTestObject> data = FXCollections.observableArrayList();
TableTestObject test = new TableTestObject();
test.setText("Test");
test.setFlag(true);
test.setPoints(1);
data.add(test);
tableView.setItems(data);
tableColumnText.setCellValueFactory(new PropertyValueFactory<>("text"));
tableColumnFlag.setCellValueFactory(param -> new SimpleObjectProperty<TableTestObject>(param.getValue()));
tableColumnAncestor.setCellValueFactory(param -> new SimpleObjectProperty<TableTestObject>(param.getValue()));
tableColumnDescendant.setCellValueFactory(param -> new SimpleObjectProperty<TableTestObject>(param.getValue()));
tableColumnPoints.setCellValueFactory(param -> new SimpleObjectProperty<TableTestObject>(param.getValue()));
tableColumnFlag.setCellFactory(param -> new TableCell<TableTestObject, TableTestObject>() {
#Override
protected void updateItem(TableTestObject item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
CheckBox cb = new CheckBox();
cb.selectedProperty().bindBidirectional(item.flagProperty());
setGraphic(cb);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
}
});
tableColumnAncestor.setCellFactory(param -> new TableCell<TableTestObject, TableTestObject>() {
#Override
protected void updateItem(TableTestObject item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
ComboBox<String> cb = new ComboBox<>(ancestorList);
cb.getSelectionModel().selectedItemProperty().addListener((ChangeListener<String>) (observable, oldValue, newValue) -> {
item.ancestorProperty().set(newValue);
System.out.println(item.descendantProperty().get());
});
setGraphic(cb);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
}
});
tableColumnDescendant.setCellFactory(param -> new TableCell<TableTestObject, TableTestObject>() {
#Override
protected void updateItem(TableTestObject item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
ComboBox<String> cb = new ComboBox<>();
item.ancestorProperty().addListener((ChangeListener<String>) (observable, oldValue, newValue) -> {
item.descendantProperty().set("");
cb.setItems(FXCollections.observableArrayList(descendantMap.get(newValue)));
});
cb.getSelectionModel().selectedItemProperty().addListener((ChangeListener<String>) (observable, oldValue, newValue) -> {
item.descendantProperty().set(newValue);
System.out.println(item.descendantProperty().get());
});
setGraphic(cb);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
}
});
tableColumnPoints.setCellFactory(param -> new TableCell<TableTestObject, TableTestObject>() {
#Override
protected void updateItem(TableTestObject item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
Spinner<Integer> spinner = new Spinner<>();
spinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(-5, +5, item.getPoints()));
spinner.valueProperty().addListener((ChangeListener<Integer>) (observable, oldValue, newValue) -> {
item.pointsProperty().set(newValue);
System.out.println(item.pointsProperty().get());
});
setGraphic(spinner);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
}
});
}
}
Once again: The Controller is the one, that is doing the binding. With Lambdas, the code is almost readable, but your should consider reworking it anyway.
Cheers, Daniel

Related

Remove item from a RecyclerView list based on status of a checkbox

I have a list application what is uses RecyclerView.
Each line has got a button, a checkbox and 2 textview.
All object is defined the RecyclerView clas, but I would like to check the checkbox status in the Main class and evaluate the status and remove the item from the list and from the SharedPreferences where the checkbox is checked
Main class is here:
package com.example.homehandling;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class ToDoList extends AppCompatActivity implements MyRecyclerViewAdapter.ItemClickListener, View.OnClickListener {
public MyRecyclerViewAdapter adapter;
public Button btn;
public SharedPreferences sharedPreferences;
public SharedPreferences.Editor myEdit;
public LinkedList<String> items, remain_days, prio;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shoppinglist);
sharedPreferences = getSharedPreferences("ToDoList", Context.MODE_PRIVATE);
myEdit = sharedPreferences.edit();
btn = findViewById(R.id.button);
items = new LinkedList<>();
remain_days = new LinkedList<>();
prio = new LinkedList<>();
RecyclerView recyclerView = findViewById(R.id.list);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new MyRecyclerViewAdapter(this,items,remain_days,prio);
adapter.setClickListener(this);
recyclerView.setAdapter(adapter);
btn.setOnClickListener(this);
StartDisplay();
}
#Override
public void onItemClick(View view, int position) {
}
#Override
public void onClick(View v) {
Intent NewTLItem = new Intent(ToDoList.this, NewTLItem.class);
startActivity(NewTLItem);
}
#Override
public void onResume() {
super.onResume();
StartDisplay();
}
public void StartDisplay()
{
sharedPreferences = getSharedPreferences("ToDoList", Context.MODE_PRIVATE);
Map<String, ?> allEntries = sharedPreferences.getAll();
items.clear();
prio.clear();
remain_days.clear();
for (Map.Entry<String, ?> entry : allEntries.entrySet())
{
String key_word = entry.getKey();
String [] item_total = entry.getValue().toString().split(";");
if(key_word.contains("_")) key_word = key_word.replace("_"," ");
items.add(key_word);
remain_days.add(Long.toString(DateCalc(item_total[0])));
prio.add(item_total[1]);
}
adapter.notifyDataSetChanged();
}
public long DateCalc(String startdate)
{
Date system_date, due_date;
long diff_date = 0;
String currentDate = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(new Date());
SimpleDateFormat dates = new SimpleDateFormat("dd-MM-yyyy");
try {
system_date = dates.parse(currentDate);
due_date = dates.parse(startdate);
diff_date = (due_date.getTime()-system_date.getTime())/86400000;
} catch (ParseException e) {
e.printStackTrace();
}
return diff_date;
}
}
At now the item is removed when the whole line is touched. I would like to exchange to if only the Checkbox is "true".
At now the remove method is in the public void onItemClick(View view, int position) function.
I would like to put the content of the OnItemClick method to "if" part of CheckBox status.
Here is the RecyclerView class:
package com.example.homehandling;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder>
{
public List<String> mData, subdata, subdata2;
public LayoutInflater mInflater;
public ItemClickListener mClickListener;
public Context context;
public CheckBox done;
MyRecyclerViewAdapter(Context context, List<String> data, List<String> subdata, List<String> subdata2) {
this.mInflater = LayoutInflater.from(context);
this.mData = data;
this.subdata = subdata;
this.subdata2 = subdata2;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.recyclerview_row, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
String meta_data = mData.get(position);
String meta_subdata = subdata.get(position);
String meta_subdata2 = subdata2.get(position);
holder.myTextView.setText(meta_data);
holder.subtext.setText(meta_subdata +" "+ meta_subdata2);
holder.add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String[] item_param = holder.myTextView.getText().toString().split(" ");
final Intent intent;
intent = new Intent(context, NewSLItem.class);
intent.putExtra("param",item_param[0]);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return mData.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
{
public TextView myTextView, subtext;
public Button add;
public ViewHolder(View itemView)
{
super(itemView);
context = itemView.getContext();
myTextView = itemView.findViewById(R.id.tvAnimalName);
subtext = itemView.findViewById(R.id.subtext);
add = itemView.findViewById(R.id.add);
done = itemView.findViewById(R.id.done);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view)
{
if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
}
}
String getItem(int id) {return mData.get(id);
}
void setClickListener(ItemClickListener itemClickListener) {
this.mClickListener = itemClickListener;
}
public interface ItemClickListener
{
void onItemClick(View view, int position);
}
}
if(adapter.done.isChecked())should work I think, but I got null opbject reference exception.
Maybe the problem is that the checkbox is in the RecyclerView layout and not in Main class layout, but I am not sure.
Is it possible to handle the Checkbox methods from other class, and if yes, then where should I improve the code?

Java Fx table with checkbox row selection

I want to create a simple table with multiple selection using checkboxes on the first column.
I've created a custom "TableColumn" to handle this feature in every table I need. I've implemented all the features except one: when I select or deselect a single row the corresponding checkbox on the first colum must be selected or unselected. I need help to develop this last function.
public class ColumnSelectRow extends TableColumn{
private CheckBox headerCheckBox = new CheckBox();
public ColumnSelectRow(String title) {
super(title);
setSortable(false);
setPrefWidth(100);
setMaxWidth(100);
setMinWidth(100);
setResizable(false);
setGraphic(headerCheckBox);
setCellValueFactory( new Callback<TableColumn.CellDataFeatures<Object, Boolean>, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Object, Boolean> p) {
return new SimpleBooleanProperty(p.getValue() != null);
}
});
setCellFactory(new Callback<javafx.scene.control.TableColumn<Object, Boolean>, TableCell<Object, Boolean>>() {
#Override
public TableCell<Object, Boolean> call(javafx.scene.control.TableColumn<Object, Boolean> p) {
return new SelectButtonCell();
}
});
headerCheckBox.setOnAction(t->{
if(disableEvents) return;
TableView table = ColumnSelectRow.this.getTableView();
if (table == null) return;
if(headerCheckBox.isSelected()) table.getSelectionModel().selectAll();
else table.getSelectionModel().clearSelection();
});
}
public CheckBox getHeaderCheckBox() {
return headerCheckBox;
}
private boolean disableEvents = false;
private class SelectButtonCell extends TableCell {
private final CheckBox checkBox = new CheckBox();
public SelectButtonCell(){
setAlignment(Pos.CENTER);
checkBox.setOnAction(t -> {
int row = getTableRow().getIndex();
TableView table = SelectButtonCell.this.getTableView();
if (table == null) return;
if(checkBox.isSelected()) table.getSelectionModel().select(row);
else table.getSelectionModel().clearSelection(row);
table.getFocusModel().focus(row);
boolean empty = table.getSelectionModel().isEmpty();
boolean full = table.getSelectionModel().getSelectedCells().size()==table.getItems().size();
disableEvents = true;
headerCheckBox.setIndeterminate(false);
if(empty) headerCheckBox.setSelected(false);
else if(full) headerCheckBox.setSelected(true);
else headerCheckBox.setIndeterminate(true);
disableEvents = false;
});
}
public CheckBox getCheckBox() {
return checkBox;
}
#Override
protected void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if(!empty){
setGraphic(checkBox);
}
int row = getTableRow().getIndex();
TableView table = SelectButtonCell.this.getTableView();
checkBox.setSelected(table.getSelectionModel().isSelected(row));
}
public boolean isCheckBoxSelected(){
return checkBox.isSelected();
}
}
thanks!!
Edit: a custom table with the first column selectable
import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
public class MyTable extends Application {
private ColumnSelectRow columSelect;
#Override
public void start(Stage primaryStage) throws Exception {
columSelect = new ColumnSelectRow();
TableView T = new TableView<>();
T.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
ObservableList<myObject> al = FXCollections.observableArrayList();
al.add(new myObject(1));
al.add(new myObject(2));
al.add(new myObject(3));
T.setItems(al);
T.getColumns().add(columSelect);
TableColumn<myObject, Number> number = new TableColumn("Number");
number.setCellValueFactory(cellData->cellData.getValue().propetry);
T.getColumns().add(number);
Scene scene = new Scene(T);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private class myObject{
public SimpleIntegerProperty propetry = new SimpleIntegerProperty();
public myObject(int i) {
propetry.set(i);
}
}
}

JavaFX: compatibility issue between Java 7.4 and Java 8

I have an app that makes significant use of checkboxes in JavaFX TreeView and TableView. There is custom code as much of it was done before the enhancements that came later in JavaFX 2.2.
I now find that checkboxes that used to work do not work (as if they are disabled) although some work intermittently.
I have checked through the Oracle compatibility documentation and I can find nothing relevant.
I have a small sample app that works perfectly in Java 7.4 but shows the same faulty behaviour as the main app in Java 8.
Could anyone you please suggest where I might start looking from your own experience - e.g. cell factory, callback etc - or indicate if anything fundamental changed with this kind of construct at Java 8? I have posted the sample app code below (four separate classes).
Thank you in advance.
package samplefx2_original_v7;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;
public class SampleFX2_Original_V7 extends Application {
private TableView table = new TableView();
private boolean everything = false;
//Sample data for the table.
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new ControlPerson(false, "Select Columns", false, false),
new Person(true, "Jacob Smith", true, false),
new Person(true, "Isabella Johnson", true, true),
new Person(true, "Ethan Williams", false, false),
new Person(true, "Emma Jones", false, true),
new Person(false, "Michael Brown", true, true));
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(850);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
//A custom cell factory that creates checkboxes for a boolean property.
Callback<TableColumn, TableCell> colCheckFactory = new Callback<TableColumn, TableCell>() {
#Override
public TableCell call(TableColumn p) {
return new CheckBoxCell();
}
};
//The various columns
TableColumn nameCol = new TableColumn("Name");
nameCol.setMinWidth(100);
nameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("name"));
TableColumn contactCol = createContactColumn(colCheckFactory);
TableColumn emailCol = createEmailColumn(colCheckFactory);
TableColumn phoneCol = createPhoneColumn(colCheckFactory);
//Add the columns and data to the table.
table.setItems(data);
table.getColumns().addAll(nameCol, contactCol, emailCol, phoneCol);
//Make the table editable
table.setEditable(true);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.getChildren().addAll(label, table);
vbox.setPadding(new Insets(10, 0, 0, 10));
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
private TableColumn createEmailColumn(Callback<TableColumn, TableCell> cellFactory) {
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(75);
emailCol.setCellValueFactory(new PropertyValueFactory("email"));
emailCol.setCellFactory(cellFactory);
emailCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, Boolean>>() {
#Override
public void handle(CellEditEvent<Person, Boolean> event) {
if (event.getRowValue() instanceof ControlPerson) {
for (Person p : data) {
p.setEmail(event.getNewValue());
}
} else {
//Need to handle the indivdual cells as well as the special control cells.
Person p = event.getRowValue();
p.setEmail( event.getNewValue() );
}
}
});
return emailCol;
}
private TableColumn createPhoneColumn(Callback<TableColumn, TableCell> cellFactory) {
TableColumn phoneCol = new TableColumn("Phone");
phoneCol.setMinWidth(75);
phoneCol.setCellValueFactory( new PropertyValueFactory("phone"));
phoneCol.setCellFactory(cellFactory);
phoneCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, Boolean>>() {
#Override
public void handle(CellEditEvent<Person, Boolean> event) {
if (event.getRowValue() instanceof ControlPerson) {
for (Person p : data) {
p.setPhone(event.getNewValue());
}
} else {
Person p = event.getRowValue();
p.setPhone( event.getNewValue() );
}
}
});
return phoneCol;
}
/**
* This is the main control column in your application (containing the green and red circles).
*
* #param cellFactory
* #return
*/
private TableColumn createContactColumn( Callback<TableColumn, TableCell> cellFactory ) {
TableColumn contactCol = new TableColumn("Contact");
contactCol.setMinWidth(75);
contactCol.setCellValueFactory( new PropertyValueFactory( "contact"));
contactCol.setCellFactory(cellFactory);
contactCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, Boolean>>() {
#Override
public void handle(CellEditEvent<Person, Boolean> event) {
//This handler is different to the other two as it controls the checking/unchecking of both
//the whole table and individual rows.
if (event.getRowValue() instanceof ControlPerson) {
for (Person p : data) {
p.setContact(event.getNewValue());
p.setEmail(event.getNewValue());
p.setPhone(event.getNewValue());
}
//This is just an example of how you would control a special "everything" flag.
//You could call any method or take any action here to deal with the special
//case where everything is selected.
everything = event.getNewValue();
} else {
//Set the state of any boolean properties to modify the whole row.
Person p = event.getRowValue();
p.setContact( event.getNewValue() );
p.setEmail(event.getNewValue());
p.setPhone(event.getNewValue());
}
}
});
return contactCol;
}
}
package samplefx2_original_v7;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
public class Person {
private final SimpleBooleanProperty contact;
private final SimpleStringProperty name;
private final SimpleBooleanProperty email;
private final SimpleBooleanProperty phone;
public Person(boolean contact, String name, boolean email, boolean phone) {
this.contact = new SimpleBooleanProperty( contact );
this.name = new SimpleStringProperty(name);
this.email = new SimpleBooleanProperty( email );
this.phone = new SimpleBooleanProperty( phone );
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
public SimpleStringProperty nameProperty() {
return name;
}
public boolean isContact() {
return contact.get();
}
public void setContact( boolean contact ) {
this.contact.set( contact );
}
public SimpleBooleanProperty contactProperty() {
return contact;
}
public boolean isEmail() {
return email.get();
}
public void setEmail( boolean email ) {
this.email.set( email );
}
public SimpleBooleanProperty emailProperty() {
return email;
}
public boolean isPhone() {
return contact.get();
}
public void setPhone( boolean phone ) {
this.phone.set( phone );
}
public SimpleBooleanProperty phoneProperty() {
return phone;
}
}
package samplefx2_original_v7;
/**
* This subclass of Person is used only to identify the row(s) that is used to control
* the checking and unchecking of columns.
*/
public class ControlPerson extends Person {
public ControlPerson(boolean active, String name, boolean email, boolean phone) {
super(active, name, email, phone);
}
}
package samplefx2_original_v7;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
public class CheckBoxCell extends TableCell<Person, Boolean> {
private CheckBox checkBox;
public CheckBoxCell() {
if (checkBox == null) {
checkBox = new CheckBox();
checkBox.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
CheckBox cb = (CheckBox) event.getSource();
getTableView().edit(getTableRow().getIndex(), getTableColumn());
commitEdit(cb.isSelected());
}
});
}
setGraphic(checkBox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
//Center align the checkboxes.
setAlignment(Pos.CENTER);
}
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (item == null) {
//If we don't have an item don't draw the checkbox.
setGraphic( null );
// checkBox.setDisable(true);
// checkBox.setSelected(false);
} else {
checkBox.setDisable(false);
checkBox.setSelected(item);
}
}
}
A couple of things are going on here. A simple thing to note is that you don't set the graphic when the cell changes from empty to non-empty. You need:
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (item == null) {
//If we don't have an item don't draw the checkbox.
setGraphic( null );
// checkBox.setDisable(true);
// checkBox.setSelected(false);
} else {
checkBox.setDisable(false);
checkBox.setSelected(item);
setGraphic(checkBox);
}
}
But probably more of an issue is that your table cell implementation really relies on undocumented behavior, in that it forces the table into editing mode (getTableView().edit(...)) and (as far as I can tell) seems to rely on some side effects of this in order to get the functionality you want. Because the behavior is undocumented, there's no guarantee it will remain the same in new releases, so I'm not too surprised it breaks under Java 8.
A better way to implement the cells is just to use listeners (or bindings) to exchange data between the check box's selectedProperty and the property in the Person class. Because the display is always the same (i.e. a check box) no matter whether the cell is being edited or not, there's no really need to work with the editing API from the cell class. This is the approach the standard CheckBoxTableCell introduced in JavaFX 2.2 works.
Assuming the functionality you want is that the "contact" column check boxes should select both the "email" and "phone" checkboxes, I would approach this slightly differently. There's really no logical need to have a contact property in the model class (Person), because that information is already incorporated in the other two boolean properties. (Another way to think of this is that the "control" checkboxes are just UI components and not really part of the data, so they have no place in the model.) Your "contact" column doesn't really have a property associated with it; just make that a TableColumn<Person, Person> and have its cells update the other properties appropriately.
Also, your ControlPerson is a bit of a hack. Again this row in the table is really just for user interface purposes and is not part of the data. So it really shouldn't appear as part of the table's items. I would put the "select all" check boxes in the table header, not in the data part of the table. This will change their appearance some (though you can always fix that with CSS if you need) but the benefit is that they remain visible when you scroll, which is probably desirable.
Here's a complete example which works this way, and runs under both Java 7 (JavaFX 2.2) and under Java 8. (I tested under 1.7.0_65, 1.8.0_25, and 1.8.0_40.)
import java.util.Arrays;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class SampleFX2 extends Application {
#Override
public void start(Stage primaryStage) {
ObservableList<Person> data = FXCollections.observableArrayList(
new Callback<Person, Observable[]>() {
#Override
public Observable[] call(Person person) {
return new Observable[] {person.emailProperty(), person.phoneProperty()};
}
});
data.addAll(Arrays.asList(
new Person("Jacob Smith", true, false),
new Person("Isabella Johnson", true, true),
new Person("Ethan Williams", false, false),
new Person("Emma Jones", false, true),
new Person("Michael Brown", true, true)));
final TableView<Person> table = new TableView<>();
table.setItems(data);
TableColumn<Person, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("name"));
final CheckBox allContactCheckBox = new CheckBox() ;
final CheckBox allEmailCheckBox = new CheckBox();
final CheckBox allPhoneCheckBox = new CheckBox();
updateHeaderCheckBoxes(table, allContactCheckBox, allEmailCheckBox, allPhoneCheckBox);
data.addListener(new ListChangeListener<Person>() {
#Override
public void onChanged(Change<? extends Person> change) {
updateHeaderCheckBoxes(table, allContactCheckBox, allEmailCheckBox, allPhoneCheckBox);
}
});
allPhoneCheckBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean wasSelected, Boolean isSelected) {
for (Person person : table.getItems()) {
person.setPhone(isSelected);
}
}
});
allEmailCheckBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean wasSelected, Boolean isSelected) {
for (Person person : table.getItems()) {
person.setEmail(isSelected);
}
}
});
allContactCheckBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean wasSelected, Boolean isSelected) {
for (Person person : table.getItems()) {
person.setPhone(isSelected);
person.setEmail(isSelected);
}
}
});
TableColumn<Person, Person> contactCol = new TableColumn<>();
contactCol.setPrefWidth(75);
contactCol.setGraphic(createTableHeader("Contact", allContactCheckBox));
contactCol.setCellValueFactory(new Callback<CellDataFeatures<Person, Person>, ObservableValue<Person>>() {
#Override
public ObservableValue<Person> call(
CellDataFeatures<Person, Person> person) {
return new ReadOnlyObjectWrapper<>(person.getValue());
}
});
contactCol.setCellFactory(new Callback<TableColumn<Person, Person>, TableCell<Person, Person>>() {
#Override
public TableCell<Person, Person> call(
TableColumn<Person, Person> param) {
return new ContactCell();
}
});
TableColumn<Person, Boolean> emailCol = new TableColumn<>();
emailCol.setPrefWidth(75);
emailCol.setGraphic(createTableHeader("Email", allEmailCheckBox));
emailCol.setCellValueFactory(new PropertyValueFactory<Person, Boolean>("email"));
emailCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
#Override
public TableCell<Person, Boolean> call(
TableColumn<Person, Boolean> column) {
return new CheckBoxCell<Person>(new Callback<Person, BooleanProperty>() {
#Override
public BooleanProperty call(Person person) {
return person.emailProperty();
}
});
}
});
TableColumn<Person, Boolean> phoneCol = new TableColumn<>();
phoneCol.setPrefWidth(75);
phoneCol.setGraphic(createTableHeader("Phone", allPhoneCheckBox));
phoneCol.setCellValueFactory(new PropertyValueFactory<Person, Boolean>("phone"));
phoneCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
#Override
public TableCell<Person, Boolean> call(
TableColumn<Person, Boolean> column) {
return new CheckBoxCell<Person>(new Callback<Person, BooleanProperty>() {
#Override
public BooleanProperty call(Person person) {
return person.phoneProperty();
}
});
}
});
table.getColumns().add(nameCol);
table.getColumns().add(contactCol);
table.getColumns().add(emailCol);
table.getColumns().add(phoneCol);
Button showButton = new Button("Debug");
showButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent evt) {
for (Person p : table.getItems()) {
System.out.println(p.getName() + " " + p.isEmail() +" "+ p.isPhone());
}
System.out.println();
}
});
HBox controls = new HBox(5);
controls.setAlignment(Pos.CENTER);
controls.setPadding(new Insets(10));
controls.getChildren().add(showButton);
BorderPane root = new BorderPane();
root.setCenter(table);
root.setBottom(controls);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private Node createTableHeader(String text, CheckBox checkBox) {
VBox header = new VBox(2);
header.setPadding(new Insets(2));
header.getChildren().addAll(new Label(text), checkBox);
header.setAlignment(Pos.CENTER);
return header ;
}
private static void updateHeaderCheckBoxes(final TableView<Person> table,
final CheckBox allContactCheckBox, final CheckBox allEmailCheckBox,
final CheckBox allPhoneCheckBox) {
boolean allPhoneSelected = true ;
boolean noPhoneSelected = true ;
boolean allEmailSelected = true ;
boolean noEmailSelected = true ;
for (Person person : table.getItems()) {
if (person.isEmail()) {
noEmailSelected = false ;
} else {
allEmailSelected = false ;
}
if (person.isPhone()) {
noPhoneSelected = false ;
} else {
allPhoneSelected = false ;
}
}
setCheckBoxState(allPhoneSelected, noPhoneSelected, allPhoneCheckBox);
setCheckBoxState(allEmailSelected, noEmailSelected, allEmailCheckBox);
setCheckBoxState(allPhoneSelected && allEmailSelected, noPhoneSelected && noEmailSelected, allContactCheckBox);
}
private static void setCheckBoxState(boolean on, boolean off, CheckBox checkBox) {
if (on) {
checkBox.setIndeterminate(false);
checkBox.setSelected(true);
} else if (off) {
checkBox.setIndeterminate(false);
checkBox.setSelected(false);
} else {
checkBox.setIndeterminate(true);
}
}
public static class ContactCell extends TableCell<Person, Person> {
private final CheckBox checkBox ;
public ContactCell() {
checkBox = new CheckBox();
final ChangeListener<Boolean> propertyListener = new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean oldValue, Boolean newValue) {
updateCheckBox();
}
};
checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean oldValue, Boolean newValue) {
Person person = getItem();
person.setEmail(newValue);
person.setPhone(newValue);
}
});
itemProperty().addListener(new ChangeListener<Person>() {
#Override
public void changed(
ObservableValue<? extends Person> observable,
Person oldValue, Person newValue) {
if (oldValue != null) {
oldValue.emailProperty().removeListener(propertyListener);
oldValue.phoneProperty().removeListener(propertyListener);
}
if (newValue != null) {
newValue.emailProperty().addListener(propertyListener);
newValue.phoneProperty().addListener(propertyListener);
updateCheckBox();
}
}
});
emptyProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean wasEmpty, Boolean isEmpty) {
if (isEmpty) {
setGraphic(null);
} else {
setGraphic(checkBox);
}
}
});
setAlignment(Pos.CENTER);
}
private void updateCheckBox() {
Person person = getItem();
if (person != null) {
setCheckBoxState(person.isEmail() && person.isPhone(), ! person.isEmail() && ! person.isPhone(), checkBox);
if (person.isEmail() && person.isPhone()) {
checkBox.setSelected(true);
} else if (! person.isEmail() && ! person.isPhone()) {
checkBox.setSelected(false);
} else {
checkBox.setIndeterminate(true);
}
checkBox.setIndeterminate(person.isEmail() != person.isPhone());
}
}
}
public static class CheckBoxCell<T> extends TableCell<T, Boolean> {
private final CheckBox checkBox ;
public CheckBoxCell(final Callback<T, BooleanProperty> propertyMapper) {
checkBox = new CheckBox();
checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(
ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
#SuppressWarnings("unchecked") // for some reason getTableRow() returns a raw type...
TableRow<T> row = getTableRow();
if (row != null) {
T item = row.getItem();
if (item != null) {
BooleanProperty property = propertyMapper.call(item);
property.set(newValue);
}
}
}
});
itemProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(
ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
if (newValue != null) {
checkBox.setSelected(newValue);
}
}
});
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setAlignment(Pos.CENTER);
emptyProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(
ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
if (newValue) {
setGraphic(null);
} else {
setGraphic(checkBox);
}
}
});
}
}
public static class Person {
private final StringProperty name;
private final BooleanProperty email;
private final BooleanProperty phone;
public Person(String name, boolean email, boolean phone) {
this.name = new SimpleStringProperty(name);
this.email = new SimpleBooleanProperty( email );
this.phone = new SimpleBooleanProperty( phone );
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
public StringProperty nameProperty() {
return name;
}
public boolean isEmail() {
return email.get();
}
public void setEmail( boolean email ) {
this.email.set( email );
}
public BooleanProperty emailProperty() {
return email;
}
public boolean isPhone() {
return phone.get();
}
public void setPhone( boolean phone ) {
this.phone.set( phone );
}
public BooleanProperty phoneProperty() {
return phone;
}
}
public static void main(String[] args) {
launch(args);
}
}

Code is working in jdk7 not in jdk8

I took the sample code from How to create custom components in JavaFX 2.0 using FXML?. the code working fine in jdk7 but the checkbox and combo box values are not working in jdk8. I am not able to click the check box even and select the dropdown.
Please guide me for the same.
Please find the sample code below
Package structure
com.example.javafx.choice
ChoiceCell.java
ChoiceController.java
ChoiceModel.java
ChoiceView.fxml
com.example.javafx.mvc
FxmlMvcPatternDemo.java
MainController.java
MainView.fxml
MainView.properties
ChoiceCell.java
package com.example.javafx.choice;
import java.io.IOException;
import java.net.URL;
import javafx.fxml.FXMLLoader;
import javafx.fxml.JavaFXBuilderFactory;
import javafx.scene.Node;
import javafx.scene.control.ListCell;
public class ChoiceCell extends ListCell<ChoiceModel>
{
#Override
protected void updateItem(ChoiceModel model, boolean bln)
{
super.updateItem(model, bln);
if(model != null)
{
URL location = ChoiceController.class.getResource("ChoiceView.fxml");
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(location);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
try
{
Node root = (Node)fxmlLoader.load(location.openStream());
ChoiceController controller = (ChoiceController)fxmlLoader.getController();
controller.setModel(model);
setGraphic(root);
}
catch(IOException ioe)
{
throw new IllegalStateException(ioe);
}
}
}
}
ChoiceController.java
package com.example.javafx.choice;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
public class ChoiceController
{
private final ChangeListener<String> LABEL_CHANGE_LISTENER = new ChangeListener<String>()
{
public void changed(ObservableValue<? extends String> property, String oldValue, String newValue)
{
updateLabelView(newValue);
}
};
private final ChangeListener<Boolean> IS_SELECTED_CHANGE_LISTENER = new ChangeListener<Boolean>()
{
public void changed(ObservableValue<? extends Boolean> property, Boolean oldValue, Boolean newValue)
{
updateIsSelectedView(newValue);
}
};
private final ChangeListener<Boolean> IS_VISIBILITY1_CHANGE_LISTENER = new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> property, Boolean oldValue, Boolean newValue) {
updateIsVisibleView1(newValue);
}};
#FXML
private Label labelView;
#FXML
private CheckBox isSelectedView;
#FXML
ComboBox isVisible1;
private ChoiceModel model;
public ChoiceModel getModel()
{
return model;
}
public void setModel(ChoiceModel model)
{
if(this.model != null)
removeModelListeners();
this.model = model;
setupModelListeners();
updateView();
}
private void removeModelListeners()
{
model.labelProperty().removeListener(LABEL_CHANGE_LISTENER);
model.isSelectedProperty().removeListener(IS_SELECTED_CHANGE_LISTENER);
model.isVisibleProperty1().removeListener(IS_VISIBILITY1_CHANGE_LISTENER);
isSelectedView.selectedProperty().unbindBidirectional(model.isSelectedProperty());
}
private void setupModelListeners()
{
model.labelProperty().addListener(LABEL_CHANGE_LISTENER);
model.isSelectedProperty().addListener(IS_SELECTED_CHANGE_LISTENER);
model.isVisibleProperty1().addListener(IS_VISIBILITY1_CHANGE_LISTENER);
isSelectedView.selectedProperty().bindBidirectional(model.isSelectedProperty());
}
private void updateView()
{
updateLabelView();
updateIsSelectedView();
updateIsVisibleView1();
}
private void updateLabelView(){ updateLabelView(model.getLabel()); }
private void updateLabelView(String newValue)
{
labelView.setText(newValue);
}
private void updateIsSelectedView(){ updateIsSelectedView(model.isSelected()); }
private void updateIsSelectedView(boolean newValue)
{
isSelectedView.setSelected(newValue);
}
private void updateIsVisibleView1() {
updateIsVisibleView1(model.isVisible1());
}
private void updateIsVisibleView1(boolean newValue) {
isVisible1.setVisible(newValue);
}
}
ChoiceModel.java
package com.example.javafx.choice;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class ChoiceModel
{
private final StringProperty label;
private final BooleanProperty isSelected;
private final BooleanProperty isVisible1;
public ChoiceModel(String label, boolean isSelected, boolean isVisible1)
{
this.label = new SimpleStringProperty(label);
this.isSelected = new SimpleBooleanProperty(isSelected);
this.isVisible1 = new SimpleBooleanProperty(isVisible1);
}
public String getLabel(){ return label.get(); }
public void setLabel(String label){ this.label.set(label); }
public StringProperty labelProperty(){ return label; }
public boolean isSelected(){ return isSelected.get(); }
public void setSelected(boolean isSelected){ this.isSelected.set(isSelected); }
public BooleanProperty isSelectedProperty(){ return isSelected; }
public boolean isVisible1()
{
return isVisible1.get();
}
public void setVisible1(boolean isVisible1)
{
this.isVisible1.set(isVisible1);
}
public BooleanProperty isVisibleProperty1()
{
return isVisible1;
}
}
ChoiceView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.collections.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<HBox xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="com.example.javafx.choice.ChoiceController">
<children>
<CheckBox fx:id="isSelectedView" />
<Label fx:id="labelView" />
<ComboBox id="isVisible1" fx:id="isVisible1" value="last 7 digits" visible="false">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="last 10 digits" />
<String fx:value="last 9 digits" />
<String fx:value="last 8 digits" />
<String fx:value="last 7 digits" />
<String fx:value="last 6 digits" />
<String fx:value="last 5 digits" />
<String fx:value="last 4 digits" />
<String fx:value="last 3 digits" />
</FXCollections>
</items>
</ComboBox>
</children>
</HBox>
FxmlMvcPatternDemo.java
package com.example.javafx.mvc;
import com.example.javafx.choice.ChoiceController;
import java.util.ResourceBundle;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class FxmlMvcPatternDemo extends Application
{
public static void main(String[] args) throws ClassNotFoundException
{
Application.launch(FxmlMvcPatternDemo.class, args);
}
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load
(
FxmlMvcPatternDemo.class.getResource("MainView.fxml"),
ResourceBundle.getBundle(FxmlMvcPatternDemo.class.getPackage().getName()+".MainView")/*properties file*/
);
ChoiceController controller = new ChoiceController();
stage.setScene(new Scene(root));
stage.show();
}
}
MainController.java
package com.example.javafx.mvc;
import com.example.javafx.choice.ChoiceCell;
import com.example.javafx.choice.ChoiceModel;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.util.Callback;
public class MainController implements Initializable
{
#FXML
private ListView<ChoiceModel> choicesView;
#Override
public void initialize(URL url, ResourceBundle rb)
{
choicesView.setCellFactory(new Callback<ListView<ChoiceModel>, ListCell<ChoiceModel>>()
{
public ListCell<ChoiceModel> call(ListView<ChoiceModel> p)
{
return new ChoiceCell();
}
});
choicesView.setItems(FXCollections.observableArrayList
(
new ChoiceModel("Tiger", true, false),
new ChoiceModel("Shark", false, true),
new ChoiceModel("Bear", false, true),
new ChoiceModel("Wolf", true, true)
));
}
#FXML
private void handleForceChange(ActionEvent event)
{
if(choicesView != null && choicesView.getItems().size() > 0)
{
boolean isSelected = choicesView.getItems().get(0).isSelected();
choicesView.getItems().get(0).setSelected(!isSelected);
}
}
}
MainView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox
xmlns:fx="http://javafx.com/fxml"
fx:controller="com.example.javafx.mvc.MainController"
prefWidth="300"
prefHeight="400"
fillWidth="false"
>
<children>
<Label text="%title" />
<ListView fx:id="choicesView" />
<Button text="Force Change" onAction="#handleForceChange" />
</children>
</VBox>
MainView.proprties
title=JavaFX 2.0 FXML MVC demo
I didn't dig deeper but refactoring like as following should suffice. Double check for correct executions of attached and removed listeners.
public class ChoiceCell extends ListCell<ChoiceModel> {
private Node root;
private ChoiceController controller;
public ChoiceCell() {
URL location = ChoiceController.class.getResource("ChoiceView.fxml");
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(location);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
try {
root = (Node) fxmlLoader.load(location.openStream());
controller = (ChoiceController) fxmlLoader.getController();
} catch (IOException ioe) {
throw new IllegalStateException(ioe);
}
}
#Override
protected void updateItem(ChoiceModel model, boolean bln) {
super.updateItem(model, bln);
if (model != null) {
controller.setModel(model);
setGraphic(root);
} else {
setGraphic(null);
}
}
}

JAVAFX editable ComboBox: refresh after changing a value

I'm still new to JavaFX and need to create a combobox with objects (SimlpePerson) and not strings. I want to edit the shown value in the box itself. Works good for strings but I have problems with SimpleObjects. I made a StringConverter and it also works, I can edit the object shown in the comboBox. But the list itself is not rerendered after that. If I click on the ComboBox I see the original values. How can I change that?
Any suggestion is very welcome!=)
BR and Thank you!
Stefan
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class ComboBoxDemo extends Application{
public class SimplePerson {
private StringProperty name;
private String somethingElse;
public SimplePerson(String name) {
setName(name);
}
public final void setName(String value) { nameProperty().set(value); }
public String getName() { return nameProperty().get(); }
public StringProperty nameProperty() {
if (name == null) name = new SimpleStringProperty(this, "name");
return name;
}
}
final ObservableList<SimplePerson> persons = FXCollections.observableArrayList(
new SimplePerson("Jacob"),
new SimplePerson("Isabella"),
new SimplePerson("Ethan"),
new SimplePerson("Emma"),
new SimplePerson("Michael")
);
#Override
public void start(Stage stage) throws Exception {
// TODO Auto-generated method stub
final ComboBox cb = new ComboBox();
cb.setItems(persons);
cb.setEditable(true);
cb.setConverter(new StringConverter<SimplePerson>() {
#Override
public String toString(SimplePerson p)
{
if(p != null)
return p.getName();
return "";
}
#Override
public SimplePerson fromString(String name)
{
if(cb.getValue() != null)
{
((SimplePerson)cb.getValue()).setName(name);
cb.show();
return (SimplePerson)cb.getValue();
}
return null;
}
});
stage.setScene(new Scene(cb));
stage.show();
}
public static void main(String[] args) { launch(args); }
}
Check out this solution. There is a handler which is triggered when you've finished editing. There you may implement all the code which changes the model's state.
To update the combobox list the following approach work may:
cb.getEditor().setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
SimplePerson person = cb.getValue();
if (null != person) {
int idx = persons.indexOf(person);
person.setName(cb.getEditor().getText());
persons.set(idx, person);
cb.setValue(person);
}
}
});

Resources