功能范例中的JavaFX ContextMenu

时间:2016-04-28 23:38:00

标签: javafx functional-programming contextmenu

有没有办法在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那样基于事件本身。

2 个答案:

答案 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

exitNestedEventLoopcom.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}}