【问题标题】:Java 8 method references with javafx带有 javafx 的 Java 8 方法引用
【发布时间】:2024-01-09 04:49:01
【问题描述】:

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

基本上,将上下文菜单放置到表格中的代码始终相同,但我希望方法引用可以在这里有所帮助。

这是实际的代码sn-p:

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 中的方法引用可以实现吗?

谢谢, 豪克

【问题讨论】:

  • 是的,有可能。

标签: java javafx lambda java-8 method-reference


【解决方案1】:

如果您只想定义一个用于创建MenuItem 的方法,那么这很容易:您只需要确定需要方法引用(或 lambda 等)的参数所需的功能接口。例如。如果方法签名没有参数并且有void返回类型,你可以使用Runnable:

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

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

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) {
    // ...
}

你实际上并没有问什么,但我猜你真正想要的是让“帮助”类实际设置行工厂并创建所有菜单等。这有点难以构建,因为你需要在行工厂内部完全构建上下文菜单,所以你需要知道所有的菜单项才能真正设置行工厂。为此,您可能需要考虑构建器模式:

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) { /* ... */}

【讨论】:

  • 简直令人难以置信。谢谢,你的猜测是正确的:-)
最近更新 更多