美文网首页
如何通过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