java - 如何在 JavaFX 中递归添加菜单和子菜单?

标签 java recursion javafx

我已经创建了扩展 javafx.scene.control.MenuBarCustomMenuBar 类,我想要实现的是仅通过 String 添加新菜单 值如以下代码的 start 方法所示:

package recursivemenu;

import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class RecursivelyAddMenuAndSubMenu extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {

        String menu1 = "File > Open";
        String menu2 = "File > Close";
        String menu3 = "File > Recently closed > File1";
        String menu4 = "File > Recently closed > File2";
        String menu5 = "File > Recently closed > File3";
        String menu6 = "File > Recently closed > File4";
        String menu7 = "File > Recently closed > File5";

        CustomMenuBar customMenuBar = new CustomMenuBar();
        customMenuBar.addMenu(menu1);
        customMenuBar.addMenu(menu2);
        customMenuBar.addMenu(menu3);
        customMenuBar.addMenu(menu4);
        customMenuBar.addMenu(menu5);
        customMenuBar.addMenu(menu6);
        customMenuBar.addMenu(menu7);

        BorderPane borderPane = new BorderPane();
        borderPane.setTop(customMenuBar);
        Scene scene = new Scene(borderPane);

        primaryStage.setScene(scene);
        primaryStage.setMaximized(true);
        primaryStage.show();
    }

    class CustomMenuBar extends MenuBar {

        void addMenu(String menu) {

            String[] menuChain = splitMenus(menu);

            // Add new top menu if not exists.
            if (getMenu(menuChain[0], getMenus()) == null)
                createMenu(menuChain[0]);

            // Adding sub menus
            Menu topMenu = getMenu(menuChain[0], getMenus());

            if (topMenu.getItems().isEmpty()) {
                addSubMenu(topMenu, menuChain[1]);
            } else {
                // Add sub menu if not exists.
                if (getItem(menuChain[1], topMenu.getItems()) == null)
                    createSubMenu(menuChain[1], topMenu);
            }
        }

        private void createSubMenu(String subMenuText, Menu menu) {
            menu.getItems().add(new MenuItem(subMenuText));
        }

        private MenuItem getItem(String subMenuText, ObservableList<MenuItem> items) {

            for (MenuItem item : items) {
                if (item.getText().equals(subMenuText)) {
                    return item;
                }
            }
            return null;
        }

        private void addSubMenu(Menu topMenu, String subMenuText) {
            topMenu.getItems().add(new MenuItem(subMenuText));
        }

        private void createMenu(String menuText) {
            getMenus().add(new Menu(menuText));
        }

        private Menu getMenu(String menuText, ObservableList<Menu> menus) {
            for (Menu menu : menus) {
                if (menu.getText().equals(menuText))
                    return menu;
            }
            return null;
        }

        private String[] splitMenus(String menuText) {

            String[] menuChain = menuText.split("\\s*>\\s*");
            for (int i = 0; i < menuChain.length; i++)
                menuChain[i] = menuChain[i].trim();
            return menuChain;
        }
    }
}

字符串格式应类似于Menu > SubMenu > SubMenu > etc。 这是一个示例,我只能添加带有一个子菜单的菜单。我被困在这里,不知道如何让 addMenu 方法递归添加所有菜单和子菜单。

最终结果应该是这样的:

编辑:
菜单和子菜单不应重复,并且必须保持层次结构顺序。

编辑2:
CustomMenuBar 应该能够添加任意长度的嵌套子菜单。

例如
它应该与 Menu > SubMenu1 > SubMenu2 > SubMenu3
配合使用 它应该与 Menu > SubMenu1 > SubMenu2 > SubMenu3 > SubMenu4 配合使用 也是。
还有 Menu > SubMenu1 > SubMenu2 > SubMenu3 > SubMenu4 > ... > ...> 等

最佳答案

我尽量保持简单。我会把优化留给你。您可以在代码中找到注释,了解有关整个过程和逻辑的更多信息。

import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class RecursivelyAddMenuAndSubMenu extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {

        String menu1 = "File > Open";
        String menu2 = "File > Close";
        String menu3 = "File > Recently closed > File1";
        String menu4 = "File > Recently closed > File2";
        String menu5 = "File > Recently closed > File3";
        String menu6 = "File > Recently closed > File4";
        String menu7 = "File > Recently closed > File5";
        String menu8 = "File > Recently closed > File5 > something";

        CustomMenuBar customMenuBar = new CustomMenuBar();
        customMenuBar.addMenu(menu1);
        customMenuBar.addMenu(menu2);
        customMenuBar.addMenu(menu3);
        customMenuBar.addMenu(menu4);
        customMenuBar.addMenu(menu5);
        customMenuBar.addMenu(menu6);
        customMenuBar.addMenu(menu7);
        customMenuBar.addMenu(menu8);

        BorderPane borderPane = new BorderPane();
        borderPane.setTop(customMenuBar);
        Scene scene = new Scene(borderPane);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    class CustomMenuBar extends MenuBar {

        private Menu currentMenu;
        private Menu head;

        void addMenu(String menu) {
            String tokens[] = splitMenusInHalf(menu);

            // we found something like "x -> y ... "
            if (tokens.length > 1) {

                // search for the current root if it contains
                // the menu we are about to create
                currentMenu = this.getMenu(tokens[0], head);
                boolean isAdded = true;

                // if not create it
                if (currentMenu == null) {
                    currentMenu = new Menu(tokens[0]);
                    isAdded = false;
                }

                // find out if there was a previous Menu created
                // if so the current is a sub-menu of the previous one
                if (head == null) {
                    head = currentMenu;
                    if (!isAdded) {
                        this.getMenus().add(currentMenu);
                    }
                } else {
                    if (!isAdded) {
                        // otherwise add the current Menu as sub-menu
                        head.getItems().add(currentMenu);
                    }
                    // set the Current "head" the sub-menu
                    head = currentMenu;
                }
                // Recursive check for more menus or menuItems
                addMenu(tokens[1]);
            } else {
                // If found only something like "x" which is MenuItem
                currentMenu.getItems().add(new MenuItem(tokens[0]));
                // reset everything for the next addMenu call
                currentMenu = null;
                head = null;
            }
        }

        private Menu getMenu(String menuText, Menu root) {
            if (root == null) {
                ObservableList<Menu> allMenus = this.getMenus();
                for (Menu m : allMenus) {
                    if (m.getText().equals(menuText)) {
                        return m;
                    }
                }
            } else {
                ObservableList<MenuItem> allMenus = root.getItems();
                for (MenuItem m : allMenus) {
                    if (m.getText().equals(menuText)) {
                        // We are about to convert MenuItem to Menu
                        if (!(m instanceof Menu)) {

                            // Get the previous menuItem location
                            int index = allMenus.indexOf(m);
                            // Remove it
                            allMenus.remove(m);
                            // Create a new Menu with the previous MenuItem text
                            m = new Menu(menuText);
                            // Add it to the correct location
                            allMenus.add(index, m);
                        }
                        return (Menu) m;
                    }
                }
            }
            return null;
        }

        private String[] splitMenusInHalf(String menuText) {
            String[] menuChain = menuText.split("\\s*>\\s*", 2);
            for (int i = 0; i < menuChain.length; i++)
                menuChain[i] = menuChain[i].trim();
            return menuChain;
        }
    }
}

正如 OP 所要求的那样,如果用户尝试在其上添加子菜单或 MenuItem,getMenu() 现在会将任何先前的 MenuItem 转换为 Menu。

关于java - 如何在 JavaFX 中递归添加菜单和子菜单?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47035344/

相关文章:

java - 递归获取列表元素

haskell - 定义一个函数,将给定列表的所有可能排列返回为对列表

java - 从可穿戴设备发送消息到手机,然后立即回复

java - 从 SQL JPA native 查询迭代结果时出现问题

java - 插入加号时查找数字的所有组合

java - 如何在 JavaFX 中将 ObjectProperty<String> 转换为 StringProperty?

java - 为什么 SSL 在构建 jar 中不起作用?

java - VBox拖动闪烁

java - java中显式创建线程导致的问题

java - 通过连续自然数的加法或减法获得一个数