JavaFX get access to a Tab or TabPane from a TabCo

2019-07-08 13:46发布

问题:

Is there a way to get a reference to Tab or TabPane without no improvised way if you have reference to only the Node inside the Tab for eg

TabPane tabPane = new TabPane();
Tab tab = new Tab();
tab.setText("new tab");
Rectangle drect = new Rectangle(200,200, Color.LIGHTSTEELBLUE);
tab.setContent(drect);
tabPane.getTabs().addAll(tab,new Tab("tab 2"));

assume i only have reference to drect how can i get tab. I am not interested in Node.setUserData() also drect.getParent().getClass().getName() returns TabPaneSkin anon inner class TabContentRegion: two strangers.

All i am trying to say is Node.getParent() should return the parent Node , general uni-knowledge ( node's representation in the scene graph can be traced with getParent() ), but, when it comes to Tabs its all wrong, hence my question

EDIT

Why i needed this, suppose you have TabPane consisting of soo many Tab, in one particular Tab you have a TreeViews as its Node-Content which is selected out of some conditions, and for that particular TreeView you have different Custom Cells because its TreeItems differ in UI and function- like changing styles binding Fonts& other bindables and Animating Tab hiding other Tabs in the TabPane, closing other Tabs

with this scenario following this approach seems legit for me as, you do not do unnecessary stuff, like exposing children to bad weather. :-)

Thanks Sir James_D

回答1:

The Tab itself is not a Node, so you can't get the tab just by iterating through the scene graph in any way. You can get to the TabPane if you go up far enough in the hierarchy. For example:

private TabPane findTabPaneForNode(Node node) {
    TabPane tabPane = null ;

    for (Node n = node.getParent(); n != null && tabPane == null; n = n.getParent()) {
        if (n instanceof TabPane) {
            tabPane = (TabPane) n;
        }
    }

    return tabPane ;
}

will return the nearest TabPane containing the node passed in, or null if the node is not in a tab pane.

However, needing this just seems like your design is wrong. If you put a node in a tab pane, at some point you create a tab, so you should just organize your code so you have the reference to the tab available.



回答2:

I know this is a little old question, but I will post the solution for someone how is still struggling with this issue.

The following is the FXML structure example of the TabPane.

<!-- (1) -->
<TabPane>
  <tabs>
    <!-- (2) -->
    <Tab>
      <!-- (3) -->
      <content>
        <!-- (4) -->
        <Pane>
          <!-- (5) -->
          <Node />
        </Pane>
      </content>
    </Tab>
  </tabs>
</TabPane>

The following method returns the Tab object which includes the Node specified as an argument.

public static Tab getParentTab1(Node node) {
    // <TabPane> object (described as (1) in the FXML example)
    TabPane tabPane = null;

    // <Tab> object (described as (2))
    Tab tab = null;

    // The object of the tab content area
    Node tabContentRegion = null;

    ClassLoader loader = ClassLoader.getSystemClassLoader();
    Class<?> innerClass = null;

    try {
        innerClass = loader.loadClass("com.sun.javafx.scene.control.skin.TabPaneSkin$TabContentRegion");
    } catch (ClassNotFoundException ex) {
        ex.printStackTrace();
    }

    for (Node n = node.getParent(); n != null && tabPane == null; n = n.getParent()) {
        // If the node is an instance of the inner class of TabPaneSkin
        if (n.getClass().isAssignableFrom(innerClass)) {
            tabContentRegion = n;
        }

        if (n instanceof TabPane) {
            tabPane = (TabPane) n;
        }
    }

    if (tabPane != null && tabContentRegion != null) {
        // The list of the <Tab> objects
        ObservableList<Tab> tabList = tabPane.getTabs();

        for (Tab t : tabList) {
            // t.getContent() returns the first <Node> object on the <Tab> (most likely it's a <Pane> described as (4))
            // t.getContent().getParent() returns the TabPaneSkin$TabContentRegion object
            System.out.println("content: " + t.getContent());
            System.out.println("parent: " + t.getContent().getParent());

            if (t.getContent().getParent().equals(tabContentRegion)) {
                tab = t;
                break;
            }
        }
    }

    return tab;
}
  1. Get the TabPane object and the tab content area of the Tab object from Node object.

  2. Scan all Tab objects who are contained in the TabPane (that is retrieved in the phase #1).

  3. Get the Tab object whose content area is equivalent to the tab content area (that is retrieved in the phase #1).

The simpler way is shown below.

public Tab getParentTab2(Node node) {
    TabPane tabPane = null;
    Tab tab = null;
    Node tabContentRegion = null;

    for (Node n = node.getParent(); n != null && tabPane == null; n = n.getParent()) {
        if (n.getStyleClass().contains("tab-content-area")) {
            tabContentRegion = n;
        }

        if (n instanceof TabPane) {
            tabPane = (TabPane) n;
        }
    }

    if (tabPane != null && tabContentRegion != null) {
        // The list of the <Tab> objects
        ObservableList<Tab> tabList = tabPane.getTabs();

        for (Tab t : tabList) {
            if (t.getContent().getParent().equals(tabContentRegion)) {
                tab = t;
                break;
            }
        }
    }

    return tab;
}