2017-01-10 9 views
1

この質問があなたの立場では意味をなさないが、私の見解では、これはきれいで保守的なコードを書くことが本当に重要です。JavaFXカスタムセルファクトリの使用時にイベントを正しく処理する方法は?

私はA.fxml、AController.java(A.fxmlのコントローラクラス)を持っています。カスタムセルファクトリを定義したTableViewがあります。別のクラスのセルファクトリを定義して、必要に応じて再利用できるようにすることをお勧めします。私は自分のイベントハンドリングコードを自分のコントローラークラスに書く方が好きです。しかし、カスタムセルファクトリを使用すると、セルファクトリクラス自体にイベント処理を記述する必要があります。

コントローラクラス自体のカスタムセルファクトリイベントを処理する方法はありますか?またはちょうどちょうど私のコントローラクラスとハンドルにカスタムセルファクトリクラスからイベントをスロー?

ありがとうございました。 ?

答えて

3

それはあなたのイベント処理をパラメータとして、あなたの細胞工場FunctionalInterfaceを与えることができることwouldntの(良いアイデアカントー場合はわからない)

私は以下のようにコードを想像:

コントローラ:

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. cellFactoryをfxmlから設定するとどうなりますか? 3.セルファクトリクラスとセルクラスが別々に実装されている場合、同じことを行う方法。私は、TableCellを返すButtonCellクラスを持っており、ButtonCellを呼び出すButtonCellFactoryクラスがあることを意味します。「return 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

時間をとっていただきありがとうございます。あなたの答えは働いています。しかし、私は完全に理解しているか分からない。コードを読み、理解しようとしましょう。ありがとう –

関連する問題