オブジェクトを工場に渡すと、コンテキストメニューを開いてメニューを準備するタイミングを決定できます。
例:
public interface CellContextMenuProvider<S, T> {
/**
* Prepares the context menu for opening.
* @param cell the cell the menu was requested for
* @param menu the menu to prepare
*/
public void prepareContextMenu(TableCell<S, T> cell, ContextMenu menu);
/**
* Checks, if a cell continaing a certain item should have an active context
* menu.
* @param empty if the cell is empty
* @param item the item of the cell
* @return {@literal true} iff the context menu should be enabled.
*/
public boolean enableContextMenu(boolean empty, T item);
/**
* Prepares the intial menu. This menu must not be empty, otherwise it won't
* be shown when it's requested for the first time.
* @param menu the menu to prepare
*/
public void prepareInitialContextMenu(ContextMenu menu);
}
public class CellFactory<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> {
private final CellContextMenuProvider<S, T> menuProvider;
private final ContextMenu contextMenu;
public CellFactory(@NamedArg("menuProvider") CellContextMenuProvider<S, T> menuProvider) {
this.menuProvider = menuProvider;
if (menuProvider == null) {
this.contextMenu = null;
} else {
this.contextMenu = new ContextMenu();
menuProvider.prepareInitialContextMenu(contextMenu);
}
this.menuEventHandler = evt -> {
if (this.contextMenu != null) {
TableCell<S, T> source = (TableCell<S, T>) evt.getSource();
this.menuProvider.prepareContextMenu(source, this.contextMenu);
}
};
}
public CellFactory() {
this(null);
}
private final EventHandler<ContextMenuEvent> menuEventHandler;
@Override
public TableCell<S, T> call(TableColumn<S, T> param) {
TableCell<S, T> result = new TableCell<S, T>() {
@Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
setText(Objects.toString(item, ""));
setContextMenu(menuProvider != null && menuProvider.enableContextMenu(empty, item) ? contextMenu : null);
}
};
result.setOnContextMenuRequested(menuEventHandler);
if (menuProvider != null && menuProvider.enableContextMenu(true, null)) {
result.setContextMenu(contextMenu);
}
return result;
}
}
public class AController {
@FXML
private TableView<Item<Integer>> table;
public void initialize() {
for (int i = 0; i < 100; i++) {
table.getItems().add(new Item<>(i));
}
}
public CellContextMenuProvider<Item<Integer>, Integer> getMenuProvider() {
return new CellContextMenuProvider<Item<Integer>, Integer>() {
private final MenuItem item = new MenuItem("Say Hello World");
{
item.setOnAction(evt -> System.out.println("Hello World"));
}
@Override
public void prepareContextMenu(TableCell<Item<Integer>, Integer> cell, ContextMenu menu) {
}
@Override
public void prepareInitialContextMenu(ContextMenu menu) {
menu.getItems().setAll(item);
}
@Override
public boolean enableContextMenu(boolean empty, Integer item) {
// only for odd items
return !empty && (item % 2) != 0;
}
};
}
}
A.fxml
<TableView fx:id="table" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fxml.table.AController">
<columns>
<TableColumn prefWidth="159.0" text="C1">
<cellValueFactory>
<PropertyValueFactory property="value" />
</cellValueFactory>
<cellFactory>
<CellFactory menuProvider="$controller.menuProvider"/>
</cellFactory>
</TableColumn>
</columns>
</TableView>
注:コンテキストメニューが常に同じ場合は、EventHandler
プロパティを工場に追加して、それらをたとえば次のように使用することもできます。 onAction
ボタンの属性を使用すると、コントローラのイベントハンドラを渡すことができ、コードが短く/簡単になります。
時間をいただきありがとうございます。私はあなたのソリューションを試して、それは働いています。疑いはほとんどありません。あなたが言及したように、良いアイデアですか?任意の副作用ですか? 2. cellFactoryをfxmlから設定するとどうなりますか? 3.セルファクトリクラスとセルクラスが別々に実装されている場合、同じことを行う方法。私は、TableCellを返すButtonCellクラスを持っており、ButtonCellを呼び出すButtonCellFactoryクラスがあることを意味します。「return ButtonCell();」 ButtonCellFactoryクラスのcall()メソッドから取得します。ありがとう! –
1)副作用についてはわかりません。少なくとも、あなたの機能インタフェースには複数のイベントハンドラがありません。したがって、異なるイベントハンドラを使用する場合は、変更する必要があります。 2)このアプローチでうまく動作していないと思うでしょう 3)あなたのFunctionalInterfaceオブジェクトをセルに転送して、それに何でもしたいと思うものはありますか?(イベントハンドラなどに電話してください) ちょっとストレートで理解しやすいアプローチです。しかし、他の答えはおそらくより良いです:) – NDY