简体   繁体   中英

JavaFX TreeItem css style for different class field

I have TreeView filled by my own tree. In class Node I have field "type" which is one of NodeType. The problem is that I want have style for each type of NodeType, eg "type1" text color should be green, "type2" text color should be red. I'm new in javaFX. I found solution by james-d ( https://github.com/james-d/heterogeneous-tree-example ), but in this example css style depends on the class name, how can I make it for class field ?

View of TreeView

My understanding is you want a TreeCell that styles differently depending on the NodeType of the Node contained within the TreeItem of said TreeCell . All via CSS. Am I correct?

Assuming I am correct, there are 2 ways I can think of to accomplish this; both of which work best if there is a small number of known NodeType s. The first involves the use of PseudoClass and the second uses the same strategy as the JavaFX Chart API.


First Option

Create a custom TreeCell that is tailored to using your Node type (ie specify the generic signature appropriately). In this custom TreeCell you declare as many PseudoClass static final fields as you need; one for each NodeType . Then you observe the NodeType of the whatever Node is currently displayed in the TreeCell and update the PseudoClass states accordingly.

Here is an example assuming NodeType is an enum that has two constants: HAPPY and SAD .

public class CustomTreeCell<T extends Node> extends TreeCell<T> {

    private static final PseudoClass HAPPY = PseudoClass.getPseudoClass("happy");
    private static final PseudoClass SAD = PseudoClass.getPseudoClass("sad");

    // this listener will activate/deactivate the appropriate PseudoClass states
    private final ChangeListener<NodeType> listener = (obs, oldVal, newVal) -> {
        pseudoClassStateChanged(HAPPY, newVal == NodeType.HAPPY);
        pseudoClassStateChanged(SAD, newVal == NodeType.SAD);
    };

    // use a weak listener to avoid a memory leak
    private final WeakChangeListener<NodeType> weakListener = /* wrap listener */;

    public CustomTreeCell() {
        getStyleClass().add("custom-tree-cell");
        itemProperty().addListener((obs, oldVal, newVal) -> {
            if (oldVal != null) {
                oldVal.nodeTypeProperty().removeListener(weakListener);
            }
            if (newVal != null) {
                newVal.nodeTypeProperty().addListener(weakListener);
                // need to "observe" the initial NodeType of the new Node item.
                // You could call the listener manually to avoid code duplication
                pseudoClassStateChanged(HAPPY, newVal.getNodeType() == NodeType.HAPPY);
                pseudoClassStateChanged(SAD, newVal.getNodeType() == NodeType.SAD);
            } else {
                // no item in this cell so deactivate all PseudoClass's
                pseudoClassStateChanged(HAPPY, false);
                pseudoClassStateChanged(SAD, false);
            }
        });
    }
}

Then in your CSS file you can use:

.custom-tree-cell:happy {
    /* style when happy */
}

.custom-tree-cell:sad {
    /* style when sad */
}

Second Option

Do what the JavaFX Chart API does when dealing with multiple series of data. What it does is dynamically update the style class of the nodes depending on the series' index in a list (eg .line-chart-series-data-<index> <-- probably not exactly this).

/*
 * Create a custom TreeCell like in the first option but
 * without any of the PseudoClass code. This listener should
 * be added/removed from the Node item just like weakListener
 * is above.
 */
ChangeListener<NodeType> listener = (obs, oldVal, newVal) -> {
    // You have to make sure you keep "cell", "indexed-cell", and "tree-cell"
    // in order to keep the basic modena styling.
    if (newVal == NodeType.HAPPY) {
        getStyleClass().setAll("cell", "indexed-cell", "tree-cell", "custom-tree-cell-happy");
    } else if (newVal == NodeType.HAPPY) {
        getStyleClass().setAll("cell", "indexed-cell", "tree-cell", "custom-tree-cell-sad");
    } else {
        getStyleClass().setAll("cell", "indexed-cell", "tree-cell"); // revert to regular TreeCell style
    }
};

Then in CSS:

.custom-tree-cell-happy {
    /* styles */
}

.custom-tree-cell-sad {
   /* styles */
}

Both of these options are really only viable when there is a small set of known types. It might become unmaintainable when you have something like 10+ NodeType s. It becomes pretty much impossible if the number of NodeType s is dynamic at runtime.

It might be easier to have NodeType , or some intermediate class/data structure, know what color the text should be and set the color programmatically based on the NodeType .

Note: I quickly typed up the code in my answer and did not test it. There may be compiler errors, runtime exceptions, or logic errors in my code.


Edit

Something else came to mind. My code above assumes that NodeType is held in a property and can be changed during runtime. If NodeType is static (unchanging) for each Node then the code can be vastly simplified. Instead of using any listeners you can simple override the following method declared in javafx.scene.control.Cell :

protected void updateItem(Node item, boolean empty)

This method is called every time a new item is set on the cell. Read the documentation , however, as overriding this method requires certain things from the developer (such as calling the super implementation).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM