有没有办法在JavaFX中生成上下文菜单并以功能方式获取单击的MenuItem
或其关联数据?我希望能够做到这样的事情:
MenuItem menuItem1 = new MenuItem("Item 1");
menuItem1.setUserData(1);
MenuItem menuItem2 = new MenuItem("Item 2");
menuItem1.setUserData(2);
ContextMenu menu = new ContextMenu(menuItem1, menuItem2);
Integer result = menu.show(...)
if (result == 1)
...
else if (result == 2)
...
else
...
但据我所知,没有办法模仿result = menu.show()
行。有办法吗?我希望这是阻塞/同步;不像JavaFX那样基于事件本身。
答案 0 :(得分:2)
你可以。需要注意的是,由于这样的方法阻塞,它必须在后台线程中运行(这使得事情有点难看)。
以下是一个例子:
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class FunctionalContextMenu extends Application {
@Override
public void start(Stage primaryStage) {
Pane root = new Pane();
ContextMenu menu = new ContextMenu();
MenuItem item1 = new MenuItem("Item 1");
MenuItem item2 = new MenuItem("Item 2");
menu.getItems().addAll(item1, item2);
root.setOnContextMenuRequested(e -> {
showMenu(menu, root, e.getScreenX(), e.getScreenY());
});
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private void showMenu(ContextMenu menu, Node anchor, double screenX, double screenY) {
new Thread(() ->
showAndWait(menu, anchor, screenX, screenY)
.ifPresent(item -> System.out.println("You chose "+item.getText()))
).start();
}
private Optional<MenuItem> showAndWait(ContextMenu menu, Node anchor, double screenX, double screenY) {
// executing this on the FX Application Thread would cause deadlock,
// so guard against it:
if (Platform.isFxApplicationThread()) {
throw new IllegalStateException("showAndWait cannot be called from the FX Application Thread");
}
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<MenuItem> selectedItem = new AtomicReference<>();
Platform.runLater(() -> {
EventHandler<ActionEvent> handler = e -> selectedItem.set((MenuItem)e.getSource());
menu.setOnHidden(e -> {
for (MenuItem item : menu.getItems()) {
item.removeEventHandler(ActionEvent.ACTION, handler);
}
latch.countDown();
});
for (MenuItem item : menu.getItems()) {
item.addEventHandler(ActionEvent.ACTION, handler);
}
menu.show(anchor, screenX, screenY);
});
try {
latch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return Optional.ofNullable(selectedItem.get());
}
public static void main(String[] args) {
launch(args);
}
}
答案 1 :(得分:0)
这是我结束的地方,感谢https://stackoverflow.com/a/27081164/2270967
exitNestedEventLoop
和com.sun
方法位于import com.sun.javafx.tk.Toolkit;
import javafx.scene.Node;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
public class FunctionalContextMenu extends ContextMenu {
/**
* Create a new FunctionalContextMenu initialized with the given items
*
* @param items the initial menu items
*/
public FunctionalContextMenu(MenuItem... items) {
super(items);
}
/**
* Shows the context menu and waits until the user clicks an item or
* otherwise closes the menu. See
* {@link #show(javafx.scene.Node, double, double)} for more details
* regarding the display of the menu.
*
* @param anchor
* @param screenX
* @param screenY
* @return the clicked menu item, or {@code null} if the menu was closed
* without selecting a menu item
*/
public MenuItem showAndWait(Node anchor, double screenX, double screenY) {
super.show(anchor, screenX, screenY);
Object lock = new Object();
this.setOnAction(actionEvt -> Toolkit.getToolkit().exitNestedEventLoop(lock, actionEvt.getTarget()));
this.setOnAutoHide(autohideEvt -> Toolkit.getToolkit().exitNestedEventLoop(lock, null));
super.show(anchor, screenX, screenY);
return (MenuItem) Toolkit.getToolkit().enterNestedEventLoop(lock);
}
}
,但slated for inclusion为Java 9的公共API。
{{1}}