2017-01-10 23 views
1

我真的很抱歉,如果这个问题在你的观点上没有多大意义,但在我看来,编写更清洁和更易维护的代码非常重要。JavaFX如何在使用自定义单元工厂时正确处理事件?

我有A.fxml,AController.java(A.fxml的控制器类)。我有一个定制自定义单元格工厂的TableView。我更喜欢在单独的课程中定义我的细胞工厂,以便在需要时可以重用它们。我更喜欢在我的控制器类中编写所有事件处理代码。但是,如果我使用自定义单元工厂,那么我不得不在单元工厂类本身中编写事件处理。

有没有一种方法可以处理我的控制器类本身的自定义单元工厂事件?或者至少只是将事件从自定义单元工厂类引发到我的控制器类并处理?

在此先感谢。

回答

3

难道不能够与你的事件处理,让您的电池工厂FunctionalInterface作为参数(不知道是否是个好主意寿)

我想,你的代码如下:?

控制器:

myTableView.setCellFactory(new MyOwnCellFactory<>(() -> { 
    // event handling 
})); 

MyOwnCellFactory:

public MyOwnCellFactory(MyFunctionalInterface myInterface) { 
    functionalInterface = myInterface; 
} 

// something something 

functionalInterface.handleEvent(); 

Func键tionalInterface:

@FunctionalInterface 
public interface MyFunctionalInterface { 
    public void handleEvent(); 
} 

不知道我是否理解你的想法是正确的。没有测试代码,只是写了我的头。

+0

感谢您抽出时间。我试过你的解决方案,它正在工作。几乎没有疑问。如你所说,这是一个好主意吗?任何副作用? 2.如果我从fxml设置cellFactory会怎么样? 3.如果我有我的细胞工厂类和单元类分开实施,如何做到这一点。我的意思是我有一个ButtonCell类,它返回TableCell,我有ButtonCellFactory类,它只是调用ButtonCell,如“返回新的ButtonCell();”来自ButtonCellFactory类的call()方法。谢谢! –

+0

1)不确定副作用。至少你不能在你的功能界面中有多个事件处理程序。因此,如果您想拥有不同的事件处理程序,您需要更改它。 2)猜测这种方法不能很好地工作 3)我想你仍然可以将你的FunctionalInterface对象向下转发到单元格,并做任何你想要的东西(调用事件处理程序或其他) 最后,只是一个简单易懂的方法。但其他答案可能更好:) – NDY

4

您可以传递一个对象到工厂,确定何时应打开上下文菜单并准备菜单。

实施例:

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属性的按钮,这将允许您传递控制器的事件处理程序,这将导致更短/更简单的代码。

+0

感谢您抽出时间。你的答案正在起作用。但我不确定我完全理解。让我通读代码并尝试理解。谢谢 –

相关问题