2014-05-07 13 views
16

Desidero utilizzare le proprietà JavaFX per l'associazione UI, ma non le desidero nelle classi modello (vedere Using javafx.beans properties in model classes). Le mie classi di modelli hanno getter e setter e voglio creare proprietà basate su quelle. Per esempio, assumendo un'istanza bean con metodi String getName() e setName(String name), vorrei scrivereJavaBean wrapping con le proprietà JavaFX

SimpleStringProperty property = new SimpleStringProperty(bean, "name") 

sperando che property.set("Foobar") innescherebbe una chiamata a bean.setName. Ma questo non sembra funzionare. Cosa mi manca?

+0

Non scartare completamente l'idea di utilizzare le proprietà javafx nelle classi del modello. Ho aggiunto una risposta alternativa alla domanda che hai collegato: potrebbe essere un approccio che vale la pena di pensare. –

risposta

36

Le classi Simple*Property sono implementazioni complete e autonome delle corrispondenti classi astratte Property e non si basano su altri oggetti. Ad esempio, SimpleStringProperty contiene un campo (privato) String che contiene il valore corrente della proprietà.

I parametri per il costruttore che si mostravano:

new SimpleStringProperty(bean, "name") 

sono:

  • bean: il fagiolo a cui la proprietà appartiene, se del caso
  • name: il nome della proprietà

bean può essere utile nel metodo changed(...) di in quanto è possibile recuperare il "bean proprietario" della proprietà che è stata modificata dalla proprietà stessa. È possibile utilizzare lo name in modo simile (se si ha lo stesso listener registrato con più proprietà, è possibile determinare quale proprietà è stata modificata: sebbene non utilizzi mai questo modello).

Quindi un utilizzo tipico di un SimpleStringProperty come una proprietà osservabile di un oggetto si presenta come:

public class Person { 
    private final StringProperty firstName 
     = new SimpleStringProperty(this, "firstName"); 

    public final String getFirstName() { 
     return firstName.get(); 
    } 

    public final void setFirstName(String firstName) { 
     this.firstName.set(firstName); 
    } 

    public StringProperty firstNameProperty() { 
     return firstName ; 
    } 

    // ... other properties, etc 
} 

La funzionalità che stai cercando: per avvolgere un edificio in stile Java Bean esistente in una proprietà osservabile JavaFX è implementato per classi nel pacchetto javafx.beans.property.adapter. Così, per esempio, si potrebbe fare

StringProperty nameProperty = new JavaBeanStringPropertyBuilder() 
     .bean(bean) 
     .name("name") 
     .build(); 

Calling

nameProperty.set("James"); 

con questa configurazione causerà effettivamente una chiamata a

bean.setName("James"); 

Se il chicco supporta PropertyChangeListener s, il JavaBeanStringProperty sarà registrarsi a PropertyChangeListener con il bean. Eventuali modifiche alla proprietà name del bean Java saranno tradotte dal JavaBeanStringProperty nelle modifiche alle proprietà JavaFX. Di conseguenza, se il JavaBean sottostante supporta PropertyChangeListener s, quindi passa al bean tramite

bean.setName(...); 

provocherà alcuna ChangeListener s (o InvalidationListener s) registrati con la JavaBeanStringProperty notifica della modifica.

Così, per esempio, se la classe Bean è

import java.beans.PropertyChangeListener; 
import java.beans.PropertyChangeSupport; 

public class Bean { 

    private String name ; 
    private final PropertyChangeSupport propertySupport ; 

    public Bean(String name) { 
     this.name = name ; 
     this.propertySupport = new PropertyChangeSupport(this); 
    } 

    public Bean() { 
     this(""); 
    } 

    public String getName() { 
     return name ; 
    } 

    public String setName(String name) { 
     String oldName = this.name ; 
     this.name = name ; 
     propertySupport.firePropertyChange("name", oldName, name); 
    } 

    public void addPropertyChangeListener(PropertyChangeListener listener) { 
     propertySupport.addPropertyChangeListener(listener); 
    } 
} 

Poi il seguente codice:

Bean bean = new Bean(); 
StringProperty nameProperty() = new JavaBeanStringPropertyBuilder() 
     .bean(bean) 
     .name("name") 
     .build(); 
nameProperty().addListener((obs, oldName, newName) -> System.out.println("name changed from "+oldName+" to "+newName)); 
bean.setName("James"); 
System.out.println(nameProperty().get()); 

produrrà l'output:

name changed from to James 
James 

Se il JavaBean non lo fa supporto PropertyChangeListener s, quindi le modifiche al bean tramite bean.setName(...) non si propagheranno a ChangeListener s oppure InvalidationListener s registrati con JavaBeanStringProperty.

Quindi, se il chicco è semplicemente

public class Bean { 

    public Bean() { 
     this(""); 
    } 

    public Bean(String name) { 
     this.name = name ; 
    } 

    private String name ; 

    public String getName() { 
     return name ; 
    } 

    public void setName(String name) { 
     this.name = name ; 
    } 
} 

Il JavaBeanStringProperty non avrebbe alcun modo di osservare il cambiamento, in modo che il cambiamento ascoltatore non sarebbe mai essere invocata da una chiamata a bean.setName(). Quindi il codice di test sopra riportato sarebbe semplicemente in uscita

James 
+0

+1 buona spiegazione. JavaBeanStringPropertyBuilder aggiungerà il supporto PropertyChangeListener per il bean. Dalla risposta mi sembra che l'utente debba aggiungere il proprio supporto al bean. –

+0

Non proprio: il 'JavaBeanStringProperty' non aggiunge il supporto' PropertyChangeListener' al bean. Risposta aggiornata per chiarire. –

+0

Ti dispiacerebbe se ti chiedessi di mostrare anche un bean Java di esempio con e senza il supporto di PropertyChangeListener? –