Updating Tableview javaFX row by row - javafx

I am facing problem in updating values in my table view.
I have a for loop which updates all my rows values. I am using StringProperty as my model class field types. When I update a single row it works fine. But my requirement is I need to stop for 1000 millisec in each row after updation. I tried using Thread.sleep(1000) in my for loop which is not helping :(

Don't try to manipulate your Thread, it will be too complicate.
Simply use a TimeLine like this :
timer = new Timeline(new KeyFrame(Duration.millis(1000), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
//Pause your timer in order to update your row
timer.pause();
//Do your update (increment your rowIndex inside)
updateRowMethod(rowIndex);
//Then launch the Timer and exit
timer.play();
}
}));
I use the pause method because your said you wanted to wait 1000 milliseconds after the edition of a line. If you just want to edit a line every second, skip the pause/play part.

Related

JavaFX TableView Column CheckBox listener

I have TableColumn with CheckBox. I have to do this listener and I wonder why the listener doubles after every click.
selectedColumn.setCellFactory(column -> new CheckBoxTableCell<>());
selectedColumn.setCellValueFactory(cellData -> {
Dir dir = cellData.getValue();
BooleanProperty property = dir.isSelectedProperty();
property.addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) ->{
System.out.println(newValue);
});
return property;
});
First click in checkbox i row return:
True
True
Second unselect return:
False
False
False
False
Thrid select return:
True
True
True
True
True
True
why ? :)
This is happening because controls like TableView (and ListView etc) virtualizes its contents. From TableView Javadoc:
The TableView control is designed to visualize an unlimited number of
rows of data, broken out into columns.
The TableView may hold a large number of items. However, on the screen, you could probably see 10-30 rows (each corresponding to a single item) at any point in time.
Without virtualization, if you have 1 million items, it would create 1 million TableRow objects (we haven't talked about TableCell), with each TableRow maintaining all the states/values. This takes a lot of memory and processing power.
On the other hand, with virtualization, if your View can only display 10 rows, TableView will only create, for example, 12 TableRow instances. As you scroll through the list, some of the items disappear from your sight. These TableRow instances are immediately reused for items that enters your sight.
This is why setCellFactory() and setCellValueFactory() methods are of Callback type. Each time a row was reused, it will call this callback object to update the TableCell. This is why your listener is being added repeatedly, causing this.
Depending on what you need, it may be possible to add a ListChangedListener to the list of items.
ObservableList<Dir> list = FXCollections.observableArrayList(item -> new javafx.beans.Observable[] {item.isSelectedProperty()});
list.addAll(DirList.getDirList());
dirList.setItems(list);
list.addListener(new ListChangeListener<Dir>() {
#Override public void onChanged(javafx.collections.ListChangeListener.Change<? extends Dir> c) {
while (c.next()) {
if (c.wasUpdated()) {
// Do something
}
}
}
});
I see you have a model named Dir with a BooleanProperty named selected
You can simply let the cellValueFactory just:
selectedColumn.setCellValueFactory(cellData -> cellData.getValue().isSelectedProperty());
This would update the model's property if you check/un-check the CheckBox in the table cell.
If you use this selected/deselected state of the checkbox, you can use the model, and add the listener there, you will get the same result. Then the listener will be added just once.
You should have instances of Dir created then you can do the following for ex. in initialize:
dir1.selectedProperty().addListener((observable, oldValue, newValue) -> {
dir2.selectedProperty().set(oldValue);
});
Or anything you want, then you are sure that the listener is added just once.
As #Jai mentioned, the cell data is used many time and every time the CallBack is called, the listener is added to the property, so don't use it inside that method if you want to avoid adding the listener many times to the property.

Execute method from other class JavaFX

I have a little problem.I have an Stage with a table view . Im counting the rows with this method:
public void count() {
int number;
number= tableview.getItems().size();
lblcount.setText(String.valueOf(number));
}
I have a second stage with the same tableview ,but in this stage i can delete rows. I need to refresh the first tableview or the count, in each change what im doing in the second tableview.
BUt if i call the method from the second stage nothing happens.
Im doing this in the second Stage:
Class1 C1 = new Class1();
C1.count();
I need to execute the method in the first Stage and update or refresh the count of the tableview from the other Stage.
Thanks in advance.. :D

refresh label not working correctly javafx

I have a problem with a refresh on a Label.
I have an function like this :
public void majMontantPaye(Double montantPaye) {
System.out.println("montant paye : "+montantPaye);
setMontantPaye(this.montantPaye+montantPaye);
Platform.runLater(() -> labelMontantPaye.setText(String.format("%.2f", this.montantPaye)+Messages.getMessage("0052")));
}
And My function is call by an API. This API communicate with a machine who allow to insert coin. And my function must to show the sum insert in the machine.
The problem is, when I insert a lot of coin in the same time in the machine, my function is correctly call every coin detected, so the System.out.println("montant paye : "+montantPaye); is correctly show every coin detected, BUT the Label "labelMontantPaye" is not refresh to every coin detected. Just on finish with the total sum.
I guess that the UI is not correctly refresh but I don't know how refresh correctly my Label.
Help please and sorry for mistake, i'm french.
You can follow the logic below:
As mentioned in the comments:
Using Platform.runLater(...) you queue the task into the JavaFXThread. But when you have many "events" you will only see the last result. (And maybe previous ones for a short time).
šŸ„Use a BlockingQueue to store each coin that is inserted.Use the method below(also have a look at the tutorial for the available methods,here i am using put which is blocking the current thread if a maximum of coins are inserted into the Queue,if you don't want this set the maximum to something like 500.000):
public void insertCoin(//maybe the kind of coin){
//add the coin into the BlockingQueue
blockingQueue.put(//coin);
}
Use a Thread which is running an infinity loop.The Thread is waking up
every time a new coin is inserted and when that is done , that Thread
waits JavaFXThread to refresh the Label Text:
new Thread(() -> {
//Run an infinity Thread
while (true) {
// Blocks until the queue has really any coins inserted
blockingQueue.get();
// Synchronize with javaFX thread
CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(() -> {
label.setText(....);
latch.countDown();
});
// Block the Current Thread until the text is refreshed from
// JavaFX Thread
latch.await();
}
}).start();

Javafx TableView Edit Validation

I build a little JavaFX TableView for displaying data. The user should be able to edit the data in the tableview. The problem is: only specific values are allowed in certain fields. If the user entered a wrong value, the field is set to 0.
Here is my Class:
private ObservableList shots;
#FXML
void initialize() {
this.shots = FXCollections.observableArrayList(match.getShots()); // values from database
tblShots.setItems(shots);
tblShots.setEditable(true);
lblserienid.setText(GUIConstants.idPlaceHolder);
lblresult.setText(GUIConstants.idPlaceHolder);
colShotID.setCellValueFactory(new PropertyValueFactory<Schuss, String>("idSchuss"));
colRing.setCellValueFactory(new PropertyValueFactory<Schuss, String>("ringString"));
colRing.setCellFactory(TextFieldTableCell.forTableColumn());
colRing.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Schuss, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<Schuss, String> t) {
Schuss s = (Schuss) t.getTableView().getItems().get(
t.getTablePosition().getRow());
try {
int ring = Integer.parseInt(t.getNewValue());
s.setRing(ring);
} catch (Exception ex) {
s.setRing(0);
}
SerienauswertungViewController.this.refreshTable();
}
});
colRing.setEditable(true);
// .... omitted
}
private void refreshTable(){
if(shots.size()>0) {
btnDeleteAll.setDisable(false);
btnEdit.setDisable(false);
int res = 0;
for(int i=0;i<shots.size();i++){
Schuss s = (Schuss)shots.get(i);
res += s.getRing();
}
lblresult.setText(""+res);
}
else {
btnDeleteAll.setDisable(true);
btnEdit.setDisable(true);
lblresult.setText(GUIConstants.idPlaceHolder);
}
}
So when I edit a tableviewcell and enter "q" (this value is not allowed) and press enter, the debugger jumps in the above catch block, sets the specific value in the observablelist to 0 (I can see this in the debugger, when I expand this object) but the tableviewcell still displays q instead of 0 (which has been corrected by the system)...
Why does the tableview not show the right values of the observablelist-Object???
This was required but brandnew since Java8u60 (yes - they changed API in an udpate!?!) there is a refresh() method on the TableView itself.
/**
* Calling {#code refresh()} forces the TableView control to recreate and
* repopulate the cells necessary to populate the visual bounds of the control.
* In other words, this forces the TableView to update what it is showing to
* the user. This is useful in cases where the underlying data source has
* changed in a way that is not observed by the TableView itself.
*
* #since JavaFX 8u60
*/
public void refresh() {
getProperties().put(TableViewSkinBase.RECREATE, Boolean.TRUE);
}
It is so new, itĀ“s not even in the official oracle docs... So I cannot provide a link.
cheers.
Okey this seems to be a bug. I used a work around which is mentioned here:
tblShots.getColumns().get(1).setVisible(false);
tblShots.getColumns().get(1).setVisible(true);
Though the refresh() definitely works in conjunction with an upgrade to 8u60, the large project on which I work is currently stuck on 8u51 and cannot reasonably move to u60 any time soon. I tried to implement the code in the refresh() that Rainer referenced in place of the other kluges mentioned above, specifically setting the column invisible/visible. However, simply implementing
getProperties().put(TableViewSkinBase.RECREATE, Boolean.TRUE);
did not work within u51. Upon doing more googling, I came across the JavaFx/Oracle Jira issue here:
https://bugs.openjdk.java.net/browse/JDK-8098085
If you open the rt22599.patch attachment you will find changes to various skins, specifically for TableViews, the TableViewSkinBase. This module is not delivered in the src-javafx.zip that comes with the jdk install. Looking for info on how to possibly incorporate the SkinBase change to a u51 install.
Since JavaFX 8u60 you can use(assuming tableView is an instance of TableView class):
tableView.refresh();
It worked for me

How to get selected TableCell in JavaFX TableView

I am looking for a way to get the selected cell of a TableView control. Note that I don't just want the cell value, I want an actual TableCell object. One would expect that the method:
tableView.getSelectionModel().getSelectedCells().get(0)
does just that, but it returns a TablePosition object, which gives you row and column information, but I don't see a way to get TableCell object from that.
The reason I need this is because I want to respond to a key press, but attaching an event filter to TableCell does not work (probably because it is not editable). So I attach it to TableView, but then I need to get the currently selected cell.
EDIT: For future readers: DO NOT mess with TableCell objects, except in cell factory. Use the TableView the way designers intended, or you will be in lot of trouble. If you need data from multiple sources in single table, it is better to make a new class that aggregates all the data and use that as a TableView source.
I just posted an answer that uses this code to edit a Cell. I don't think you can get a reference to the actual table cell as that's internal to the table view.
tp = tv.getFocusModel().getFocusedCell();
tv.edit(tp.getRow(), tp.getTableColumn());
Your method also returns a TablePosition so you can use that as well.
Here's the link https://stackoverflow.com/a/21988562/2855515
This will probably get downvoted because the OP asked about returning the cell itself, rather than what I'll describe, but a Google search led me here for my issue.
I personally ran into issues trying to retrieve data from an individual cell.
java.is.for.desktop offered buggy code related to this matter, that throws an ArrayIndexOutOfBoundsException, but is on the right track. My goal is to offer a better example of that using a lambda.
To access data from a single TableCell:
tableView.getFocusModel().focusedCellProperty().addListener((ObservableValue<? extends TablePosition> observable, TablePosition oldPos, TablePosition pos) -> {
int row = pos.getRow();
int column = pos.getColumn();
String selectedValue = "";
/* pos.getColumn() can return -1 if the TableView or
* TableColumn instances are null. The JavaDocs state
* this clearly. Failing to check will produce an
* ArrayIndexOutOfBoundsException when underlying data is changed.
*/
if ((pos.getRow() != -1) && (pos.getColumn() != -1))
{
selectedValue = tableView.getItems()
.get(row)
.get(column);
if ((selectedValue != null) && (!selectedValue.isEmpty()))
{
// handling if contains data
}
else
{
// handling if doesn't contain data
}
}
});
Edit:
I meant to say ArrayIndexOutOfBoundsException, rather than NullPointerException, I updated this answer to reflect that. I also cleaned up spelling and grammar.
You want to respond to key press? Better don't.
Instead, you could register a listener for focusing of table cells, which would work with arrow keys and mouse clicks on table cells (and even with touch events, oh my, the future is already there).
table.getFocusModel().focusedCellProperty().addListener(
new ChangeListener<TablePosition>() {
#Override
public void changed(ObservableValue<? extends TablePosition> observable,
TablePosition oldPos, TablePosition pos) {
int row = pos.getRow();
int column = pos.getColumn();
String selectedValue = "";
if (table.getItems().size() > row
&& table.getItems().get(row).size() > column) {
selectedValue = table.getItems().get(row).get(column);
}
label.setText(selectedValue);
}
});
In this example, I am using a "classic" TableView with List<String> as column model. (So, your data type could be different than String.) And, of course, that label is just an example from my code.

Resources