69

I need to detect double clicks on a row of a TableView.

How can I listen for double clicks on any part of the row and get all data of this row to print it to the console?

Ivar
  • 6,138
  • 12
  • 49
  • 61
Marcos
  • 4,827
  • 3
  • 20
  • 28
  • The solutions using double-click on cells would work, because you can always call `getTableRow().getItem()` on a cell to get the item for the row. Better is just to register a listener with the table row directly though (see answer). – James_D Oct 25 '14 at 18:46

7 Answers7

147
TableView<MyType> table = new TableView<>();

//...

table.setRowFactory( tv -> {
    TableRow<MyType> row = new TableRow<>();
    row.setOnMouseClicked(event -> {
        if (event.getClickCount() == 2 && (! row.isEmpty()) ) {
            MyType rowData = row.getItem();
            System.out.println(rowData);
        }
    });
    return row ;
});

Here is a complete working example:

import java.util.Random;
import java.util.function.Function;

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.stage.Stage;

public class TableViewDoubleClickOnRow extends Application {

    @Override
    public void start(Stage primaryStage) {
        TableView<Item> table = new TableView<>();
        table.setRowFactory(tv -> {
            TableRow<Item> row = new TableRow<>();
            row.setOnMouseClicked(event -> {
                if (event.getClickCount() == 2 && (! row.isEmpty()) ) {
                    Item rowData = row.getItem();
                    System.out.println("Double click on: "+rowData.getName());
                }
            });
            return row ;
        });
        table.getColumns().add(column("Item", Item::nameProperty));
        table.getColumns().add(column("Value", Item::valueProperty));

        Random rng = new Random();
        for (int i = 1 ; i <= 50 ; i++) {
            table.getItems().add(new Item("Item "+i, rng.nextInt(1000)));
        }

        Scene scene = new Scene(table);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private static <S,T> TableColumn<S,T> column(String title, Function<S, ObservableValue<T>> property) {
        TableColumn<S,T> col = new TableColumn<>(title);
        col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
        return col ;
    }

    public static class Item {
        private final StringProperty name = new SimpleStringProperty();
        private final IntegerProperty value = new SimpleIntegerProperty();

        public Item(String name, int value) {
            setName(name);
            setValue(value);
        }

        public StringProperty nameProperty() {
            return name ;
        }

        public final String getName() {
            return nameProperty().get();
        }

        public final void setName(String name) {
            nameProperty().set(name);
        }

        public IntegerProperty valueProperty() {
            return value ;
        }

        public final int getValue() {
            return valueProperty().get();
        }

        public final void setValue(int value) {
            valueProperty().set(value);
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
James_D
  • 201,275
  • 16
  • 291
  • 322
  • 2
    @Aubin I think since this answer was accepted and has 80+ up votes that it's reasonable to assume that if it's not working for you then there is something wrong with the way you are implementing this. Post your own question, with a [MCVE]. – James_D Dec 31 '17 at 13:54
  • What if I want to use a cell factory instead? I already have code that uses a `PropertyValueFactory` for each field, how can I extend your method to listen for double clicks? Also, I don't understand this part of your row factory: you create a new `TableRow`, call `getItem` on it, then print this item's name? When does actual data get involved? – Hey Feb 12 '18 at 15:13
  • 1
    @Arno A `PropertyValueFactory` is used for a `cellValueFactory`, not a `cellFactory`. You always need a `cellValueFactory` on your `TableColumn`s (note both the columns in the table also have `cellValueFactory`s define on them). You can additionally define `cellFactory`s on some or all of the columns if you need them; they are independent of this. Not sure I understand your last question; the code you are referring to is in the mouse handler. So when the mouse is double-clicked on the row, the handler retrieves the value for that row and displays its name. – James_D Feb 12 '18 at 17:20
  • I just understood the difference between `ValueFactory` and `CellFactory`, thanks. As for my second question, I just missed the context, I understand now. – Hey Feb 12 '18 at 17:31
22

Example:

table.setOnMousePressed(new EventHandler<MouseEvent>() {
    @Override 
    public void handle(MouseEvent event) {
        if (event.isPrimaryButtonDown() && event.getClickCount() == 2) {
            System.out.println(table.getSelectionModel().getSelectedItem());                   
        }
    }
});

If you are using custom selection model, then you can get the row from event, example:

table.setOnMousePressed(new EventHandler<MouseEvent>() {
    @Override 
    public void handle(MouseEvent event) {
        if (event.isPrimaryButtonDown() && event.getClickCount() == 2) {
            Node node = ((Node) event.getTarget()).getParent();
            TableRow row;
            if (node instanceof TableRow) {
                row = (TableRow) node;
            } else {
                // clicking on text part
                row = (TableRow) node.getParent();
            }
            System.out.println(row.getItem());
        }
    }
});
Alexander.Berg
  • 2,225
  • 1
  • 17
  • 18
  • 3
    While this works (because the first click of the double-click selects the row), selection is semantically different attaching a mouse handler to the row. If you needed to use a custom selection model for some reason, it might break this code. – James_D Oct 25 '14 at 18:43
  • @James_D in the question there wasn't written down a custom selection model case, but answer adjusted for this case too. – Alexander.Berg Oct 27 '14 at 01:17
  • That relies instead on a particular node hierarchy (which is undocumented, and purposely so). What if the implementation of the table view layout mechanism changes in a future release of JavaFX? Just attach the listener directly to the table row when it's created. – James_D Oct 27 '14 at 01:23
  • You misunderstood my point a bit, too. The issue wasn't whether or not custom selection models were supported by your code, but really that "selection" and "registering a mouse listener" are two independent things. Using "what's selected" as a mechanism to determine the node on which the mouse was clicked introduces a coupling (dependency) between two parts of the API which didn't previously exist. – James_D Oct 27 '14 at 01:38
  • @James_D I think each answer is correct, your too. The differences are in the use case, which wasn't defined in the question. For example I found myself more times in cases, where I needed to put listeners to components after they where already created and rendered so I needed to add or remove handlers dynamically, not before creation... – Alexander.Berg Oct 28 '14 at 22:25
  • 3
    Thanks, but I had to use `event.getButton().equals(MouseButton.PRIMARY)` instead of `event.isPrimaryButtonDown()` to get mine to work. – Johis May 03 '15 at 23:29
  • (event.isPrimaryButtonDown() && event.getClickCount() == 2) is always false – Aubin Dec 31 '17 at 13:10
5

This works for me:

table.setOnMouseClicked((MouseEvent event) -> {
            if (event.getButton().equals(MouseButton.PRIMARY) && event.getClickCount() == 2){
                System.out.println(table.getSelectionModel().getSelectedItem());
            }
        });
    }
Abdul.Moqueet
  • 902
  • 12
  • 19
3

If you are using SceneBuilder you can set your table's OnMouseClicked to handleRowSelect() method as shown below:

MyType temp;
Date lastClickTime;
@FXML
private void handleRowSelect() {
    MyType row = myTableView.getSelectionModel().getSelectedItem();
    if (row == null) return;
    if(row != temp){
        temp = row;
        lastClickTime = new Date();
    } else if(row == temp) {
        Date now = new Date();
        long diff = now.getTime() - lastClickTime.getTime();
        if (diff < 300){ //another click registered in 300 millis
             System.out.println("Edit dialog");
        } else {
            lastClickTime = new Date();
        }
    }
}
Neuron
  • 5,141
  • 5
  • 38
  • 59
  • you can get event `handleRowSelect(MouseEvent event)` to detect double click with primay mouse button like in @Abdul.Moqueet answer – Adam Silenko Feb 28 '20 at 14:19
1

Extending the previous answer:

The extra check ensures the selected row was double clicked - ignoring double clicks on empty rows or the column header

table.setRowFactory(param -> {
            TableRow<MyType> row = new TableRow<>();
            row.setOnMouseClicked(event -> Optional.ofNullable(row.getItem()).ifPresent(rowData-> {
                if(event.getClickCount() == 2 && rowData.equals(table.getSelectionModel().getSelectedItem())){
                    System.out.println(rowData);
                }
            }));
            return row;
        });
```
0

This answer has been tested:

table.setOnMouseClicked( event -> {
   if( event.getClickCount() == 2 ) {
      System.out.println( table.getSelectionModel().getSelectedItem());
   }});

table.getSelectionModel().getSelectedItem() can be use since we catch a double-click. One the first click the selection moves, on the second this handler is executed.

Aubin
  • 14,617
  • 9
  • 61
  • 84
0

I had similar situation not to detect mouse double click event on TableView. Above all samples worked perfectly. but my application did not detect double click event at all.

But I found that if TableView is on editable, mouse double click event can not be detected !!

check your application if TableView is on editable like this.

tableView.setEditable( true );

if then, double click event only raises on same row selected.

nayasis
  • 11
  • 3