I have an object with bound members in JavaFX andI want to reset the members to initial state at a certain moment.
So my first option is to reset them one by one, which is to much code, but its works fine.
The other option is to re-instantiate i.e. myModel = new Model(), but in the case the object reference changes and I lose my binding.
Is there any alternative way to accomplish this?
EDIT
Model
public class Service extends BaseModel {
private StringProperty serviceType = new SimpleStringProperty();
private ObjectProperty<BigDecimal> buyingPrice = new SimpleObjectProperty<>(new BigDecimal(0));
private ObjectProperty<BigDecimal> sellingPrice = new SimpleObjectProperty<>(new BigDecimal(0));
public Service(){
}
public String getServiceType() {
return bundle.getString("service");
}
public StringProperty serviceTypeProperty() {
return serviceType;
}
public void setServiceType(String serviceType) {
this.serviceType.set(serviceType);
}
public BigDecimal getBuyingPrice() {
return buyingPrice.get();
}
public ObjectProperty<BigDecimal> buyingPriceProperty() {
return buyingPrice;
}
public void setBuyingPrice(BigDecimal buyingPrice) {
this.buyingPrice.set(buyingPrice);
}
public BigDecimal getSellingPrice() {
return sellingPrice.get();
}
public ObjectProperty<BigDecimal> sellingPriceProperty() {
return sellingPrice;
}
public void setSellingPrice(BigDecimal sellingPrice) {
this.sellingPrice.set(sellingPrice);
}
}
The bindings:
public class ServiceForm extends HBox implements Initializable {
private Service service = new Service();
#FXML
private TextField serviceDescriptionField;
#FXML
private TextField servicePriceField;
#FXML
private Button addButton;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
serviceDescriptionField.textProperty().bindBidirectional(service.descriptionProperty());
servicePriceField.textProperty().bindBidirectional(service.sellingPriceProperty(), new BigDecimalStringConverter());
}
public Service getService() {
return service;
}
public void setService(Service service) {
this.service = service;
}
public Button getAddButton() {
return addButton;
}
public void setAddButton(Button addButton) {
this.addButton = addButton;
}
}
Doing this does not clear my fields and I believe it breaks my binding:
serviceForm.setService(new Service());
You can try to have the Service as a property:
private ObjectProperty<Service> service = new SimpleObjectProperty<>(new Service());
Extract the bindings into a private method which is able to unbind old bindings and create new ones:
private void createBindings(Service oldService) {
// Unbind if there is an older service
if (oldService != null) {
servicePriceField.textProperty().unbindBidirectional(oldService.sellingPriceProperty());
serviceDescriptionField.textProperty().unbindBidirectional(oldService.descriptionProperty());
}
servicePriceField.textProperty().bindBidirectional(service.get().sellingPriceProperty(), new BigDecimalStringConverter());
serviceDescriptionField.textProperty().bindBidirectional(service.descriptionProperty());
}
and then in the initialize method:
createBindings(null);
service.addListener((obs, oldval, newval) -> createBindings(oldval));
This way if you call service.set(new Service()), the bindings will be created for the new Service and will be removed for the previous one.
Now when you add new bindings, you can bundle them in the createBindings method while adding the unbinging logic to the same place. Note: this could be further generalized.
Related
I try to make sample login page with two fields (username, password) and save button with android architecture component, using android data binding, validating the data in viewmodel and from view model I make call to repository for remote server call as mentioned in official doc, remote server return me userid with success so how can I start new fragment from view model using this success? I learn something about singleLiveEvent and EventObserver, but I'm not able to find there clear usage example:
LoginViewModel
private MutableLiveData<String> snackbarStringSingleLiveEvent= new MutableLiveData<>();
#Inject
public LoginViewModel(#NonNull AppDatabase appDatabase,
#NonNull JobPortalApplication application,
#NonNull MyApiEndpointInterface myApiEndpointInterface) {
super(application);
loginRepository = new LoginRepository(application, appDatabase, myApiEndpointInterface);
snackbarStringSingleLiveEvent = loginRepository.getLogin(username.get(), password.get(), type.get());
}
public MutableLiveData<String> getSnackbarStringSingleLiveEvent() {
return snackbarStringSingleLiveEvent;
}
Repository
public SingleLiveEvent<String> getLogin(String name, String password, String type) {
SingleLiveEvent<String> mutableLiveData = new SingleLiveEvent<>();
apiEndpointInterface.getlogin(name, password, type).enqueue(new Callback<GenericResponse>() {
#Override
public void onResponse(Call<GenericResponse> call, Response<GenericResponse> response) {
mutableLiveData.setValue(response.body().getMessage());
}
#Override
public void onFailure(Call<GenericResponse> responseCall, Throwable t) {
mutableLiveData.setValue(Constant.FAILED);
}
});
return mutableLiveData;
}
Login Fragment
private void observeViewModel(final LoginViewModel viewModel) {
// Observe project data
viewModel.getSnackbarStringSingleLiveEvent().observe(this, new Observer<String>() {
#Override
public void onChanged(String s) {
}
});
}
How can I use EventObserver in above case? Any practical example?
Check out below example about how you can create single LiveEvent to observe only one time as LiveData :
Create a class called Event as below that will provide our data once and acts as child of LiveData wrapper :
public class Event<T> {
private boolean hasBeenHandled = false;
private T content;
public Event(T content) {
this.content = content;
}
public T getContentIfNotHandled() {
if (hasBeenHandled) {
return null;
} else {
hasBeenHandled = true;
return content;
}
}
public boolean isHandled() {
return hasBeenHandled;
}
}
Then declare this EventObserver class like below so that we don't end up placing condition for checking about Event handled every time, everywhere :
public class EventObserver<T> implements Observer<Event<T>> {
private OnEventChanged onEventChanged;
public EventObserver(OnEventChanged onEventChanged) {
this.onEventChanged = onEventChanged;
}
#Override
public void onChanged(#Nullable Event<T> tEvent) {
if (tEvent != null && tEvent.getContentIfNotHandled() != null && onEventChanged != null)
onEventChanged.onUnhandledContent(tEvent.getContentIfNotHandled());
}
interface OnEventChanged<T> {
void onUnhandledContent(T data);
}
}
And How you can implement it :
MutableLiveData<Event<String>> data = new MutableLiveData<>();
// And observe like below
data.observe(lifecycleOwner, new EventObserver<String>(data -> {
// your unhandled data would be here for one time.
}));
// And this is how you add data as event to LiveData
data.setValue(new Event(""));
Refer here for details.
Edit for O.P.:
Yes, data.setValue(new Event("")); is meant for repository when you've got response from API (Remember to return same LiveData type you've taken in VM instead of SingleLiveEvent class though).
So, let's say you've created LiveData in ViewModel like below :
private MutableLiveData<Event<String>> snackbarStringSingleLiveEvent= new MutableLiveData<>();
You provide value to this livedata as Single Event from repository like below :
#Override
public void onResponse(Call<GenericResponse> call, Response<GenericResponse> response) {
mutableLiveData.setValue(new Event(response.body().getMessage())); // we set it as Event wrapper class.
}
And observe it on UI (Fragment) like below :
viewModel.getSnackbarStringSingleLiveEvent().observe(this, new EventObserver<String>(data -> {
// your unhandled data would be here for one time.
}));
Event.java
public class Event<T> {
private T content;
private boolean hasBeenHandled = false;
public Event(T content) {
this.content = content;
}
/**
* Returns the content and prevents its use again.
*/
public T getContentIfNotHandled() {
if (hasBeenHandled) {
return null;
} else {
hasBeenHandled = true;
return content;
}
}
/**
* Returns the content, even if it's already been handled.
*/
public T peekContent() {
return content;
}
}
EventObserver.java
public class EventObserver<T> implements Observer<Event<? extends T>> {
public interface EventUnhandledContent<T> {
void onEventUnhandledContent(T t);
}
private EventUnhandledContent<T> content;
public EventObserver(EventUnhandledContent<T> content) {
this.content = content;
}
#Override
public void onChanged(Event<? extends T> event) {
if (event != null) {
T result = event.getContentIfNotHandled();
if (result != null && content != null) {
content.onEventUnhandledContent(result);
}
}
}
}
Example, In ViewModel Class
public class LoginViewModel extends BaseViewModel {
private MutableLiveData<Event<Boolean>> _isProgressEnabled = new MutableLiveData<>();
LiveData<Event<Boolean>> isProgressEnabled = _isProgressEnabled;
private AppService appService;
private SchedulerProvider schedulerProvider;
private SharedPreferences preferences;
#Inject
LoginViewModel(
AppService appService,
SchedulerProvider schedulerProvider,
SharedPreferences preferences
) {
this.appService = appService;
this.schedulerProvider = schedulerProvider;
this.preferences = preferences;
}
public void login(){
appService.login("username", "password")
.subscribeOn(schedulerProvider.executorIo())
.observeOn(schedulerProvider.ui())
.subscribe(_userLoginDetails::setValue,
_userLoginDetailsError::setValue,
() -> _isProgressEnabled.setValue(new Event<>(false)),
d -> _isProgressEnabled.setValue(new Event<>(true))
)
}
}
In Login Fragment,
viewModel.isProgressEnabled.observe(this, new EventObserver<>(hasEnabled -> {
if (hasEnabled) {
// showProgress
} else {
// hideProgress
}
}));
Using Event and EventObserver class we can achieve the same like SingleLiveEvent class but if you are thinking a lot of boilerplate code just avoid this method. I hope it would help you and give some idea about why we are using SingleEvent in LiveData.
I understand that Google gives the guidelines to use LiveData between the ViewModel and UI but there are edge cases where using LiveData as a SingleLiveEvent is like reinventing the wheel. For single time messaging between the view model and user interface we can use the delegate design pattern. When initializing the view model in the activity we just have to set the activity as the implementer of the interface. Then throughout our view model we can call the delegate method.
Interface
public interface Snackable:
void showSnackbarMessage(String message);
UI
public class MyActivity extends AppCompatActivity implements Snackable {
private MyViewModel myViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_layout);
this.myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
this.myViewModel.setListener(this);
}
#Override
public void showSnackbarMessage(String message) {
Toast.makeText(this, "message", Toast.LENGTH_LONG).show();
}
}
View Model
public class MyViewModel extends AndroidViewModel {
private Snackable listener;
public MyViewModel(#NonNull Application application) {
super(application);
}
public void setListener(MyActivity activity){
this.listener = activity;
}
private void sendSnackbarMessage(String message){
if(listener != null){
listener.showSnackbarMessage(message);
}
}
private void anyFunctionInTheViewModel(){
sendSnackbarMessage("Hey I've got a message for the UI!");
}
}
Wicket use of models can be cumbersome. For a stateful page to properly render an object, you need to use lots of boiler-plate code, overriding classes to properly get the visibility status, etc... A simple example:
private IModel<FooBar> fooBarModel;
public MyPage() {
Label lbl1 = new Label("field1",
new PropertyModel<>(fooBarModel, "field1")) {
#Override public boolean isVisible() {
return fooBarModel.getObject().someCondition();
} }
add(lbl1);
/// Etc... same for dozen of other fields
}
I'm often using a trick using a ListView to help. Same example:
public MyPage() {
add(new ListView<FooBar>("content",
new SingleListModel<FooBar>(fooBarModel)) {
#Override protected void populateItem(...) {
FooBar fooBar = item.getModelObject();
// Code here gets simpler:
Label lbl1 = new Label("field1", fooBar.getField1());
lbl1.setVisible(fooBar.someCondition());
item.add(lbl1);
// Etc...
}
});
}
With a simple utility class SingleListModel, that transform a IModel<T> to a ListModel<T>, having 1 or 0 elements, depending whether T is null or not:
public class SingleListModel<T>
extends LoadableDetachableModel<List<T>> {
private IModel<T> tModel;
public SingleListModel(IModel<T> tModel) {
this.tModel = tModel;
}
#Override
protected List<T> load() {
List<T> ret = new ArrayList<>(1);
T t = tModel.getObject();
if (t != null)
ret.add(tModel.getObject());
return ret;
}
}
The nice side-effect of this is that the whole "content" element in the markup is hidden if fooBarModel returns null; no special treatment needed.
But all this smells like a hack to me, as I use ListView in a somehow "unnatural" fashion.
Is there a cleaner way to get the same result? A standard wicket framework?
You should use Behavior instead to avoid such duplications.
public class MyBehavior extends Behavior {
private final MyModel model;
public MyBehavior(MyModel model) {this.model = model;}
#Override public void onConfigure(Component c) {
if (model.someCondition()) {
component.setVisible(false);
}
}
}
Usage:
MyBehavior b = new MyBehavior(modelInstance);
component1.add(b);
component2.add(b);
// dozen more
Label lbl1 = new Label("field1",
new PropertyModel<>(fooBarModel, "field1")) {
#Override public boolean isVisible() {
return fooBarModel.getObject().someCondition();
} }
add(lbl1);
with little refactoring it can be converted into
add(new FLabel("id","text")
.setVisibilityFunction(()->model.getObject().isVisible()))
);
the FLabel class:
public class FLabel extends Label implements IComponentWithVisibilityFunction<FLabel> {
private SerializableBooleanSupplier visibilityFunction;
public FLabel(String id) {
super(id);
}
public FLabel(String id, Serializable label) {
super(id, label);
}
public FLabel(String id, IModel<?> model) {
super(id, model);
}
#Override
public FLabel setVisibilityFunction(SerializableBooleanSupplier visibilityFunction) {
this.visibilityFunction = visibilityFunction;
return this;
}
#Override
protected void onConfigure() {
if (visibilityFunction != null) {
setVisible(visibilityFunction.getAsBoolean());
}
}
}
public interface IComponentWithVisibilityFunction<T> {
T setVisibilityFunction(SerializableBooleanSupplier visibilityFunction);
}
Moreover you can put supplier into constructor:
add(new FLabel("id","text", ()->model.getObject().isVisible()));
I have multiple JavaFX panes and canvases that reference a complex object with data they need, and I want them to redraw when the object changes.
This would call for the object to be Observable, but which class do I use? JavaFX seems to mostly have ObservableValue subclasses, which wrap a value and allow swapping it out. I don't want to swap out the complex object, just notify the listeners when changes occur. I could do that by implementing addListener, but I'm sure there's a subclass that does it for me already.
class ComplexObject /* extends SomeObservableClass */ {
public int getValue1 { complex calculations... };
public int getValue2 { ... };
public void setNewValue1(int newValue) { ... }
}
class ComplexRenderer extends Canvas implements InvalidationListener {
private ComplexObject complexObject;
public void setComplexObject(ComplexObject complexObject) {
this.complexObject = complexObject;
complexObject.addListener(this);
}
public void draw() { ... }
}
Which class should ComplexObject extend? Is there something that maintains the list of listeners and has something like fireValueChangedEvent() so I can make it notify all listeners?
Everything I see in JavaFX seems to be geared towards properties, which don't seem the right choice here.
Not really sure what you meant by swapping, and not really sure if I understood you right.
class ComplexObject {
private IntegerProperty value1 = new SimpleIntegerProperty();
private IntegerProperty value2 = new SimpleIntegerProperty();
private BooleanProperty internalChanged = new SimpleBooleanProperty(false);
public ComplexObject() {
this.internalChanged.bind(Bindings.createBooleanBinding(() ->
this.internalChanged.set(!this.internalChanged.get()), this.value1, this.value2));
}
public IntegerProperty value1Property() { return this.value1; }
public int getValue1() { return this.value1.get(); }
public void setValue1(int value) { return this.value1.set(value); }
public IntegerProperty value2Property() { return this.value2; }
public int getValue2() { return this.value2.get(); }
public void setValue2(int value) { return this.value2.set(value); }
public void setNewValue1(int newValue) { /* What value is this??? */ }
public BooleanProperty internalChangedProperty() { return this.internalChanged; }
}
class ComplexRenderer extends Canvas implements InvalidationListener {
private ComplexObject complexObject;
public void setComplexObject(ComplexObject complexObject) {
this.complexObject = complexObject;
complexObject.internalChangedProperty().addListener(this);
}
#Override public void invalidated(Observable observable) {
// Something inside complex object changed
}
public void draw() { ... }
}
Maybe you can have a look at the Interface ObjectPropertyBase<T> and the classes ObjectPropertyBase<T> and SimpleObjectProperty<T> which implements Observable.
However you have to define when your object changes and listening logic.
I'm sorry it's just a trace of work, but I hope it may be useful.
I am looking for a design pattern / solution for the following problem, that is related to the Observer pattern, I have already studied.
In my code I have a MyModel class. It has many properties.
public class MyModel {
private List<Flower> flowers = new ArrayList<Flower>();
private List<Toys> toys = new ArrayList<Toys>();
private List<Coffee> coffees = new ArrayList<Coffee>();
private List<IBusinessEntityListener> listener =
new ArrayList<IBusinessEntityListener>();
public void addChangeListener(IBusinessEntityListener newListener) {
listener.add(newListener);
}
}
So classes that implement IBusinessEntityListener can register to MyModel class.
Then I have 10+ listeners that are interested only in some properties of MyModel. They all implement IBusinessEntityListener. But how can I specify (for example with Java Generics?) that some listener are only interested in Flowers, some only about Toys, etc.?
So How to design such class structure that would support listening to certain properties?
All listeners would anyway implement 3 methods for the operations add, update and delete.
How about an application of the Extrinsic Visitor pattern?
Define an interface for properties:
public interface ListenableProperty {
// Degenerate interface for listeners
public interface Listener {}
public void acceptUpdate(Listener listener);
}
Then implement a class for each property, and a Listener interface for each property, and use like so from your model:
public class MyModel {
public static class FlowersProperty implements ListenableProperty {
public interface Listener extends ListenableProperty.Listener {
public void update(FlowersProperty p);
}
#Override
public void acceptUpdate(ListenableProperty.Listener listener) {
if (listener instanceof FlowersProperty.Listener) {
Listener myListenerType = (Listener)listener;
myListenerType.update(this);
}
}
// some property accessors here
}
public static class ToysProperty implements ListenableProperty {
public interface Listener extends ListenableProperty.Listener {
public void update(ToysProperty p);
}
#Override
public void acceptUpdate(ListenableProperty.Listener listener) {
if (listener instanceof ToysProperty.Listener) {
Listener myListenerType = (Listener)listener;
myListenerType.update(this);
}
}
// some property accessors here
}
private FlowersProperty flowers = new FlowersProperty();
private ToysProperty toys = new ToysProperty();
private List<ListenableProperty> properties = new ArrayList();
// CopyOnWrite so that listeners can remove themselves during update if desired
private List<ListenableProperty.Listener> listeners =
new CopyOnWriteArrayList<>();
// Convenience interface for implementors that want all properties
public interface AllPropertiesListener extends
FlowersProperty.Listener,
ToysProperty.Listener
{}
public MyModel() {
properties.add(flowers);
properties.add(toys);
}
public void addListener(ListenableProperty.Listener l) {
if (!listeners.contains(l)) {
listeners.add(l);
}
}
private void updateAll() {
for (ListenableProperty p : properties) {
for (ListenableProperty.Listener l : listeners) {
p.acceptUpdate(l);
}
}
}
private void updateToys() {
for (ListenableProperty.Listener l : listeners) {
toys.acceptUpdate(l);
}
}
private void updateFlowers() {
for (ListenableProperty.Listener l : listeners) {
flowers.acceptUpdate(l);
}
}
}
Listeners can then implement as many or as few of the listener interfaces as they please, or all of them via the convenience interface MyModel.AllPropertiesListener
You could also move the update routines for individual properties to the properties themselves.
for any type of Listeners have a class :
FlowerListerner implemts IBusinessEntityListener;
ToyListerner implemts IBusinessEntityListener;
and a listener list:
public class MyModel {
private List<Flower> flowers = new ArrayList<Flower>();
private List<Toys> toys = new ArrayList<Toys>();
private List<IBusinessEntityListener> flowerListeners =
new ArrayList<IBusinessEntityListener>();
private List<IBusinessEntityListener> toyListeners =
new ArrayList<IBusinessEntityListener>();
public void addListener(IBusinessEntityListener newListener) {
if(newListener instance of FlowerListener)
flowerListeners.add(newListener);
else if (newListener instance of ToyListener)
} toyListeners.add(newListener);
updateFlowerListeners() { ....}
updateToyListeners() { ....}
}
and any changes to each property reflect to related listeners.
UPDATE
another solution is that u have a list of interest in Listener Object:
Class Listener {
private List<Class> interests;
public Listener(List<Class> interests) {
this.interests = interests;
}
public boolean isInterested(Class clazz) {
return list.contains(clazz);
}
public void update() { ... }
}
an in model :
public class MyModel {
private List<Flower> flowers = new ArrayList<Flower>();
private List<Toys> toys = new ArrayList<Toys>();
private List<Listener> listeners =
new ArrayList<Listener>();
public void addListener(Listener newListener) {
listeners.add(newListener);
}
updateFlowerListeners() {
for(Listener l : listerners) {
if(l.isInterested(Flower.class)
l.update();
}
updateToyListeners() { ... }
}
Maybe I missunderstood JavaFX binding or there is a bug in SimpleStringProperty.
When I run this testcode my changed model value didn't get the new value. Test testBindingToModel fails. I thought my model should then be updated with the value of the TextField tf. But only the binding value of prop1Binding gets the value "test".
public class BindingTest {
private TextField tf;
private Model model;
private ModelBinding mb;
#Before
public void prepare() {
tf = new TextField();
model = new Model();
mb = new ModelBinding(model);
Bindings.bindBidirectional(tf.textProperty(), mb.prop1Binding);
}
#Test
public void testBindingToMB() {
tf.setText("test");
assertEquals(tf.getText(), mb.prop1Binding.get());
}
#Test
public void testBindingToModel() {
tf.setText("test");
assertEquals(tf.getText(), mb.prop1Binding.get());
assertEquals(tf.getText(), model.getProp1());
}
private static class ModelBinding {
private final StringProperty prop1Binding;
public ModelBinding(Model model) {
prop1Binding = new SimpleStringProperty(model, "prop1");
}
}
private static class Model {
private String prop1;
public String getProp1() {
return prop1;
}
public void setProp1(String prop1) {
this.prop1 = prop1;
}
}
}
Thanks for your help.
Best regards
Sebastian
EDIT:
With this class I can set the value of the model directly. I will test this class in the next days and comment on this post with my result.
public class MySimpleStringProperty extends SimpleStringProperty {
public MySimpleStringProperty(Object obj, String name) {
super(obj, name);
}
public MySimpleStringProperty(Object obj, String name, String initVal) {
super(obj, name, initVal);
}
#Override
public void set(String arg0) {
super.set(arg0);
if (this.getBean() != null) {
try {
Field f = this.getBean().getClass().getDeclaredField(this.getName());
f.setAccessible(true);
f.set(this.getBean(), arg0);
} catch (NoSuchFieldException e) {
// logging here
} catch (SecurityException e) {
// logging here
} catch (IllegalArgumentException e) {
// logging here
} catch (IllegalAccessException e) {
// logging here
}
}
}
}
This constructor doesn't attach SimpleStringProperty to a bean object unfortunately. It just says to SimpleStringProperty which bean property belongs to.
E.g., if you want to have a property in your class you should do it next way:
public static class Model {
private StringProperty prop1 =
new SimpleStringProperty(this, "prop1", "default_value");
public String getProp1() {
return prop1.get();
}
public void setProp1(String value) {
prop1.set(value);
}
public StringProperty prop1Property() {
return prop1;
}
}
Note, that there is no way to bind to your original Model class as it provides no events about setting new prop1 value. If you want to have observable model, you should use fx properties from the beginning.
Just figured out that there is provided the class JavaBeanStringProperty, which just fullfill my request.
Using this code I can directly bind the value of my bean to a StringProperty (included setting and getting of my value to / from my Bean).
binding = JavaBeanStringPropertyBuilder.create().beanClass(Model.class).bean(model).name("prop1").build();
The only problem I found is that when you change the value of the model after setting the binding, there is no update e.g. in the TextField.