2016-04-28 33 views
0

有沒有辦法在JavaFX中生成上下文菜單,並以功能性方式獲取點擊的MenuItem或其相關數據?我希望能夠做這樣的事情:功能範例中的JavaFX ContextMenu

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

這裏就是我結束了,感謝https://stackoverflow.com/a/27081164/2270967

enterNestedEventLoopexitNestedEventLoop方法在com.sunslated for inclusion整合到Java 9

公共API
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); 
    } 
} 
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); 
    } 
} 
+0

謝謝!仔細閱讀JavaFX文檔,我想我可以使用[Task](http://docs.oracle.com/javafx/2/api/javafx/concurrent/Task.html)將該線程完全封裝在內部showAndWait()。 –