美文网首页
如何通过Tab的text属性管理TabPane中的Tab?

如何通过Tab的text属性管理TabPane中的Tab?

作者: Huangjs1994 | 来源:发表于2018-04-10 20:32 被阅读157次

    写在前面

    有时候会有这种需求:点击同一个标签或者按钮时打开同一个标签页(Tab),点击不同的标签或者按钮打开不同的标签页。例如编辑器中打开同一个文件,对应打开同一个标签页,点击不同的文件打开不同的标签页。
    但是,javafx中的TabPane没有提供通过text来管理Tab的方法——依据属性text获得标签页;依据属性text删除标签页。

    解决方案

    那么如何解决上面的问题?我们可以在属性textTab之间建立起联系,所以理所当然的想到了哈希。

    简易实现

    新建一个HashMap变量用于存储textTab之间的映射,在向TabPane中加入Tab时,在哈希中增加textTab映射

    package pre.huangjs.tabpane;
    
    import javafx.application.Application;
    import javafx.scene.control.Tab;
    import javafx.scene.control.TabPane;
    import javafx.stage.Stage;
    
    import java.util.HashMap;
    
    /**
     * Created by huangjs on 2018/4/9.
     */
    public class TabPaneTest extends Application {
    
        public void start(Stage primaryStage) throws Exception{
    
            TabPane tabPane = new TabPane();
            HashMap<String, Tab> tabsMap = new HashMap<>();
    
            // 向TabPane中加入Tab时,在哈希中建立属性text和Tab的联系
            Tab tab1 = new Tab("tab I");
            tabsMap.put(tab1.getText(), tab1);
            tabPane.getTabs().add(tab1);
    
            // 当想使用text的值取得相应的Tab时,就从哈希中取得
            Tab tab1Backup = tabsMap.get("tab I");
            System.out.println("tab1Backup == tab1: " + (tab1Backup == tab1));
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    但是这样做的话,每次添加新的Tab时,就需要手动添加一个Tab,当然删除时也需要手动删除HashMap中的Tab。所以我们可以新建一个辅助类来完成这些工作。
    TabPaneHelper.java

    package pre.huangjs.tabpane;
    
    import javafx.scene.control.Tab;
    import javafx.scene.control.TabPane;
    
    import java.util.HashMap;
    
    /**
     * Created by huangjs on 2018/4/10.
     */
    public class TabPaneHelper {
        private TabPane tabPane;
        private HashMap<String, Tab> tabsMap;
    
        public TabPane getTabPane() {
            return tabPane;
        }
    
        public HashMap<String, Tab> getTabsMap() {
            return tabsMap;
        }
    
        public TabPaneHelper() {
            this.tabPane = new TabPane();
            this.tabsMap = new HashMap<>();
        }
    
        /**
         * 添加一个Tab到TabPane中
         * @param tab
         */
        public void addTab(Tab tab) {
            tabPane.getTabs().add(tab);
            tabsMap.put(tab.getText(), tab);
        }
    
        /**
         * 添加多个Tab到TabPane
         * @param tabs
         */
        public void addTabs(Tab... tabs) {
            boolean flag = tabPane.getTabs().addAll(tabs);
            if (flag == true) {
                for (Tab tab : tabs) {
                    tabsMap.put(tab.getText(), tab);
                }
            }
        }
    
        /**
         * 删除一个TabPane
         * @param tab
         */
        public void removeTab(Tab tab) {
            tabPane.getTabs().remove(tab);
            tabsMap.remove(getTabByText(tab.getText()));
        }
    
        /**
         * 通过Tab的text获取对应的Tab
         * @param text
         * @return
         */
        public Tab getTabByText(String text) {
            return tabsMap.get(text);
        }
    }
    

    接下来测试一下,不能直接写一个main方法然后测试,会报错。需要新建一个类继承Application,然后测试。
    测试类TabPaneTest

    package pre.huangjs.tabpane;
    
    import javafx.application.Application;
    import javafx.scene.control.Tab;
    import javafx.stage.Stage;
    
    /**
     * Created by huangjs on 2018/4/10.
     */
    public class TabPaneHelperTest extends Application {
        @Override
        public void start(Stage primaryStage) throws Exception {
            TabPaneHelper tp = new TabPaneHelper();
            Tab tab1 = new Tab("tab1");
            Tab tab2 = new Tab("tab2");
            Tab tab3 = new Tab("tab3");
            Tab tab4 = new Tab();
    
            // add tabs
            tp.addTabs(tab1, tab2);
            System.out.println("**************************华丽分割线1*****************************");
            System.out.println(tp.getTabPane().getTabs());
    
            // remove tab
            tp.removeTab(tab1);
            System.out.println("**************************华丽分割线2*****************************");
            System.out.println(tp.getTabPane().getTabs());
    
            tp.removeTab(tab3);
            System.out.println("**************************华丽分割线3*****************************");
            System.out.println(tp.getTabPane().getTabs());
    
            // add a tab which text is null
            tp.addTab(tab4);
            System.out.println(tp.getTabPane().getTabs());
            System.out.println("**************************华丽分割线4*****************************");
            System.out.println(tp.getTabsMap());
            System.out.println(tp.getTabByText(null));// 可以添加没有设定text值的Tab,也可以获得,但是这样没有什么意义~~原本想通过text管理Tab,都没有text...
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    结果

    E:\software\jdk1.8.0_121\bin\java -Didea.launcher.port=7546 -Didea.launcher.bin.path=E:\software\IDEA\bin -Dfile.encoding=UTF-8 -classpath E:\software\jdk1.8.0_121\jre\lib\charsets.jar;E:\software\jdk1.8.0_121\jre\lib\deploy.jar;E:\software\jdk1.8.0_121\jre\lib\ext\access-bridge-64.jar;E:\software\jdk1.8.0_121\jre\lib\ext\cldrdata.jar;E:\software\jdk1.8.0_121\jre\lib\ext\dnsns.jar;E:\software\jdk1.8.0_121\jre\lib\ext\jaccess.jar;E:\software\jdk1.8.0_121\jre\lib\ext\jfxrt.jar;E:\software\jdk1.8.0_121\jre\lib\ext\localedata.jar;E:\software\jdk1.8.0_121\jre\lib\ext\nashorn.jar;E:\software\jdk1.8.0_121\jre\lib\ext\sunec.jar;E:\software\jdk1.8.0_121\jre\lib\ext\sunjce_provider.jar;E:\software\jdk1.8.0_121\jre\lib\ext\sunmscapi.jar;E:\software\jdk1.8.0_121\jre\lib\ext\sunpkcs11.jar;E:\software\jdk1.8.0_121\jre\lib\ext\zipfs.jar;E:\software\jdk1.8.0_121\jre\lib\javaws.jar;E:\software\jdk1.8.0_121\jre\lib\jce.jar;E:\software\jdk1.8.0_121\jre\lib\jfr.jar;E:\software\jdk1.8.0_121\jre\lib\jfxswt.jar;E:\software\jdk1.8.0_121\jre\lib\jsse.jar;E:\software\jdk1.8.0_121\jre\lib\management-agent.jar;E:\software\jdk1.8.0_121\jre\lib\plugin.jar;E:\software\jdk1.8.0_121\jre\lib\resources.jar;E:\software\jdk1.8.0_121\jre\lib\rt.jar;D:\workspace\coding\java\javafx-in-action\target\test-classes;D:\workspace\coding\java\javafx-in-action\target\classes;E:\software\IDEA\lib\idea_rt.jar com.intellij.rt.execution.application.AppMain pre.huangjs.tabpane.TabPaneHelperTest
    **************************华丽分割线1*****************************
    [javafx.scene.control.Tab@269f4b07, javafx.scene.control.Tab@2a01737]
    **************************华丽分割线2*****************************
    [javafx.scene.control.Tab@2a01737]
    **************************华丽分割线3*****************************
    [javafx.scene.control.Tab@2a01737]
    [javafx.scene.control.Tab@2a01737, javafx.scene.control.Tab@23de4bc6]
    **************************华丽分割线4*****************************
    {null=javafx.scene.control.Tab@23de4bc6, tab1=javafx.scene.control.Tab@269f4b07, tab2=javafx.scene.control.Tab@2a01737}
    javafx.scene.control.Tab@23de4bc6
    
    

    Yeah! we are successful!
    但是这个辅助类需要完善:

    • 添加Tab时的方法addTab()addtabs()没有针对添加是否成功做出提示,其实就是想仿照List的add()方法
    • 这里开始我想在添加Tab时判断text是否为空,但是我觉得没有必要,因为这是一个通过text来管理Tab的类,text都为null,那还何谈通过text来管理?

    其他的实现

    上面是一种实现方法,这里在给出另一种实现方法:

    package pre.huangjs.tabpane;
    
    import javafx.collections.ListChangeListener;
    import javafx.scene.control.Tab;
    import javafx.scene.control.TabPane;
    
    import java.util.HashMap;
    import java.util.List;
    
    /**
     * Created by huangjs on 2018/4/9.
     */
    public class TabPaneExpansion {
        private TabPane tabPane;
        private HashMap<String, Tab> tabsMap;
    
        public TabPane getTabPane() {
            return tabPane;
        }
    
        public void setTabPane(TabPane tabPane) {
            this.tabPane = tabPane;
        }
    
        public TabPaneExpansion() {
            this.tabPane = new TabPane();
            this.tabsMap = new HashMap<>();
            initial();
        }
    
        public TabPaneExpansion(TabPane tabPane) {
            this.tabPane = tabPane;
            this.tabsMap = new HashMap<>();
            initial();
        }
    
        private void initial() {
            tabPane.getTabs().addListener(new ListChangeListener<Tab>() {
    
                @Override
                public void onChanged(Change<? extends Tab> c) {
                    while (c.next()) {
    
                        // if elements were added into list, the elements's text
                        // and the elements themselves need to be added into HashMap
                        if (c.wasAdded()) {
                            List<? extends Tab> addedTabs = c.getAddedSubList();
                            for (Tab tab : addedTabs) {
                                tabsMap.put(tab.getText(), tab);
                            }
                        }
    
                        // if elements were removed from list, the elements's text
                        // and the elements themselves need to be removed from HashMap
                        if(c.wasRemoved()){
                            List<? extends Tab> removedTabs = c.getRemoved();
                            for(Tab tab : removedTabs){
                                tabsMap.remove(tab.getText());
                            }
                        }
                    }
    
                }
            });
        }
    
        public boolean addTab(Tab tab) {
            return this.tabPane.getTabs().add(tab);
        }
    
        public boolean addTabs(Tab... tabs) {
           return this.tabPane.getTabs().addAll(tabs);
        }
    
        public Tab getTabByText(String text) {
            return tabsMap.get(text);
        }
    
        public boolean removeTab(String text){
            return this.tabPane.getTabs().remove(getTabByText(text));
        }
    }
    

    TabPane中保存Tab是使用ObservableList,所以我们可以为这个可观察的列表添加一个监听器,监听它的改变——添加新元素的同时向tabsMap中添加;删除时在tabsMap中删除。

    • 这是TabPane源码中tabs的初始化
    private ObservableList<Tab> tabs = FXCollections.observableArrayList();
    
    • 关于ObservableList其实它就是JDK中观察者模式的运用
    • 看上面代码时可以主要关注initialize()方法

    总结

    实现“通过text管理Tab”这一目的的三种实现,其原理都是利用HashMap来连接text和Tab,第三种使用TabPane本身方法的返回值来弥补了第二种的缺憾。

    相关文章

      网友评论

          本文标题:如何通过Tab的text属性管理TabPane中的Tab?

          本文链接:https://www.haomeiwen.com/subject/tgrihftx.html