写在前面
有时候会有这种需求:点击同一个标签或者按钮时打开同一个标签页(Tab),点击不同的标签或者按钮打开不同的标签页。例如编辑器中打开同一个文件,对应打开同一个标签页,点击不同的文件打开不同的标签页。
但是,javafx中的TabPane
没有提供通过text
来管理Tab
的方法——依据属性text
获得标签页;依据属性text
删除标签页。
解决方案
那么如何解决上面的问题?我们可以在属性text
和Tab
之间建立起联系,所以理所当然的想到了哈希。
简易实现
新建一个HashMap变量用于存储text
和Tab
之间的映射,在向TabPane中加入Tab时,在哈希中增加text
到Tab
映射
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本身方法的返回值来弥补了第二种的缺憾。
网友评论