Java 8 个方法引用与 javafx

Java 8 method references with javafx

我刚刚开发了一个 JavaFX 应用程序这二十个不同的页面。每个页面都有一个 table,我想在每个 table 上放置一个上下文菜单。

基本上,将上下文菜单放置到 table 的代码总是相同的,但我希望方法引用可以在这里提供一点帮助。

这是实际的代码片段:

resultTable.setRowFactory(new Callback<TableView<InterfaceModel>, TableRow<InterfaceModel>>() {
    @Override
    public TableRow<InterfaceModel> call(TableView<InterfaceModel> tableView) {
        final TableRow<InterfaceModel> row = new TableRow<InterfaceModel>();

        final ContextMenu rowMenu = new ContextMenu();
        MenuItem editItem = new MenuItem("EDIT");
        editItem.setOnAction(event -> {
            // action if edit was selected
        });

我想要这样的东西:

ContextMenuHelper helper = new ContextMenuHelper(resultTable);
helper.addItem("Edit", [referenceToAMethod]);
helper.addItem("Item 2", [referenceToADifferentMethod]);

我的意思是这个助手创建了上下文菜单。这个助手需要的只是条目的标签和在选择此条目后调用的方法。

使用 java 8 中的方法引用可能吗?

谢谢, 豪克

如果您只想定义一个方法来创建一个 MenuItem,那么这很简单:您只需要决定您需要的功能接口,该接口将采用方法引用(或 lambda)的参数, ETC)。例如。如果方法签名不带参数并且具有 void return 类型,则可以使用 Runnable:

public MenuItem createItem(String text, Runnable handler) {
    MenuItem item = new MenuItem(text);
    item.setOnAction(e -> handler.run());
}

您可能希望菜单项事件处理程序能够访问行中的 table 项,在这种情况下,它需要对该行的引用:

public <T> MenuItem createItem(String text, TableRow<T> row, Consumer<T> handler) {
    MenuItem item = new MenuItem(text);
    item.setOnAction(e -> handler.accept(row.getItem()));
}

那你可以做

TableView<InterfaceModel> table = new TableView<>();
ContextMenuHelper helper = new ContextMenuHelper();
table.setRowFactory(t -> {
    TableRow<InterfaceModel> row = new TableRow<>();
    ContextMenu menu = new ContextMenu();
    row.setContextMenu(menu);
    menu.getItems().addItem(helper.createItem("Edit", row, this::edit));
    // etc...
});

private void edit(InterfaceModel model) {
    // ...
}

你实际上并没有问,但我猜你真的想要的是 "helper" class 实际设置行工厂并创建所有菜单等. 这有点难构造,因为你需要完全在行工厂内部构建上下文菜单,所以你需要知道所有的菜单项,然后才能真正设置行工厂。为此,您可能需要考虑构建器模式:

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.util.Callback;

public class TableRowContextMenuBuilder<T> {
    private final List<MenuItemConfig<T>> items ;
    private boolean built ;

    public TableRowContextMenuBuilder() {
        this.items = new ArrayList<>();
    }


    public static <T> TableRowContextMenuBuilder<T> create(Class<T> type) {
        return new TableRowContextMenuBuilder<>();
    }

    public TableRowContextMenuBuilder<T> addItem(String text, Consumer<T> handler) {
        if (built) {
            throw new IllegalStateException("Row factory is already built: cannot add new items");
        }
        items.add(new MenuItemConfig<T>(text, handler));
        return this ;
    }

    public TableRowContextMenuBuilder<T> addItem(String text, Runnable handler) {
        return addItem(text, t -> handler.run());
    }

    public Callback<TableView<T>, TableRow<T>> build() {
        if (built) {
            throw new IllegalStateException("Cannot build row factory more than once");
        }
        built = true ;
        return t -> {
            TableRow<T> row = new TableRow<>();
            ContextMenu menu = new ContextMenu();
            row.setContextMenu(menu);
            items.stream()
                .map(config -> config.asMenuItem(row))
                .forEach(menu.getItems()::add);
            return row ;
        };
    }

    public void buildForTable(TableView<T> table) {
        table.setRowFactory(build());
    }


    private static class MenuItemConfig<T> {
        private final String text ;
        private final Consumer<T> handler ;
        MenuItemConfig(String text, Consumer<T> handler) {
            this.text = text;
            this.handler = handler;
        }
        MenuItem asMenuItem(TableRow<T> row) {
            MenuItem item = new MenuItem(text);
            item.setOnAction(e -> handler.accept(row.getItem()));
            return item ;
        }
    }
}

现在你可以做到

TableView<InterfaceModel> table = new TableView<>();
TableViewContextMenuBuilder.create(InterfaceModel.class)
    .menuBuilder.addItem("Edit", this::edit);
    .menuBuilder.addItem("Item 2", this::handleOtherItem);
    // ...
    .buildForTable(table);

定义了适当的方法:

private void edit(InterfaceModel model) { /* ... */}
private void handleOtherItem(InterfaceModel model) { /* ... */}