Suppose I have some classes:

public class Person{ String name; int age; List<Work> works; List<Certificate> certificates; } public class Work{ String companyName; String positionName; int experienceAtMonth; } public class Certificate{ String organization; String position; Date date; } 

The bottom line is that I need to display in the TreeView :

  • Person instances inside which a list of jobs and certificates are displayed.

The remaining fields should be displayed in a separate container (such as TextField ) when you click on the corresponding. note.

You can of course form the string, and when working with data, search by name. However, it seems to me a crutch and I would like to avoid it and use some kind of binding, but I can not figure out how to describe this behavior.

According to this, please tell me possible implementations.

    1 answer 1

    UPDATE: + extremely primitive binding

    Add an interface:

     public interface Titlable { String getTitle(); StringProperty getProperty(); } 

    We add your classes:

     public class Person implements Titlable { String name; int age; List<Work> works; List<Certificate> certificates; public String getTitle() { return name; } private StringProperty property; public StringProperty getProperty() { if (null == property) return property = new SimpleStringProperty( name + " " + age ); return property; } } public class Work implements Titlable{ String companyName; String positionName; int experienceAtMonth; public String getTitle() { return companyName; } private StringProperty property; public StringProperty getProperty() { if (null == property) return property = new SimpleStringProperty( companyName + " " + positionName ); return property; } } public class Certificate implements Titlable{ String organization; String position; Date date; public String getTitle() { return organization; } private StringProperty property; public StringProperty getProperty() { if (null == property) return property = new SimpleStringProperty( organization + " " + position + " " + date ); return property; } } 

    Actually the tree itself:

     public class CustomTree extends TreeView<Titlable> { private final ObservableList<Person> persons; private final Main main; public CustomTree( ObservableList<Person> persons, Main main ) { this.persons = persons; this.main = main; TreeItem<Titlable> rootItem = new TreeItem<Titlable>(); rootItem.setExpanded( true ); this.setShowRoot( false ); this.setCellFactory( p -> new TreeCell<Titlable>() { @Override public void updateItem( Titlable t, boolean empty ) { super.updateItem( t, empty ); setText( t == null ? null : t.getTitle() ); } }); for ( Person p : persons ) { TreeItem<Titlable> pItem = new TreeItem<Titlable>( p ); rootItem.getChildren().add( pItem ); Titlable fake1 = new Titlable() { public String getTitle() { return "Works"; } private StringProperty property = new SimpleStringProperty( "-" ); }; Titlable fake2 = new Titlable() { public String getTitle() { return "Certificates"; } private StringProperty property = new SimpleStringProperty( "-" ); }; TreeItem<Titlable> f1Item = new TreeItem<Titlable>( fake1 ); TreeItem<Titlable> f2Item = new TreeItem<Titlable>( fake2 ); pItem.getChildren().addAll( f1Item, f2Item ); for ( Work w : p.works ) { f1Item.getChildren().add( new TreeItem<Titlable>( w ) ); } for ( Certificate c : p.certificates ) { f2Item.getChildren().add( new TreeItem<Titlable>( c ) ); } } setRoot( rootItem ); EventDispatcher originalDispatcher = this.getEventDispatcher(); this.setEventDispatcher( new NotExpandedEventDispatcher( originalDispatcher ) ); } private class NotExpandedEventDispatcher implements EventDispatcher{ private final EventDispatcher originDispatcher; public NotExpandedEventDispatcher( EventDispatcher originDispatcher ){ this.originDispatcher = originDispatcher; } @Override public Event dispatchEvent( Event event, EventDispatchChain tail ) { if ( event instanceof MouseEvent ) { if ( ((MouseEvent)event).getButton() == MouseButton.PRIMARY && ((MouseEvent)event).getClickCount() >= 2 ) { if ( !event.isConsumed() ) { TreeItem<Titlable> selectedItem = getSelectionModel().getSelectedItem(); main.doSomething( selectedItem .getValue() ); } event.consume(); } }else if ( event instanceof KeyEvent ){ if ( ((KeyEvent)event).getCode() == KeyCode.ENTER ){ if ( !event.isConsumed() ) { TreeItem<Titlable> selectedItem = getSelectionModel().getSelectedItem(); main.doSomething( selectedItem .getValue() ); } event.consume(); } } return originDispatcher.dispatchEvent( event, tail ); } } } 

    Checking:

     public class Main extends Application { private TextArea ta; @Override public void start( Stage stage ) { ObservableList<Person> persons = FXCollections.<Person>observableArrayList(); Work work1 = new Work(); Work work2 = new Work(); Work work3 = new Work(); Work work4 = new Work(); Work work5 = new Work(); Work work6 = new Work(); Work work7 = new Work(); Work work8 = new Work(); work1.companyName = "roga i kapbIta 1"; work2.companyName = "roga i kapbIta 2"; work3.companyName = "roga i kapbIta 3"; work4.companyName = "roga i kapbIta 4"; work5.companyName = "roga i kapbIta 5"; work6.companyName = "roga i kapbIta 6"; work7.companyName = "roga i kapbIta 7"; work8.companyName = "roga i kapbIta 8"; Certificate certificate1 = new Certificate(); Certificate certificate2 = new Certificate(); Certificate certificate3 = new Certificate(); Certificate certificate4 = new Certificate(); certificate1.organization = "kapbIta i roga 1"; certificate2.organization = "kapbIta i roga 2"; certificate3.organization = "kapbIta i roga 3"; certificate4.organization = "kapbIta i roga 4"; Person a = new Person(); Person b = new Person(); Person c = new Person(); a.name = "ivan"; b.name = "da"; c.name = "mar'ya"; a.works = new ArrayList<>( Arrays.asList( work1, work2, work3 ) ); b.works = new ArrayList<>( Arrays.asList( work4, work5, work6 ) ); c.works = new ArrayList<>( Arrays.asList( work7, work8 ) ); a.certificates = new ArrayList<>( Arrays.asList( certificate1 ) ); b.certificates = new ArrayList<>( Arrays.asList( certificate2 ) ); c.certificates = new ArrayList<>( Arrays.asList( certificate3, certificate4 ) ); persons.addAll( a, b, c ); Parent container = new CustomTree( persons, this ); ta = new TextArea(); Scene scene = new Scene( new HBox( container, ta ) ); stage.setWidth( 650 ); stage.setHeight( 600 ); stage.setScene( scene ); stage.show(); } public static void main(String[] args) { launch(args); } public void doSomething( Titlable t ) { ta.textProperty().unbind(); ta.textProperty().bind( t.getProperty() ); } } 

    result

    • It remains to add a dynamic (on the change event of the selected element) binding between some TextArea.textProperty and the active (selected) tree element. Well, do not forget to unbind'it. - DimXenon
    • Thank you for your reply. About a binding it would be desirable to specify. Judging by your code, an object of type TreeItem <Titlable> is selected, then how to describe that an object of a certain class shows its fields in the same TextArea? And the second point: as I understand it, there is no alternative way so that, for example, in xStream you can annotate the required fields and build a tree on their basis? - I. Perevoz pm
    • I did not write anything about the binding, but the easiest option will probably be to check the Titlable instanceof Person on the event ( MouseEvent , KeyEvent or something else) and then explicitly lead and bind. You can expand the interface. I can’t say anything about alternative ways. - Andrey M
    • @AndreyM is so kind of instanceof very long and slow. Now it is not so important, but when there are a few orders more records, it may haunt. I originally thought to do the same way, but I was just stopped by the prospects for expanding and maintaining the code. - I. Perevoz pm
    • one
      @ I.Perevoz On the fingers will not work, but a brief summary: that one, that the other is a redefinition of standard behavior. In the case of cellFactory , the redefinition of the drawing (content, color, etc.) depending on the element of the cell. Its default behavior is setGraphic( (Node)cell.item ) if the instanceof Node element is instanceof Node , otherwise setText( cell.item.toString() ) . eventDispetcher is responsible for sending an event in the delivery chain with the ability to process the event , change, replace, reject, etc. - Andrey M