JavaFX: How to highlight certain Items in a TreeView -
i trying implement search function treeview
in javafx. want highlight matches when user hits enter key. added boolean ishighlighted
treeitem
, in treecell
s updateitem
, check whether item ishighlighted
, if apply css. works fine items/cells not visible @ moment of search -- when scroll them, highlighted. problem is: how can "repaint" treecells visible @ search reflect whether item ishighlighted
? controller not have reference treecells
treeview
creates.
this answer based on this one, adapted treeview
instead of tableview
, , updated use javafx 8 functionality (greatly reducing amount of code required).
one strategy maintain observableset
of treeitems
match search (this useful other functionality may want anyway). use css pseudoclass
, external css file highlight required cells. can create booleanbinding
in cell factory binds cell's treeitemproperty
, observableset
, evaluating true
if set contains cell's current tree item. register listener binding , update pseudoclass state of cell when changes.
here's sscce. contains tree items integer
-valued. update search when type in search box, matching value multiple of value entered.
import java.util.arraylist; import java.util.hashset; import java.util.list; import java.util.random; import java.util.set; import javafx.application.application; import javafx.beans.binding.bindings; import javafx.beans.binding.booleanbinding; import javafx.collections.fxcollections; import javafx.collections.observableset; import javafx.css.pseudoclass; import javafx.geometry.insets; import javafx.scene.scene; import javafx.scene.control.textfield; import javafx.scene.control.textformatter; import javafx.scene.control.treecell; import javafx.scene.control.treeitem; import javafx.scene.control.treeview; import javafx.scene.layout.borderpane; import javafx.stage.stage; public class treewithsearchandhighlight extends application { @override public void start(stage primarystage) { treeview<integer> tree = new treeview<>(createrandomtree(100)); // keep track of items match our search: observableset<treeitem<integer>> searchmatches = fxcollections.observableset(new hashset<>()); // cell factory returns instance of treecell implementation defined below. // pass cell implementation reference set of search matches tree.setcellfactory(tv -> new searchhighlightingtreecell(searchmatches)); // search text field: textfield textfield = new textfield(); // allow numeric input: textfield.settextformatter(new textformatter<integer>(change -> change.getcontrolnewtext().matches("\\d*") ? change : null)); // when text changes, update search matches: textfield.textproperty().addlistener((obs, oldtext, newtext) -> { // clear search: searchmatches.clear(); // if no text, or 0, exit: if (newtext.isempty()) { return ; } int searchvalue = integer.parseint(newtext); if (searchvalue == 0) { return ; } // search matching nodes , put them in searchmatches: set<treeitem<integer>> matches = new hashset<>(); searchmatchingitems(tree.getroot(), matches, searchvalue); searchmatches.addall(matches); }); borderpane root = new borderpane(tree, textfield, null, null, null); borderpane.setmargin(textfield, new insets(5)); borderpane.setmargin(tree, new insets(5)); scene scene = new scene(root, 600, 600); // stylesheet sets style cells matching search using selector // .tree-cell:search-match // (specified in initalization of pseudoclass @ top of code) scene.getstylesheets().add("tree-highlight-search.css"); primarystage.setscene(scene); primarystage.show(); } // find tree items value multiple of search value: private void searchmatchingitems(treeitem<integer> searchnode, set<treeitem<integer>> matches, int searchvalue) { if (searchnode.getvalue() % searchvalue == 0) { matches.add(searchnode); } (treeitem<integer> child : searchnode.getchildren()) { searchmatchingitems(child, matches, searchvalue); } } // build random tree numnodes nodes (all nodes expanded): private treeitem<integer> createrandomtree(int numnodes) { list<treeitem<integer>> items = new arraylist<>(); treeitem<integer> root = new treeitem<>(1); root.setexpanded(true); items.add(root); random rng = new random(); (int = 2 ; <= numnodes ; i++) { treeitem<integer> item = new treeitem<>(i); item.setexpanded(true); treeitem<integer> parent = items.get(rng.nextint(items.size())); parent.getchildren().add(item); items.add(item); } return root ; } public static class searchhighlightingtreecell extends treecell<integer> { // must keep reference binding prevent premature garbage collection: private booleanbinding matchessearch ; public searchhighlightingtreecell(observableset<treeitem<integer>> searchmatches) { // pseudoclass highlighting state // css can set style selector // .tree-cell:search-match { ... } pseudoclass searchmatch = pseudoclass.getpseudoclass("search-match"); // initialize binding. evaluates true if searchmatches // contains current treeitem // note binding observes both treeitemproperty , searchmatches, // updates if either 1 changes: matchessearch = bindings.createbooleanbinding(() -> searchmatches.contains(gettreeitem()), treeitemproperty(), searchmatches); // update pseudoclass state if binding value changes: matchessearch.addlistener((obs, didmatchsearch, nowmatchessearch) -> pseudoclassstatechanged(searchmatch, nowmatchessearch)); } // update text when item displayed changes: @override protected void updateitem(integer item, boolean empty) { super.updateitem(item, empty); settext(empty ? null : "item "+item); } } public static void main(string[] args) { launch(args); } }
the css file tree-highlight-search.css has contain style highlighted cells:
.tree-cell:search-match { -fx-background: yellow ; }
Comments
Post a Comment