How to eliminate the gray background of the tableview edit-cell - javafx

Problem
When you edit a table cell the row height usually becomes higher, which is unwanted behavior:
I managed to elimininate the height change with this css:
.text-field-table-cell {
-fx-padding: 0;
-fx-background-insets: 0.0;
}
.text-field-table-cell .text-field {
-fx-padding: 0;
-fx-background-insets: 0.0;
-fx-background-color:yellow;
-fx-border-width: 0;
}
But the cell is still not filled fully with the TextField. There's a gray background visible:
Question
Does anyone know how to eliminate the gray background of the edit-cell? Either by colorizing it or by removing it.
Code
Here's the full code if someone wants to test it:
InlineEditingTableViewCSS.java
public class InlineEditingTableViewCSS extends Application {
private final ObservableList<Data> data =
FXCollections.observableArrayList(
new Data(1.,5.),
new Data(2.,6.),
new Data(3.,7.),
new Data(4.,8.)
);
private TableView<Data> table;
#Override
public void start(Stage stage) {
// create edtiable table
table = new TableView<Data>();
table.setEditable(true);
// column 1 contains numbers
TableColumn<Data, Number> number1Col = new TableColumn<>("Number 1");
number1Col.setMinWidth(100);
number1Col.setCellValueFactory( cellData -> cellData.getValue().number1Property());
number1Col.setCellFactory( createNumberCellFactory());
// column 2 contains numbers
TableColumn<Data, Number> number2Col = new TableColumn<>("Number 2");
number2Col.setMinWidth(100);
number2Col.setCellValueFactory( cellData -> cellData.getValue().number2Property());
number2Col.setCellFactory( createNumberCellFactory());
// add columns & data to table
table.setItems(data);
table.getColumns().addAll( number1Col, number2Col);
// switch to edit mode on keypress
// this must be KeyEvent.KEY_PRESSED so that the key gets forwarded to the editing cell; it wouldn't be forwarded on KEY_RELEASED
table.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if( event.getCode() == KeyCode.ENTER) {
// event.consume(); // don't consume the event or else the values won't be updated;
return;
}
// switch to edit mode on keypress, but only if we aren't already in edit mode
if( table.getEditingCell() == null) {
if( event.getCode().isLetterKey() || event.getCode().isDigitKey()) {
TablePosition focusedCellPosition = table.getFocusModel().getFocusedCell();
table.edit(focusedCellPosition.getRow(), focusedCellPosition.getTableColumn());
}
}
}
});
table.addEventFilter(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if( event.getCode() == KeyCode.ENTER) {
table.getSelectionModel().selectBelowCell();
}
}
});
// single cell selection mode
table.getSelectionModel().setCellSelectionEnabled(true);
table.getSelectionModel().selectFirst();
// add nodes to stage
BorderPane root = new BorderPane();
root.setCenter(table);
Scene scene = new Scene( root, 800,600);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
/**
* Number cell factory which converts strings to numbers and vice versa.
* #return
*/
private Callback<TableColumn<Data, Number>, TableCell<Data, Number>> createNumberCellFactory() {
Callback<TableColumn<Data, Number>, TableCell<Data, Number>> factory = TextFieldTableCell.forTableColumn( new StringConverter<Number>() {
#Override
public Number fromString(String string) {
return Double.parseDouble(string);
}
#Override
public String toString(Number object) {
return object.toString();
}
});
return factory;
}
/**
* Table data container
*/
public static class Data {
private final SimpleDoubleProperty number1;
private final SimpleDoubleProperty number2;
private Data( Double number1, Double number2) {
this.number1 = new SimpleDoubleProperty(number1);
this.number2 = new SimpleDoubleProperty(number2);
}
public final DoubleProperty number1Property() {
return this.number1;
}
public final double getNumber1() {
return this.number1Property().get();
}
public final void setNumber1(final double number1) {
this.number1Property().set(number1);
}
public final DoubleProperty number2Property() {
return this.number2;
}
public final double getNumber2() {
return this.number2Property().get();
}
public final void setNumber2(final double number2) {
this.number2Property().set(number2);
}
}
public static void main(String[] args) {
launch(args);
}
}
application.css
.text-field-table-cell {
-fx-padding: 0;
-fx-background-insets: 0.0;
}
.text-field-table-cell .text-field {
-fx-padding: 0;
-fx-background-insets: 0.0;
-fx-background-color:yellow;
-fx-border-width: 0;
}
Or is there a good mechanism to find out which CSS is currently being used by a node?
Thank you very much!

Does anyone know how to eliminate the gray background of the
edit-cell?
Set padding values for textfield:
.text-field-table-cell {
-fx-padding: 0;
-fx-background-insets: 0.0;
}
.text-field-table-cell .text-field {
-fx-padding: 3 0 3 0;
-fx-background-insets: 0.0;
-fx-background-color:yellow;
-fx-border-width: 0;
}

Related

Unable to use DatePicker choose box when using TextFormatter

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;
}
}
}

JavaFX CSS property for foreground color and others?

I am trying to override foreground color for table rows with something like this
.table-row-cell:odd {
-fx-text-fill: blue;
-fx-background-color: lightblue;
}
.table-row-cell:even {
-fx-text-fill: red;
-fx-background-color: lightpink;
}
I see, that background color works, while foreground does not:
The sample code is follows:
**
* Checking how to override foreground color
*/
public class TableViewCSS2 extends Application {
private static String[][] names = new String[][]{
{"Julius", "Caesar"},
{"Wolfgang", "Mozart"},
{"William", "Shakespeare"},
{"Napoleon", "Bonaparte"},
{"Isaac", "Newton"},
{"Albert", "Einstein"},
{"Christopher", "Columbus"},
{"Johann", "Bach"},
{"Ludwig", "Beethoven"},
{"Gautama", "Buddha"},
{"Martin", "Luther"},
{"Galileo", "Galilei"},
{"Karl", "Marx"},
{"Marco", "Polo"},
{"Immanuel", "Kant"}
};
BorderPane root;
{
BorderPane ans = new BorderPane();
root = ans;
}
TableView<String[]> table;
{
TableView<String[]> ans = new TableView<String[]>();
root.setCenter(ans);
table = ans;
}
TableColumn<String[],String> firstNameCol;
{
TableColumn<String[],String> ans = new TableColumn<String[], String>("First Name");
ans.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<String[], String>, ObservableValue<String>>() {
public ObservableValue<String> call(final TableColumn.CellDataFeatures<String[], String> param) {
return new ObservableValueBase<String>() {
public String getValue() {
return param.getValue()[0];
}
};
}
});
table.getColumns().add(ans);
firstNameCol = ans;
}
TableColumn<String[],String> secondNameCol;
{
TableColumn<String[],String> ans = new TableColumn<String[], String>("Second Name");
ans.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<String[], String>, ObservableValue<String>>() {
public ObservableValue<String> call(final TableColumn.CellDataFeatures<String[], String> param) {
return new ObservableValueBase<String>() {
public String getValue() {
return param.getValue()[1];
}
};
}
});
table.getColumns().add(ans);
secondNameCol = ans;
}
private ObservableList<String[]> namesList;
{
ObservableList<String[]> ans = FXCollections.observableArrayList();
for(int i=0; i<names.length; ++i) {
ans.add( names[i] );
}
table.setItems(ans);
namesList = ans;
}
#Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(root, 800, 600);
scene.getStylesheets().add("/sample2.css");
primaryStage.setTitle("TableViewCSS2");
primaryStage.setScene(scene);
primaryStage.show();
}
}
UPDATE 2
Adding important! didn't help.
The following CSS worked:
.table-row-cell:odd {
-fx-background-color: lightblue;
}
.table-row-cell:odd > .table-cell{
-fx-text-fill: blue;
}
.table-row-cell:even {
-fx-background-color: lightpink;
}
.table-row-cell:even > .table-cell{
-fx-text-fill: red;
}
And you can check the default styles defined by JvaFX itself. See also: Default JavaFX-CSS
You'll find plenty of examples in there
The selector is valid and applies to the font color. Seeing as the text is not red, it's possible that a more-specific CSS rule applies (and takes priority)

How to refresh pane in javafx

I recently have been learning JavaFX and have an issue about how to refresh the pane. In this simple program, I want the black square to move to the right into the next block when I am clicking the move button, but it doesn't work, I'm wondering how can I fix my code.
screen shot
Main Class:
public class Main extends Application {
private Cell cells[] = new Cell[5];
private Player player;
private Board board;
Button move = new Button("move");
public Main() throws Exception {
for (int i = 0; i < cells.length; i++) {
cells[i] = new Cell(i);
}
this.player = new Player(0, cells);
this.board = new Board(player, cells);
}
#Override
public void start(Stage primaryStage) throws Exception {
Main game = new Main();
BorderPane pane = new BorderPane();
pane.setCenter(board);
pane.setBottom(move);
Scene scene = new Scene(pane,400,80);
primaryStage.setTitle("Move");
primaryStage.setScene(scene);
primaryStage.show();
move.setOnAction(e -> game.move());
}
public void move() {
player.currentCell.index += 1;
board.paint();
}
public static void main(String[] args) {
launch(args);
}
}
Board class:
class Board extends Pane {
private Player player;
public Cell cells[];
private final int CELLWIDTH = 40;
private final int CELLHEIGHT = 40;
private final int LMARGIN = 100;
public Board(Player p, Cell cells[]) {
player = p;
this.cells = cells;
paint();
}
public void paint() {
Cell cell;
for (int i=0; i<cells.length; i++) {
cell = cells[i];
Rectangle r1 = new Rectangle(xCor(cell.index), 0, CELLWIDTH, CELLHEIGHT);
r1.setStroke(Color.BLACK);
r1.setFill(Color.WHITE);
getChildren().add(r1);
}
cell = player.currentCell;
Rectangle r2 = new Rectangle(xCor(cell.index), 0, CELLWIDTH, CELLHEIGHT);
r2.setFill(Color.BLACK);
getChildren().add(r2);
}
private int xCor(int col) {
return LMARGIN + col * CELLWIDTH;
}
}
Player Class:
class Player {
public int position;
public Cell currentCell;
public Player(int position, Cell cell[]) throws Exception {
this.currentCell = cell[0];
}
}
Cell Class:
class Cell {
public int index;
public Cell(int index) {
this.index = index;
}
}
You might want to reword your code, storing the player's location just in the Player class is going to make life difficult. I would also suggest adding a flag to the Cell class stating if the player is inside, e.g.
class Cell {
public int index;
private Player playerInCell;
public Cell(int index) {
this.index = index;
}
public void setPlayerInCell(Player p){
this.playerInCell = p;
}
public void clearPlayerInCell(){
this.playerInCell = null;
}
public Player getPlayerInCell(){
return this.playerInCell;
}
}
Then, upon moving a player to the Cell you can clear them from the previous Celland set them in the new one and in your Paint() function, if player is present, colour the cell in.
The other thing, if you wish to stick with your method, your issue is caused by that you are only changing the index property on the Cell class, you should also be either changing the Cell's position in the array cells[] or just changing the currentCell property of the Player class, otherwise your player always stays in the same place. Here is an example of changing the Player's currentCell property:
public void move() {
Cell currentCell = player.currentCell;
Cell nextCell = null;
for (int i = 0; i < cells.length; i++) {
if (cells[i] == currentCell && i+1 < cells.length){
nextCell = cells[i+1];
break;
}
}
if (nextCell != null)
player.currentCell = nextCell;
else{
//Error handling, next cell not found
}
board.paint();
}
[Edit]
I've done some major code cleanup, some of the ways you were doing things was a bit odd, I hope you don't mind, here are the classes that changed:
Main
public class Main extends Application {
private Cell cells[] = new Cell[5];
private Player player;
private Board board;
Button move = new Button("move");
public Main() throws Exception{
for (int i = 0; i < cells.length; i++) {
cells[i] = new Cell(i);
}
this.player = new Player(cells[0]);
this.board = new Board(player, cells);
}
#Override
public void start(Stage primaryStage) throws Exception{
BorderPane pane = new BorderPane();
pane.setCenter(board);
pane.setBottom(move);
Scene scene = new Scene(pane,400,80);
primaryStage.setTitle("Move");
primaryStage.setScene(scene);
primaryStage.show();
move.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
move();
}
});
}
public void move() {
//Get current players cell, we want to move them one right
Cell currentCell = player.getCurrentCell();
Cell nextCell = null;
//Searching for current cell in board, if found we need to clear the player from it and select the next cell
for (int i = 0; i < cells.length; i++) {
if (cells[i] == currentCell && i+1 < cells.length){
cells[i].clearPlayerInCell();
nextCell = cells[i+1];
break;
}
}
//We found it, let's move the player
if (nextCell != null) {
player.setCurrentCell(nextCell);
nextCell.setPlayerInCell(player);
}
//We didn't find it, or our index was out of range, what do we do now?
else{
//Error handling, next cell not found
//Example, let's put them back at the start
player.setCurrentCell(cells[0]);
cells[0].setPlayerInCell(player);
cells[cells.length-1].clearPlayerInCell();
}
board.paint();
}
public static void main(String[] args) {
launch(args);
}
}
Board
public class Board extends Pane {
private Player player;
private Cell cells[];
private final int CELLWIDTH = 40;
private final int CELLHEIGHT = 40;
private final int LMARGIN = 100;
public Board(Player p, Cell cells[]) {
player = p;
this.cells = cells;
paint();
}
public Cell[] getCells(){
return this.cells;
}
public Player getPlayer() {
return player;
}
public void paint() {
//Clear previous cells, we don't need them now
getChildren().clear();
//Redraw them
for(Cell cell : cells){
Rectangle r1 = new Rectangle(xCor(cell.getIndex()), 0, CELLWIDTH, CELLHEIGHT);
r1.setStroke(Color.BLACK);
//We've found a player in the cell, let's colour it black
if (cell.getPlayerInCell() != null)
r1.setFill(Color.BLACK);
//No, player in this cell, white it is
else
r1.setFill(Color.WHITE);
getChildren().add(r1);
}
}
private int xCor(int col) {
return LMARGIN + col * CELLWIDTH;
}
}
Player
public class Player {
private Cell currentCell;
public Player(Cell cell) throws Exception {
this.currentCell = cell;
cell.setPlayerInCell(this);
}
public Cell getCurrentCell(){
return this.currentCell;
}
public void setCurrentCell(Cell cell){
this.currentCell = cell;
}
}
Cell
public class Cell {
private int index;
private Player playerInCell;
public Cell(int index) {
this.index = index;
}
public void setPlayerInCell(Player p){
this.playerInCell = p;
}
public void clearPlayerInCell(){
this.playerInCell = null;
}
public Player getPlayerInCell(){
return this.playerInCell;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
That now works and I can move the cell along, I've also set it so that the cell goes back to the beginning if the player reaches the end, but that's an example.
It works off using the Cell property of playerInCell, if that isn't null then we know a player is in the cell and can colour it black. If it is null, no player is in the cell and we can colour it white. This also allows you in the future to maybe have more players with different colours. Though I don't know what your end goal is. Hope this helps and if you want any further explanation, feel free to ask
Also, for further reading, see here why it's better to use getter and setters like I have
Also, the reasoning behind this bit of code:
move.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
move();
}
});
Is because I'm using Java 1.7 instead of Java 1.8 and can't use predicates, you should be safe to change that to move.setOnAction(e -> this.move()); instead.

JavaFx Create Table Cell Accepts numbers only?

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.

How to select multiple components of Canvas using ctrl key in javafx?

I want to select few images available on my Canvas. How to select multiple of them using control key?
You create a model, e. g.
List<Node> selectionModel = new ArrayList<Node>();
and add a listener to each node, e. g.
imageView.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> {
if( event.isControlDown()) {
selectionModel.add( (Node) event.getSource());
// logging
System.out.println("Items in model:");
selectionModel.forEach(n -> System.out.println(n));
}
});
Of course you need to provide an option to clear the selection model, e. g. by clicking outside on the scene instead of an ImageView.
Here's a more sophisticated example. Supports rubberband selection, ctrl for selection toggle, shift for adding to selection and click on background to clear the selection.
public class ImageSelection extends Application {
Image image = new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/320px-Siberischer_tiger_de_edit02.jpg");
SelectionModel selectionModel = new SelectionModel();
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
double width = image.getWidth();
double height = image.getHeight();
double padding = 20;
for( int row=0; row < 4; row++) {
for( int col=0; col < 4; col++) {
ImageView imageView = new ImageView( image);
imageView.relocate( padding * (col+1) + width * col, padding * (row + 1) + height * row);
pane.getChildren().add(imageView);
}
}
Scene scene = new Scene( pane, 1800, 1200);
primaryStage.setScene( scene);
primaryStage.show();
new RubberBandSelection( pane);
}
public static void main(String[] args) {
launch(args);
}
private class SelectionModel {
Set<Node> selection = new HashSet<>();
public void add( Node node) {
node.setStyle("-fx-effect: dropshadow(three-pass-box, red, 10, 10, 0, 0);");
selection.add( node);
}
public void remove( Node node) {
node.setStyle("-fx-effect: null");
selection.remove( node);
}
public void clear() {
while( !selection.isEmpty()) {
remove( selection.iterator().next());
}
}
public boolean contains( Node node) {
return selection.contains(node);
}
public void log() {
System.out.println( "Items in model: " + Arrays.asList( selection.toArray()));
}
}
private class RubberBandSelection {
final DragContext dragContext = new DragContext();
Rectangle rect;
Pane group;
public RubberBandSelection( Pane group) {
this.group = group;
rect = new Rectangle( 0,0,0,0);
rect.setStroke(Color.BLUE);
rect.setStrokeWidth(1);
rect.setStrokeLineCap(StrokeLineCap.ROUND);
rect.setFill(Color.LIGHTBLUE.deriveColor(0, 1.2, 1, 0.6));
group.addEventHandler(MouseEvent.MOUSE_PRESSED, onMousePressedEventHandler);
group.addEventHandler(MouseEvent.MOUSE_DRAGGED, onMouseDraggedEventHandler);
group.addEventHandler(MouseEvent.MOUSE_RELEASED, onMouseReleasedEventHandler);
}
EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
dragContext.mouseAnchorX = event.getSceneX();
dragContext.mouseAnchorY = event.getSceneY();
rect.setX(dragContext.mouseAnchorX);
rect.setY(dragContext.mouseAnchorY);
rect.setWidth(0);
rect.setHeight(0);
group.getChildren().add( rect);
event.consume();
}
};
EventHandler<MouseEvent> onMouseReleasedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if( !event.isShiftDown() && !event.isControlDown()) {
selectionModel.clear();
}
for( Node node: group.getChildren()) {
if( node instanceof ImageView) {
if( node.getBoundsInParent().intersects( rect.getBoundsInParent())) {
if( event.isShiftDown()) {
selectionModel.add( node);
} else if( event.isControlDown()) {
if( selectionModel.contains( node)) {
selectionModel.remove( node);
} else {
selectionModel.add( node);
}
} else {
selectionModel.add( node);
}
}
}
}
selectionModel.log();
rect.setX(0);
rect.setY(0);
rect.setWidth(0);
rect.setHeight(0);
group.getChildren().remove( rect);
event.consume();
}
};
EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
double offsetX = event.getSceneX() - dragContext.mouseAnchorX;
double offsetY = event.getSceneY() - dragContext.mouseAnchorY;
if( offsetX > 0)
rect.setWidth( offsetX);
else {
rect.setX(event.getSceneX());
rect.setWidth(dragContext.mouseAnchorX - rect.getX());
}
if( offsetY > 0) {
rect.setHeight( offsetY);
} else {
rect.setY(event.getSceneY());
rect.setHeight(dragContext.mouseAnchorY - rect.getY());
}
event.consume();
}
};
private final class DragContext {
public double mouseAnchorX;
public double mouseAnchorY;
}
}
}

Resources