I am a problem with javafx. I have GridPane with about 40 CheckBoxes. I need make user can select only one CheckBox and selected CheckBox is saved to a variable.
This is method for work with checkboxes:
public static class createBet {
public static CheckBox bet;
public static CheckBox isBet(CheckBox[] group, int finalI, AnchorPane resultBlock) {
for (CheckBox j : group) {
if (j.equals(bet)) {
j.setSelected(false);
}
}
ObservableList resultLabels = resultBlock.getChildren();
Label label_num = (Label)resultLabels.get(0);
Label label_win = (Label)resultLabels.get(1);
if (group[finalI].isSelected()) {
bet = group[finalI];
resultBlock.setStyle("-fx-border-color: black;");
label_num.setStyle("-fx-text-fill: black");
label_win.setStyle("-fx-text-fill: black");
}
else {
bet = null;
resultBlock.setStyle("-fx-border-color: gray");
label_num.setStyle("-fx-text-fill: gray");
label_win.setStyle("-fx-text-fill: gray");
}
return bet;
}
}
This is a class for final variables:
public class bets {
public CheckBox numberBet = null;
public CheckBox colorBet = null;
public CheckBox evenBet = null;
}
There I use this classes:
//(block with variables and links to FXML)
createBet bet = new createBet();
bets bets = new bets();
for (int i = 0; i < numbersGroup.length; i++) {
int finalI = i;
numbersGroup[i].selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
bets.numberBet = createBet.isBet(numbersGroup, finalI, resultNum);
}
});
}
}
}
Checkboxes are selected correctly, but variables for selected CheckBox (class bets) always equal null.
UPD. I comment class createBet and add code from it to the public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue). It not change the situation - variable bets.numberBet is changing in the listener only, outside it this variable allways equals null.
I want use numberBet in the whole code, it's main problem.
My solution for this problem. I DON'T set varbable numberBet with method isBet. isBetwork for change CheckBoxes and activation for block resultNum only.
Variables i set in inner action, where thei are used.
new version of class:
public class Bet {
private CheckBox bet;
public void forBet(CheckBox[] group, int finalI, AnchorPane resultBlock) {
for (CheckBox j : group) {
if (j.equals(bet)) {
j.setSelected(false);
}
}
ObservableList resultLabels = resultBlock.getChildren();
Label label_num = (Label)resultLabels.get(0);
Label label_win = (Label)resultLabels.get(1);
if (group[finalI].isSelected()) {
bet = group[finalI];
resultBlock.setStyle("-fx-border-color: black;");
label_num.setStyle("-fx-text-fill: black");
label_win.setStyle("-fx-text-fill: black");
}
else {
bet = null;
resultBlock.setStyle("-fx-border-color: gray");
label_num.setStyle("-fx-text-fill: gray");
label_win.setStyle("-fx-text-fill: gray");
}
}
public CheckBox startBet(CheckBox[] group) {
for (CheckBox i : group){
if (i.isSelected()) {
bet = i;
break;
}
}
return bet;
}
}
variables:
public CheckBox numberBet = null;
public CheckBox colorBet = null;
public CheckBox evenBet = null;
Using for methods:
Bet Bet = new Bet();
for (int i = 0; i < numbersGroup.length; i++) {
int finalI = i;
numbersGroup[i].selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
Bet.forBet(numbersGroup, finalI,resultNum);
}
});
}
btn_start.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
numberBet = Bet.startBet(numbersGroup);
System.out.println(numberBet);
}
});
I think, it's resolve my issue.
Related
I want to create three(3) sets of Combobox (Year, Month, Day).
The Combobox Day should only be enabled until the Combobox Month and Year were filed correctly, and values should be synchronized based on the given month and year. (This means that it should check for leap years).
Here is what I have so far, I have a hint that I should use bindings and/or listeners to do this but struggle to do so.
public class Testing extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
final JFXComboBox<Month> cbMonths = new JFXComboBox<>();
final JFXComboBox<Integer> cbYears = new JFXComboBox<>();
final JFXComboBox<Integer> cbDays = new JFXComboBox<>();
// Month Values
cbMonths.getItems().setAll(Month.values());
// Year Values
Calendar calendar = Calendar.getInstance();
for (int i = calendar.get(Calendar.YEAR) ;
i >= (calendar.get(Calendar.YEAR) -35) ; i--)
{
cbYears.getItems().add(i);
}
// NOTE: will cause NPE
// I want to insert this code only when cbMonth and cbYears has a value
YearMonth numberOfDays = YearMonth.of(cbYear.getValue(), cbMonth.getValue());
for (int i = 1 ; i >= numberOfDays.lengthOfMonth() ; i ++) {
cbDays.getItems().add(i);
}
final HBox root = new HBox(cbMonth, cbYear, cbDays);
root.setAlignment(Pos.CENTER);
root.setSpacing(10.0);
root.setPadding(new Insets(10, 10, 10, 10));
Scene scene = new Scene(root, 300, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
EDIT
Having a lack of time, I tried other options.
OPTION 1:
As #Zephyr points out, I switch to a date picker and set it to editable. I tried to override some of its default settings to come up with my desire output. But I notice that whenever I use TextFormatter I was unable to pick dates on the DatePicker choice box. Here is the sample code
public class DatePickerFinal extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
final String DATE_REGEX = "(0[1-9]|1[012])\\s(0[1-9]|[12][0-9]|3[01])\\s((19|2[0-9])[0-9]{2})";
final DateTimeFormatter SHOW_DATE = DateTimeFormatter.ofPattern("MMMM dd, yyyy", Locale.getDefault());
final DateTimeFormatter ENTER_DATE = DateTimeFormatter.ofPattern("MM dd yyyy", Locale.getDefault());
final LocalDate TODAY = LocalDate.now();
final JFXDatePicker DATE_PICKER = new JFXDatePicker();
// Disable some dates
DATE_PICKER.setDayCellFactory(new Callback<DatePicker, DateCell>() {
#Override
public DateCell call(DatePicker datePicker) {
return new DateCell() {
#Override
public void updateItem(LocalDate localDate, boolean b) {
super.updateItem(localDate, b);
setDisable(b || localDate.compareTo(TODAY) > 0 || localDate.compareTo(TODAY.minusYears(45)) < 0);
}
};
}
});
// Add StringConverter to make it more readable,
// and also rejecting disable dates inputted by the user
DATE_PICKER.setConverter(new StringConverter<LocalDate>() {
#Override
public String toString(LocalDate localDate) {
if (localDate == null) {
return "";
} else if (localDate.isAfter(TODAY) || localDate.isBefore(TODAY.minusYears(45))) {
return "";
} else {
return SHOW_DATE.format(localDate);
}
}
#Override
public LocalDate fromString(String s) {
return (s == null || s.isEmpty()) ? null : LocalDate.parse(s, ENTER_DATE);
}
});
// Then I want to manage user input so that they can only enter digits to the date picker
// then format it accordingly.
DATE_PICKER.getEditor().setTextFormatter(new TextFormatter<Object>(change -> {
String enteredText = change.getText();
if((enteredText.matches("[\\d]+")) || change.isDeleted()) {
final int oldTextLength = change.getControlText().length();
int newTextLength = change.getControlNewText().length();
if (newTextLength < oldTextLength) return change;
switch (newTextLength) {
case 2 :
case 5 :
StringBuilder stringBuilder = new StringBuilder(enteredText);
stringBuilder.append(" ");
change.setText(stringBuilder.toString());
newTextLength++;
break;
case 11 :
return null;
}
change.setCaretPosition(newTextLength);
change.setAnchor(newTextLength);
return change;
}
return null;
}));
// Add some validators where if the user input was valid or not. The below code was still in progress though.
RequiredFieldValidator requiredFieldValidator = new RequiredFieldValidator();
requiredFieldValidator.setMessage("Field Should Not Be Empty");
RegexValidator regexValidator = new RegexValidator("MM DD YYYY");
regexValidator.setRegexPattern(DATE_REGEX);
DATE_PICKER.setValidators(regexValidator);
DATE_PICKER.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observableValue, Boolean aBoolean, Boolean t1) {
if (t1) {
DATE_PICKER.validate();
}
}
});
DATE_PICKER.getEditor().textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observableValue, String s, String t1) {
if (!DATE_PICKER.getEditor().getText().matches(DATE_REGEX)) {
DATE_PICKER.validate();
}
}
});
VBox root = new VBox(20, DATE_PICKER, new JFXButton("Button"));
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 300, 120);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Aside from being editable, I also want the user to be able to just click and/or pick dates from the choice box. I hope someone could point me in the right direction :)
With #kleopatra's help. My solution is to create a class responsible for parsing the date selected by the user on the DatePickers default choice box. Furthermore, the date picker is set to editable so that the user can also edit it manually. However, there is a restriction where a user can ONLY insert numerical value when editing manually, also I wanted to make sure that the user should input only valid dates.
MCVE
public class DatePickerFinal extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
final String DATE_OF_BIRTH_REGEX
= "(0[1-9]|1[012])\\s(0[1-9]|[12][0-9]|3[01])\\s((19|2[0-9])[0-9]{2})";
final DateTimeFormatter showingDateFormat = DateTimeFormatter.ofPattern("MMMM dd, yyyy", Locale.getDefault());
final DateTimeFormatter inputtedDateFormat = DateTimeFormatter.ofPattern("MM dd yyyy", Locale.getDefault());
final LocalDate dateToday = LocalDate.now();
final JFXDatePicker datePicker = new JFXDatePicker();
// Disable some dates
datePicker.setDayCellFactory(new Callback<DatePicker, DateCell>() {
#Override
public DateCell call(DatePicker datePicker) {
return new DateCell() {
#Override
public void updateItem(LocalDate localDate, boolean b) {
super.updateItem(localDate, b);
setDisable(b || localDate.compareTo(dateToday) > 0 || localDate.compareTo(dateToday.minusYears(45)) < 0);
}
};
}
});
// Add StringConverter to make it more readable,
// and also rejecting disable dates inputted by the user
datePicker.setConverter(new StringConverter<LocalDate>() {
#Override
public String toString(LocalDate localDate) {
if (localDate == null) {
return "";
} else if (localDate.isAfter(dateToday) || localDate.isBefore(dateToday.minusYears(45))) {
return "";
} else {
return showingDateFormat.format(localDate);
}
}
#Override
public LocalDate fromString(String s) {
return (s == null || s.isEmpty()) ? null : LocalDate.parse(s, inputtedDateFormat);
}
});
// Add a validator
RequiredFieldValidator requiredFieldValidator = new RequiredFieldValidator();
requiredFieldValidator.setMessage("Enter with the format\nMM DD YYYY");
datePicker.setValidators(requiredFieldValidator);
// Format the user's input field
datePicker.getEditor().setTextFormatter(new TextFormatter<>(change -> {
String textEntered = change.getText();
DateValidator validator;
if (change.isContentChange()) {
validator = new DateValidator(change.getControlNewText(), showingDateFormat);
if (!validator.isValid()) {
datePicker.validate();
} else {
datePicker.resetValidation();
return change;
}
if (textEntered.matches("\\D+")) {
return null;
} else {
final int oldLength = change.getControlText().length();
int newLength = change.getControlNewText().length();
if (newLength < oldLength) return change;
if (newLength == 2 || newLength == 5) {
change.setText(textEntered + " ");
newLength++;
} else if (newLength == 11) {
validator = new DateValidator(change.getControlNewText(), inputtedDateFormat);
if (!validator.isValid()) {
return null;
} else {
datePicker.resetValidation();
}
}
change.setCaretPosition(newLength);
change.setAnchor(newLength);
}
}
return change;
}));
datePicker.focusedProperty().addListener((observableValue, wasFocused, isFocused) -> {
if (isFocused) {
Platform.runLater(()-> {
datePicker.validate();
datePicker.getEditor().selectAll();
});
} else {
datePicker.resetValidation();
}
});
datePicker.getEditor().textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observableValue, String s, String t1) {
if (t1.matches(DATE_OF_BIRTH_REGEX)) {
datePicker.resetValidation();
}
}
});
// Show picker choice box on MouseEvent
datePicker.addEventFilter(MouseEvent.MOUSE_CLICKED, mouseEvent -> {
datePicker.show();
});
VBox root = new VBox(50, datePicker, new JFXButton("Button"));
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 300, 120);
primaryStage.setScene(scene);
primaryStage.show();
}
private static class DateValidator {
DateTimeFormatter formatter;
String date;
DateValidator (String date, DateTimeFormatter formatter) {
this.date = date;
this.formatter = formatter;
}
public boolean isValid() {
try {
LocalDate.parse(this.date, this.formatter);
} catch (Exception e) {
return false;
}
return true;
}
}
}
I want to bind a CheckBox in a TableViewCell to a BooleanBinding. The following sample consists of a TableView with a column name and isEffectiveRequired. The checkbox in the column is bound to the Expression:
isRequired.or(name.isEqualTo("X"))
So an item is "effectivly required" when the item in the row is required OR the name is an X, then the expression should be true.
Unfortunately the CheckBox does not reflect the change. For debugging I added a textfield, showing the nameProperty, requiredProperty and the computed effectiveRequiredProperty.
Interestingly when returning just the isRequiredProperty instead of the binding the checkbox works.
public ObservableBooleanValue effectiveRequiredProperty() {
// Bindings with this work:
// return isRequired;
// with this not
return isRequired.or(name.isEqualTo(SPECIAL_STRING));
}
So what is the difference between a Property and a ObservableValue in regard to a CheckBox?
public class TableCellCBBinding extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
private void init(Stage primaryStage) {
primaryStage.setScene(new Scene(buildContent()));
}
private Parent buildContent() {
TableView<ViewModel> tableView = new TableView<>();
tableView.setItems(sampleEntries());
tableView.setEditable(true);
tableView.getColumns().add(buildRequiredColumn());
tableView.getColumns().add(buildNameColumn());
// Add a Textfield to show the values for the first item
// As soon as the name is set to "X", the effectiveRequiredProperty should evaluate to true and the CheckBox should reflect this but it does not
TextField text = new TextField();
ViewModel firstItem = tableView.getItems().get(0);
text.textProperty()
.bind(Bindings.format("%s | %s | %s", firstItem.nameProperty(), firstItem.isRequiredProperty(), firstItem.effectiveRequiredProperty()));
return new HBox(text, tableView);
}
private TableColumn<ViewModel, String> buildNameColumn() {
TableColumn<ViewModel, String> nameColumn = new TableColumn<>("Name");
nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
nameColumn.setEditable(true);
return nameColumn;
}
private TableColumn<ViewModel, Boolean> buildRequiredColumn() {
TableColumn<ViewModel, Boolean> requiredColumn = new TableColumn<>("isEffectiveRequired");
requiredColumn.setMinWidth(50);
// This is should bind my BindingExpression from to ViewModel to the CheckBox
requiredColumn.setCellValueFactory( p -> p.getValue().effectiveRequiredProperty());
requiredColumn.setCellFactory( CheckBoxTableCell.forTableColumn(requiredColumn));
return requiredColumn;
}
private ObservableList<ViewModel> sampleEntries() {
return FXCollections.observableArrayList(
new ViewModel(false, "A"),
new ViewModel(true, "B"),
new ViewModel(false, "C"),
new ViewModel(true, "D"),
new ViewModel(false, "E"));
}
public static class ViewModel {
public static final String SPECIAL_STRING = "X";
private final StringProperty name;
private final BooleanProperty isRequired;
public ViewModel(boolean isRequired, String name) {
this.name = new SimpleStringProperty(this, "name", name);
this.isRequired = new SimpleBooleanProperty(this, "isRequired", isRequired);
this.name.addListener((observable, oldValue, newValue) -> System.out.println(newValue));
}
public StringProperty nameProperty() {return name;}
public final String getName(){return name.get();}
public final void setName(String value){
name.set(value);}
public boolean isRequired() {
return isRequired.get();
}
public BooleanProperty isRequiredProperty() {
return isRequired;
}
public void setRequired(final boolean required) {
this.isRequired.set(required);
}
public ObservableBooleanValue effectiveRequiredProperty() {
// Bindings with this work:
// return isRequired;
// with this not
return isRequired.or(name.isEqualTo(SPECIAL_STRING));
}
}
}
When typing an X into the name the checkbox in the row should be checked.
When typing an X into the name the checkbox in the row is not checked. It's never checked like it is not bound at all.
CheckBoxXXCells don't live up to their doc when it comes to binding their selected state, f.i. (citing here just for signature, even if not set explicitely):
public final Callback <Integer,ObservableValue<Boolean>> getSelectedStateCallback()
Returns the Callback that is bound to by the CheckBox shown on screen.
clearly talks about an ObservableValue, so we would expect that it at least shows the selection state.
Actually, the implementation does exactly nothing if it's not a property, the relevant part from its updateItem:
StringConverter<T> c = getConverter();
if (showLabel) {
setText(c.toString(item));
}
setGraphic(checkBox);
if (booleanProperty instanceof BooleanProperty) {
checkBox.selectedProperty().unbindBidirectional((BooleanProperty)booleanProperty);
}
ObservableValue<?> obsValue = getSelectedProperty();
if (obsValue instanceof BooleanProperty) {
booleanProperty = (ObservableValue<Boolean>) obsValue;
checkBox.selectedProperty().bindBidirectional((BooleanProperty)booleanProperty);
}
checkBox.disableProperty().bind(Bindings.not(
getTableView().editableProperty().and(
getTableColumn().editableProperty()).and(
editableProperty())
));
To work around, use a custom cell that updates the selected state in its updateItem. With the added quirk that we need to disable the check's firing to really keep the visuals in sync with backing state:
requiredColumn.setCellFactory(cc -> {
TableCell<ViewModel, Boolean> cell = new TableCell<>() {
CheckBox check = new CheckBox() {
#Override
public void fire() {
// do nothing - visualizing read-only property
// could do better, like actually changing the table's
// selection
}
};
{
getStyleClass().add("check-box-table-cell");
check.setOnAction(e -> {
e.consume();
});
}
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
check.setSelected(item);
setGraphic(check);
}
}
};
return cell;
});
I have this task:
"We do not want to rely on us Currency their toString() for how a currency is displayed in list our. We will be able to set this up ourselves.
Create a static nested class called "Currency Cell" in ValutaOversikController as extender List Cell <Value>.
Override methods updateItem (Currency and Currency, boolean empty).
Set how a currency should be presented in the list e.g. "Country - Currency Code"
Then put CellFactory for our ListView, which returns an instance of the new Currency Cell class."
I started to make the last method in Controller, but don't know if this is correct. As of now this is what I have:
public class Controller {
#FXML
private ComboBox<Valuta> listeMedValutaerEn, listeMedValutaerTo;
#FXML
private ComboBox<Sorteringen> listeMedSortering;
#FXML
private TextField textFieldValutaerEn, textFieldValutaerTo;
#FXML
private ImageView imageViewValutaerEn, imageViewValutaerTo;
#FXML
public void initialize() {
listeMedValutaerEn.setItems(DataHandler.hentValutaData());
listeMedValutaerTo.setItems(DataHandler.hentValutaData());
listeMedSortering.setItems(DataHandler.hentSorteringsData());
listeMedValutaerEn.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Valuta>() {
#Override
public void changed(ObservableValue<? extends Valuta> observableValue, Valuta gammelValuta, Valuta nyValuta) {
fyllUtValutaEn(nyValuta);
}
});
listeMedValutaerTo.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Valuta>() {
#Override
public void changed(ObservableValue<? extends Valuta> observableValue, Valuta gammelValuta, Valuta nyValuta) {
fyllUtValutaTo(nyValuta);
}
});
listeMedSortering.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Sorteringen>() {
#Override
public void changed(ObservableValue<? extends Sorteringen> observableValue, Sorteringen gammelSortering, Sorteringen nySortering) {
sortere(nySortering);
}
});
}
private void sortere(Sorteringen nySortering) {
ObservableList<Valuta> valutaSomSkalSorteres = DataHandler.hentValutaData();
CompareToValuta sortere = new CompareToValuta(nySortering.getSorteringsKode());
Collections.sort(valutaSomSkalSorteres, sortere);
listeMedValutaerEn.setItems(valutaSomSkalSorteres);
listeMedValutaerTo.setItems(valutaSomSkalSorteres);
}
private void fyllUtValutaEn(Valuta enValuta) {
if (enValuta != null) {
Image flaggEn = new Image("https://www.countryflags.io/" + enValuta.getLandskode() + "/shiny/64.png");
imageViewValutaerEn.setImage(flaggEn);
}
}
private void fyllUtValutaTo(Valuta enValuta) {
if (enValuta != null) {
Image flaggTo = new Image("https://www.countryflags.io/" + enValuta.getLandskode() + "/shiny/64.png");
imageViewValutaerTo.setImage(flaggTo);
}
}
#FXML
private void buttonBeregn(ActionEvent event) {
Integer valutaAntall = Integer.valueOf(textFieldValutaerEn.getText());
double valutaNrEn = listeMedValutaerEn.getSelectionModel().getSelectedItem().getValutakurs();
double valutaNrTo = listeMedValutaerTo.getSelectionModel().getSelectedItem().getValutakurs();
double valutaResultat = valutaAntall * (valutaNrEn / valutaNrTo);
textFieldValutaerTo.setText(String.valueOf(valutaResultat));
}
private static ListCell<Valuta> ValutaCelle() {
ListCell<Valuta> tja = new ListCell<>();
return tja;
}
}
Class DataHandler:
public class DataHandler {
private final static ObservableList<Valuta> valutaListe = FXCollections.observableArrayList();
private final static ObservableList<Sorteringen> sorteringsListe = FXCollections.observableArrayList();
public static ObservableList<Sorteringen> hentSorteringsData() {
if (sorteringsListe.isEmpty()) {
sorteringsListe.add(new Sorteringen("Sortere alfabetisk på land synkende", 1));
sorteringsListe.add(new Sorteringen("Sortere alfabetisk på land stigende", 2));
sorteringsListe.add(new Sorteringen("Sortere på valutakode, stigende", 3));
sorteringsListe.add(new Sorteringen("Sortere på valutakode, synkende", 4));
}
return sorteringsListe;
}
public static ObservableList<Valuta> hentValutaData() {
if (valutaListe.isEmpty()) {
valutaListe.addAll(genererValutaData());
}
return valutaListe;
}
private static ArrayList<Valuta> genererValutaData() {
File kilden = new File("src/no/hiof/aleksar/oblig5/valutakurser.csv");
ArrayList<Valuta> valutaerFraFiler = lesFraCSVFil(kilden);
return valutaerFraFiler;
}
private static ArrayList<Valuta> lesFraCSVFil(File filSomLesesFra) {
ArrayList<Valuta> valutaerFraFil = new ArrayList<>();
try (BufferedReader bufretLeser = new BufferedReader(new FileReader(filSomLesesFra))) {
String linje;
while( (linje = bufretLeser.readLine()) != null ){
String[] deler = linje.split(";");
Valuta enValuta = new Valuta(deler[0], deler[1], deler[2], Double.parseDouble(deler[3]));
valutaerFraFil.add(enValuta);
}
} catch (IOException e) {
System.out.println(e);
}
return valutaerFraFil;
}
}
So I am trying to get a TableView to represent rows of seats. So a row represents an Object of class "Reihe" (german "row").A Reihe has an array of Sitzplatz("seat"). Every seat has got a Button which is supposed to be displayed in the seats cell.
So I am a bit confused about the cellFactories for the TableColumns. How do I tell the Columns to display the button of a seat from row.seat[columnIdx] ?
I cant return an ObservableValue< Button> right? So what am I using as CellFactories?
Class "Reihe"(=row):
public class Reihe implements DataObject
{
private Sitzplatz[] seats;
public Reihe(int seats,int saal)
{
this.seats=new Sitzplatz[seats];
for(int i=0; i<this.seats.length; i++)
{
this.seats[i]=new Sitzplatz();
this.seats[i].setSaal_SID(""+saal);
}
}
public Sitzplatz getSeat(int idx)
{
return seats[idx];
}
...
Class "Sitzplatz" ("seat"):
public class Sitzplatz implements DataObject
{
private SimpleStringProperty platz, reihe,saal_SID, reservierung_REID;
private SeatButton button;
public Sitzplatz()
{
this.platz=new SimpleStringProperty();
this.saal_SID=new SimpleStringProperty();
this.reihe=new SimpleStringProperty();
this.reservierung_REID=new SimpleStringProperty();
button=new SeatButton();
}
public SeatButton getButton()
{
return button;
}
...
Initialization of Columns:
for(int j=0; j<seatColumns; j++)
{
TableColumn<Reihe,Button> nColumn=new TableColumn<>("Seat"+j);
//final int idx=j;
nColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Reihe, Button>, ObservableValue<Button>>() {
#Override
public ObservableValue<Button> call(TableColumn.CellDataFeatures<Reihe, Button> p) {
// ???
}
});
nColumn.setMinWidth(50);
nColumn.setEditable(true);
//getColumns().add(nColumn);
getColumns().add(nColumn);
}
I found something about using Button extends TableCell but again I could not really work out how its supposed to work:
public class SeatButton extends TableCell<Reihe, Button>
{
Button cellButton;
//private Sitzplatz seat;
public SeatButton()
{
//seat=row.getSeat(column);
cellButton=new Button();
cellButton.setMinWidth(30);
cellButton.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent t) {
//....
}
});
}
}
You shouldn't put GUI elements in the model. In this case it makes even less sense, since SeatButton extends TableCell and TableCell creation is independent of the items. Also items are assigned to TableCells by TableView and the item of a TableCell may be changed/removed.
Use the cellValueFactory to return the Sitzplatz for the given column and use a cellFactory that returns TableCell<Reihe, Sitzplatz>:
for(int j=0; j<seatColumns; j++) {
final index = j;
TableColumn<Reihe, Sitzplatz> nColumn = new TableColumn<>("Seat"+j);
nColumn.setCellValueFactory(p -> new SimpleObjectProperty<>(p.getValue().getSeat(index)));
nColumn.setCellFactory(c -> new SeatButton<>());
nColumn.setMinWidth(50);
nColumn.setEditable(false); // you want to modify items not replace them
getColumns().add(nColumn);
}
public class SeatButton<T> extends TableCell<T, Sitzplatz> {
Button cellButton;
public SeatButton() {
cellButton=new Button();
cellButton.setMinWidth(30);
cellButton.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent t) {
//....
}
});
}
#Override
protected void updateItem(Sitzplatz item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
setGraphic(cellButton);
// TODO: adjust button according to data
}
}
}
I have TableView with column inside it that must only accept numbers.
and I added onMouseClickListener to enter edit mode on the mouse click instead of double click on the cell
I want a way to not allowing the user to enter any character except numbers. My code is:
Callback<TableColumn<DailyDetails, String>, TableCell<DailyDetails, String>> defaultCellFactory
= TextFieldTableCell.<DailyDetails>forTableColumn();
dailyCredit.setCellFactory(column -> {
TableCell<DailyDetails, String> cell = defaultCellFactory.call(column);
cell.setOnMouseClicked(e -> {
if (!cell.isEditing() && !cell.isEmpty()) {
cell.getTableView().edit(cell.getIndex(), column);
}
});
return cell;
});
I implemented Table cell from the scratch:
class NumberCell extends TableCell<DailyDetails, String> {
private TextField textField;
public NumberCell() {
}
#Override
public void startEdit() {
super.startEdit();
if (textField == null) {
createTextField();
}
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.selectAll();
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText(String.valueOf(getItem()));
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setText(getString());
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
private void createTextField() {
textField = new TextField(getString());
//textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.lengthProperty().addListener(new ChangeListener<Number>(){
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
if (newValue.intValue() > oldValue.intValue()) {
char ch = textField.getText().charAt(oldValue.intValue());
// Check if the new character is the number or other's
if (!(ch >= '0' && ch <= '9' )) {
// if it's not number then just setText to previous one
textField.setText(textField.getText().substring(0,textField.getText().length()-1));
}
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
Callback<TableColumn<DailyDetails, String>,
TableCell<DailyDetails, String>> cellFactory
= (TableColumn<DailyDetails, String> p) -> new NumberCell();
dailyDebit.setCellFactory(cellFactory);
the problem is i lost the on mouse listener cell.setOnMouseClicked!!!
how do i get the cell again to assign the listener ???
Just for driving the new api into everybody's brain: a full example with a slightly different TextFormatter (than in the other answer) that is Locale-aware and (dirtily!) hooked into core TextFieldTableCell, can be used in any custom editing TableCell as well:
/**
* Example of how-to use a TextFormatter in a editing TableCell.
*/
public class CellFormatting extends Application {
private Parent getContent() {
ObservableList<IntData> data = FXCollections.observableArrayList(
new IntData(1), new IntData(2), new IntData(3)
);
TableView<IntData> table = new TableView<>(data);
table.setEditable(true);
TableColumn<IntData, Integer> column = new TableColumn<>("Data");
column.setCellValueFactory(new PropertyValueFactory("data"));
// core default: will throw exception on illegal values
// column.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));
NumberFormat format = NumberFormat.getIntegerInstance();
UnaryOperator<TextFormatter.Change> filter = c -> {
if (c.isContentChange()) {
ParsePosition parsePosition = new ParsePosition(0);
// NumberFormat evaluates the beginning of the text
format.parse(c.getControlNewText(), parsePosition);
if (parsePosition.getIndex() == 0 ||
parsePosition.getIndex() < c.getControlNewText().length()) {
// reject parsing the complete text failed
return null;
}
}
return c;
};
column.setCellFactory(c -> new ValidatingTextFieldTableCell<>(
// note: each cell needs its own formatter
// see comment by #SurprisedCoconut
new TextFormatter<Integer>(
// note: should use local-aware converter instead of core!
new IntegerStringConverter(), 0,
filter)));
table.getColumns().add(column);
VBox box = new VBox(table);
return box;
}
/**
* TextFieldTableCell that validates input with a TextFormatter.
* <p>
* Extends TextFieldTableCell, accesses super's private field reflectively.
*
*/
public static class ValidatingTextFieldTableCell<S, T> extends TextFieldTableCell<S, T> {
private TextFormatter<T> formatter;
private TextField textAlias;
public ValidatingTextFieldTableCell() {
this((StringConverter<T>)null);
}
public ValidatingTextFieldTableCell(StringConverter<T> converter) {
super(converter);
}
public ValidatingTextFieldTableCell(TextFormatter<T> formatter) {
super(formatter.getValueConverter());
this.formatter = formatter;
}
/**
* Overridden to install the formatter. <p>
*
* Beware: implementation detail! super creates and configures
* the textField lazy on first access, so have to install after
* calling super.
*/
#Override
public void startEdit() {
super.startEdit();
installFormatter();
}
private void installFormatter() {
if (formatter != null && isEditing() && textAlias == null) {
textAlias = invokeTextField();
textAlias.setTextFormatter(formatter);
}
}
private TextField invokeTextField() {
Class<?> clazz = TextFieldTableCell.class;
try {
Field field = clazz.getDeclaredField("textField");
field.setAccessible(true);
return (TextField) field.get(this);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
public static class IntData {
IntegerProperty data = new SimpleIntegerProperty(this, "data");
public IntData(int value) {
setData(value);
}
public void setData(int value) {
data.set(value);
}
public int getData() {
return data.get();
}
public IntegerProperty dataProperty() {
return data;
}
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent()));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
BTW, the formatter is re-used from another question where the task at hand was to restrict input into a Spinner.
Use a TextFormatter on the TextField like this:
TextFormatter<String> formatter = new TextFormatter<String>( change -> {
change.setText(change.getText().replaceAll("[^0-9.,]", ""));
return change;
});
textField.setTextFormatter(formatter);
Works with Java8u40 upwards. Use e. g. the TableView example from the Oracle site as base.