Поэтому я последовал этому примеру при использовании контекстного меню с TableViews из здесь. Я заметил, что с помощью этого кода
row.contextMenuProperty().bind(Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(rowMenu)
.otherwise((ContextMenu)null));
не отображается при первом щелчке правой кнопкой мыши по строке со значениями. Мне нужно снова щелкнуть правой кнопкой мыши по этой строке, чтобы появилось контекстное меню. Я также попробовал этот код (это мой первый подход, но я больше его не использую, потому что я где-то читал, что руководство это является лучшей / хорошей практикой для всего, что связано с контекстным меню и представлением таблицы), и он немедленно отображает контекстное меню
if (row.getItem() != null) {
rowMenu.show(row, event.getScreenX(), event.getScreenY());
}
else {
// do nothing
}
но моя проблема с этим кодом заключается в том, что он генерирует исключение NullPointerException всякий раз, когда я пытаюсь щелкнуть правой кнопкой мыши строку, в которой нет данных.
Что я могу сделать, чтобы предотвратить NullPointerException, когда контекстное меню появляется сразу после щелчка правой кнопкой мыши? В моем коде у меня также есть код, в котором определенный пункт меню в контекстном меню будет отключен на основе свойства myObject, привязанного к строке, поэтому мне нужно, чтобы контекстное меню всплывало сразу.
Я тоже заметил это с первым блоком кода. Даже если свойство myObject уже изменилось, в нем по-прежнему включен/отключен пункт меню, если я снова не щелкну правой кнопкой мыши по этой строке. Я надеюсь, что вы могли бы мне помочь. Спасибо!
Вот MCVE:
public class MCVE_TableView extends Application{
@Override
public void start(Stage primaryStage) throws Exception {
BorderPane myBorderPane = new BorderPane();
TableView<People> myTable = new TableView<>();
TableColumn<People, String> nameColumn = new TableColumn<>();
TableColumn<People, Integer> ageColumn = new TableColumn<>();
ContextMenu rowMenu = new ContextMenu();
ObservableList<People> peopleList = FXCollections.observableArrayList();
peopleList.add(new People("John Doe", 23));
nameColumn.setMinWidth(100);
nameColumn.setCellValueFactory(
new PropertyValueFactory<>("Name"));
ageColumn.setMinWidth(100);
ageColumn.setCellValueFactory(
new PropertyValueFactory<>("Age"));
myTable.setItems(peopleList);
myTable.getColumns().addAll(nameColumn, ageColumn);
myTable.setRowFactory(tv -> {
TableRow<People> row = new TableRow<>();
row.setOnContextMenuRequested((event) -> {
People selectedRow = row.getItem();
rowMenu.getItems().clear();
MenuItem sampleMenuItem = new MenuItem("Sample Button");
if (selectedRow != null) {
if (selectedRow.getAge() > 100) {
sampleMenuItem.setDisable(true);
}
rowMenu.getItems().add(sampleMenuItem);
}
else {
event.consume();
}
/*if (row.getItem() != null) { // this block comment displays the context menu instantly
rowMenu.show(row, event.getScreenX(), event.getScreenY());
}
else {
// do nothing
}*/
// this requires the row to be right clicked 2 times before displaying the context menu
row.contextMenuProperty().bind(Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(rowMenu)
.otherwise((ContextMenu)null));
});
return row;
});
myBorderPane.setCenter(myTable);
Scene scene = new Scene(myBorderPane, 500, 500);
primaryStage.setTitle("MCVE");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main (String[] args) {
launch(args);
}
} Вот класс людей
public class People {
SimpleStringProperty name;
SimpleIntegerProperty age;
public People(String name, int age) {
this.name = new SimpleStringProperty(name);
this.age = new SimpleIntegerProperty(age);
}
public SimpleStringProperty NameProperty() {
return this.name;
}
public SimpleIntegerProperty AgeProperty() {
return this.age;
}
public String getName() {
return this.name.get();
}
public int getAge() {
return this.age.get();
}
}
Обновлено: добавлен MCVE
Edit2: обновлен MCVE. По-прежнему требуется дважды щелкнуть правой кнопкой мыши, прежде чем появится всплывающее меню contextMenu.
спасибо :) NPE возникает из-за того, что не проверяется, является ли selectedItem нулевым (перед доступом к его getAge).. понятия не имею, почему он не отображается при первом щелчке в непустой строке, может быть ошибка
возможно, это не ошибка: вы, кажется, заново создаете contextMenu при каждом запросе - делайте это один раз во время создания экземпляра и привязывайте этот единственный (для каждой строки) contextMenu к его contextMenuProperty, если он не пуст.
После создания contextMenu во время создания экземпляра, как я могу использовать это и связать одиночное (для каждой строки) contextMenu? if (row.contextMenuProperty() != null) { row.getContextMenu().addAll(edit, merge, unmergeSchedule, delete); }
. Я не знаю, как я могу использовать contextMenu, созданный во время создания экземпляра.
Что я пытался сделать, так это создать contextMenu во время создания экземпляра и очищать элементы внутри этого меню каждый раз, когда я выбираю строку. У меня стойкое ощущение, что я делаю это неправильно..
Я обновил MCVE. NPE теперь отсутствует, так как я последовал одному из ваших предложений (проверьте, имеет ли selectedItem значение null). contextMenu по-прежнему появляется после двойного щелчка правой кнопкой мыши, даже если contextMenu создается во время создания экземпляра.
Ниже приведен фрагмент кода в качестве быстрой демонстрации того, как и где создавать и настраивать контекстное меню для каждой строки. Это
Фрагмент:
myTable.setRowFactory(tv -> {
TableRow<People> row = new TableRow<>() {
ContextMenu rowMenu = new ContextMenu();
MenuItem sampleMenuItem = new MenuItem("Sample Button");
{
rowMenu.getItems().addAll(sampleMenuItem);
contextMenuProperty()
.bind(Bindings
.when(Bindings.isNotNull(itemProperty()))
.then(rowMenu).otherwise((ContextMenu) null));
rowMenu.setOnShowing(e -> {
People selectedRow = getItem();
sampleMenuItem.setDisable(selectedRow.getAge() > 100);
});
}
};
return row;
});
Привет! Я только что добавил MCVE. Спасибо