How to use WeakChangeListener with JavaFx? - javafx

I wrote my TableCell implementation based on TableCell. I'm using ChangeListener, but it is not garbage collected. How to use WeakChangeListener in this case. Please see my code. How to switch it to WeakChangeListener.
changeListener = new ChangeListener<Object[]>() {
#Override
public void changed(ObservableValue<? extends Object[]> observable, Object[] oldValue, Object[] newValue) {
if (newValue != null && oldValue != null) {
if (oldValue[21].equals(newValue[21])) {
if (newValue[updateBasedValues] != null) {
if (!newValue[updateBasedValues].equals(oldValue[updateBasedValues])) {
justUpdated = true;
}
}
}
}
}
};
itemProperty().addListener(changeListener);

You need to keep a reference to the weak listener so that it is not garbage collected too soon.
Read this very good discussion on the oracle forum : https://community.oracle.com/thread/2396063
I've written an adapter class "WeakAdapter" that you can use or extend.
Example on how to use this class:
Instead of writing
myBooleanProperty.addListener(new ChangeListener<Boolean> () {...});
use:
weak = new WeakAdapter();
weak.addChangeListener(myBooleanProperty, new ChangeListener<Boolean> (){});
Here is the code of class WeakAdapter:
public class WeakAdapter {
ArrayList<Object> listenerRefs = new ArrayList<>();
public WeakAdapter() {
}
public void dipose() {
listenerRefs.clear();
}
public final <T> void remove(ChangeListener<T> listener) {
listenerRefs.remove(listener);
}
public final <T> void addChangeListener(final ObservableValue observable, ChangeListener<T> listener) {
listenerRefs.add(listener);
observable.addListener(new WeakChangeListener<>(listener));
}
public final <T> WeakListChangeListener<T> addListChangeListener(ListChangeListener<T> listener) {
listenerRefs.add(listener);
return new WeakListChangeListener<>(listener);
}
public void addInvalidationListener(final Observable listened, InvalidationListener listener) {
listenerRefs.add(listener);
listened.addListener(new WeakInvalidationListener(listener));
}
public final void stringBind(final StringProperty propertyToUpdate, final StringExpression expressionToListen) {
ChangeListener<String> listener = new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> ov, String t, String name) {
propertyToUpdate.set(name);
}
};
listenerRefs.add(listener);
expressionToListen.addListener(new WeakChangeListener<>(listener));
listener.changed(null, null, expressionToListen.get());
}
public final void booleanBind(final BooleanProperty propertyToUpdate, final BooleanExpression expressionToListen) {
ChangeListener<Boolean> listener = new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean name) {
propertyToUpdate.set(name);
}
};
listenerRefs.add(listener);
expressionToListen.addListener(new WeakChangeListener<>(listener));
propertyToUpdate.set(expressionToListen.get());
}
}

Related

javafx: display hyperlink in table cell

I am new to JAVAFX and am trying to display the hyperlink in table cell. I could able to display as a hyperlink but not able to open the link.
Please find the logic for the same.
Main method goes here::
public class Main extends Application {
private BorderPane root;
private TableView<Item> table;
private Scene scene;
private TableColumn<Item, String> nameColumn;
private TableColumn<Item, Hyperlink> urlColumn;
private ObservableList<Item> websiteList;
#Override
public void start(Stage primaryStage) {
root = new BorderPane();
//scene = new Scene(root, 400, 400);
table = new TableView<Item>();
//root.setCenter(table);
nameColumn = new TableColumn<>("Name");
nameColumn.setCellValueFactory(new PropertyValueFactory<>("websiteName"));
urlColumn = new TableColumn<>("Address");
urlColumn.setCellValueFactory(new PropertyValueFactory<>("hyperlink"));
urlColumn.setCellFactory(new HyperlinkCell());
table.getColumns().add(nameColumn);
table.getColumns().add(urlColumn);
websiteList = FXCollections.observableArrayList();
websiteList.add(new Item("Google", "https://www.google.co.in/"));
websiteList.add(new Item("Facebook", "www.facebook.com"));
websiteList.add(new Item("Superglobals", "www.superglobals.net"));
Hyperlink hyperlink = new Hyperlink("Go to Eclipse home page");
hyperlink.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
getHostServices().showDocument("https://www.google.co.in/");
}
});
root.getChildren().addAll(hyperlink);
// root.setBottom(hyperlink);
table.setItems(websiteList);
root.setCenter(table);
scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Item.java::
public class Item {
private String websiteName;
private Hyperlink hyperlink;
public Item(String websiteName, String websiteUrl) {
this.websiteName = websiteName;
this.hyperlink = new Hyperlink(websiteUrl);
}
public String getWebsiteName() {
return websiteName;
}
public void setWebsiteName(String websiteName) {
this.websiteName = websiteName;
}
public Hyperlink getHyperlink() {
return hyperlink;
}
public void setHyperlink(String websiteUrl) {
this.hyperlink = new Hyperlink(websiteUrl);
}
}
HyperlinkCell.java::
public class HyperlinkCell implements Callback<TableColumn<Item, Hyperlink>, TableCell<Item, Hyperlink>> {
private static HostServices hostServices ;
public static HostServices getHostServices() {
return hostServices ;
}
#Override
public TableCell<Item, Hyperlink> call(TableColumn<Item, Hyperlink> arg) {
TableCell<Item, Hyperlink> cell = new TableCell<Item, Hyperlink>() {
#Override
protected void updateItem(Hyperlink item, boolean empty) {
super.updateItem(item, empty);
setGraphic(empty ? null : item);
}
};
return cell;
}
}
Output is displaying like this but am not able to open the hyperlink. Please help us on this.
Thanks.
Just update the onAction handler of the hyperlink in the updateItem() method:
TableCell<Item, Hyperlink> cell = new TableCell<Item, Hyperlink>() {
#Override
protected void updateItem(Hyperlink item, boolean empty) {
super.updateItem(item, empty);
setGraphic(empty ? null : item);
if (! empty) {
item.setOnAction(e -> {
// handle event here...
});
}
}
};
Note that it's really not a good idea to use UI elements (such as Hyperlink) in your data classes (such as Item). I recommend you refactor this so that Item only holds the data:
public class Item {
private String websiteName;
private String url;
public Item(String websiteName, String websiteUrl) {
this.websiteName = websiteName;
this.url = websiteUrl;
}
public String getWebsiteName() {
return websiteName;
}
public void setWebsiteName(String websiteName) {
this.websiteName = websiteName;
}
public String getUrl() {
return url;
}
public void setUrl(String websiteUrl) {
this.url = websiteUrl;
}
}
And then:
private TableColumn<Item, String> urlColumn;
// ...
urlColumn = new TableColumn<>("Address");
urlColumn.setCellValueFactory(new PropertyValueFactory<>("url"));
urlColumn.setCellFactory(new HyperlinkCell());
Somewhere in start() you need to do
HyperlinkCell.setHostServices(getHostServices());
and finally define the Hyperlink in the cell. That way there is only one Hyperlink instance per cell, instead of one for every item in the table.
public class HyperlinkCell implements Callback<TableColumn<Item, Hyperlink>, TableCell<Item, Hyperlink>> {
private static HostServices hostServices ;
public static HostServices getHostServices() {
return hostServices ;
}
public static void setHostServices(HostServices hostServices) {
HyperlinkCell.hostServices = hostServices ;
}
#Override
public TableCell<Item, String> call(TableColumn<Item, String> arg) {
TableCell<Item, Hyperlink> cell = new TableCell<Item, Hyperlink>() {
private final Hyperlink hyperlink = new Hyperlink();
{
hyperlink.setOnAction(event -> {
String url = getItem();
hostServices.showDocument(url);
});
}
#Override
protected void updateItem(String url, boolean empty) {
super.updateItem(url, empty);
if (empty) {
setGraphic(null);
} else {
hyperlink.setText(url);
setGraphic(hyperlink);
}
}
};
return cell;
}
}

ViewPager returning empty array

When I start my app I want my customSwipeAdapter.java to wait until my savedImages ArrayList has received and been populated with the data from firebase. But instead my class is being ran and my whole page is empty because getCount() method is returning savedImages.size() as 0 because my arraylist hasn't been populated in time. Any help on maybe running my class when my array list is populated. Not sure what to do here :)
customSwipeAdapter.java
public class customSwipeAdapter extends PagerAdapter {
private Firebase mRef;
private Context ctx;
private LayoutInflater layoutInflator;
public customSwipeAdapter(Context ctx) {
this.ctx = ctx;
}
private int[] frontImages = {R.drawable.amen_parham, R.drawable.janel_parham, R.drawable.kevin_parham};
// Populate ArrayList with firebase data
List<String> savedImages = new ArrayList<String>();
Boolean goingToCallOnce = false;
Boolean finishedLoadingData = false;
#Override
public int getCount() {
return savedImages.size();
}
#Override
public boolean isViewFromObject(View view, Object o) {
return (view == o);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
getSavedImages_FromDB();
layoutInflator = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View item_view = layoutInflator.inflate(R.layout.swipe_layout, container, false);
final EasyFlipView mYourFlipView = (EasyFlipView) item_view.findViewById(R.id.flipView);
ImageView imageView_Front = (ImageView) item_view.findViewById(R.id.imageView_Front);
imageView_Front.setImageResource(frontImages[position]);
container.addView(item_view);
System.out.println(savedImages);
return item_view;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((RelativeLayout)object);
}
public void getSavedImages_FromDB() {
mRef = new Firebase("");
if (goingToCallOnce == false) {
goingToCallOnce = true;
mRef.child("Q6i3fI6lNdYYS0z5Jty4WUYE9g13").child("SavedImages").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String savedImage = (String) dataSnapshot.child("Image").getValue();
savedImages.add(0, savedImage);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
mRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
finishedLoadingData = true;
System.out.println("finishedLoadingData");
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
}
savedCardsViewController.java
public class savedCardsViewController extends AppCompatActivity {
private Swipe swipe;
ViewPager viewPager;
customSwipeAdapter adapter;
private Firebase mRef;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_saved_cards_view_controller);
viewPager = (ViewPager) findViewById(R.id.view_pager);
adapter = new customSwipeAdapter(this);
viewPager.setAdapter(adapter);
viewPager.setPageTransformer(false, new DefaultTransformer());
}
}
'Context' to 'ValueEventListener'
2'nd 'Context' to 'ValueEventListener'
I suggest you load the data from Firebase on your Activity first and then pass it as a parameter to the adapter's constructor. This way your CustomSwipeAdapter would look similar to this:
public class customSwipeAdapter extends PagerAdapter {
private Firebase mRef;
private Context ctx;
private LayoutInflater layoutInflator;
List<String> savedImages = new ArrayList<String>();
public customSwipeAdapter(Context ctx, List<String> savedImages){
this.ctx = ctx;
this.savedImages = savedImages
}
...
}
Another note on Loading data from firebase on the Activity: use A SingleValueListener with an iterator instead of onChildAdded:
mRef.child("Q6i3fI6lNdYYS0z5Jty4WUYE9g13").child("SavedImages").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Iterator<DataSnapshot> data = dataSnapshot.getChildren().iterator();
while(data.hasNext())
{
String savedImage = (String) data.next().child("Image").getValue();
savedImages.add(0, savedImage);
}
//Data has finished loading. Load your adapter
adapter = new customSwipeAdapter(this, savedImages);
viewPager.setAdapter(adapter);
viewPager.setPageTransformer(false, new DefaultTransformer());
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});

Retrieving list of images from Firebase, using GenericTypeIndicator<Map<String, String>>() { }

I need help with this, I've been trying to show a list of images into a recycler view but seriously I can't.
This is my database in firebase, shows the images from different events with randoms ids
This is my class:
public class Imagenes {
GenericTypeIndicator<Map<String,String>> images = new GenericTypeIndicator<Map<String, String>>(){};
public Imagenes(GenericTypeIndicator<Map<String, String>> images) {
this.images = images;
}
public GenericTypeIndicator<Map<String, String>> getImages() {
return images;
}
public void setImages(GenericTypeIndicator<Map<String, String>> images) {
this.images = images;
}
}
And yes its seems very bad but I don't know how to do it, since its not like a normal class with
name
phone
images
I mean i don't know their names, it's just random. I been reading that I have to use this kind of map, but I don't know how to use it in a class
This is my code in EventSingle:
public class EventoSingleActivity extends AppCompatActivity {
DatabaseReference mDatabaseEvento;
TextView TituloEvento;
RecyclerView mListaImagenes;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_evento_single);
mListaImagenes = (RecyclerView) findViewById(R.id.listaImagenes);
mListaImagenes.setHasFixedSize(true);
mListaImagenes.setLayoutManager(new LinearLayoutManager(this));
String Evento_key = getIntent().getExtras().getString("Evento_id");
Toast.makeText(this, Evento_key, Toast.LENGTH_SHORT).show();
mDatabaseEvento = FirebaseDatabase.getInstance().getReference().child("Evento").child(Evento_key).child("Imagenes");
System.out.println(mDatabaseEvento);
TituloEvento = (TextView) findViewById(R.id.tituloEventoField);
mDatabaseEvento.keepSynced(true);
}
#Override
protected void onStart() {
super.onStart();
FirebaseRecyclerAdapter<Imagenes, ImagenesEventoViewHolder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Imagenes, ImagenesEventoViewHolder>(
Imagenes.class,
R.layout.cardview_imagen,
ImagenesEventoViewHolder.class,
mDatabaseEvento
) {
#Override
protected void populateViewHolder(final ImagenesEventoViewHolder viewHolder, Imagenes model, int position) {
mDatabaseEvento.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println("Estoy entrando aqui");
Log.i("gguwu", "Ayudaaaa");
GenericTypeIndicator<Map<String,String>> ImagenesType = new GenericTypeIndicator<Map<String, String>>() { };
Map<String,String> Imagenes = dataSnapshot.getValue(ImagenesType);
if (Imagenes!=null ) {
for (String imagen: Imagenes.values()) {
System.out.println(imagen);
viewHolder.setImagen(getApplicationContext(), imagen);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
};
mListaImagenes.setAdapter(firebaseRecyclerAdapter);
}
public static class ImagenesEventoViewHolder extends RecyclerView.ViewHolder{
View view;
public ImagenesEventoViewHolder(View itemView) {
super(itemView);
view = itemView;
}
public void setImagen(Context ctx, String imagen){
ImageView imagencita = (ImageView) view.findViewById(R.id.imagen_item);
Picasso.with(ctx).load(imagen).into(imagencita);
}
}
}
I don't know how to make this works.
I tried everything, but I just cant. Help me please. Thanks!.
try ChildEventLister class for this task.
mDatabaseEvento =
mDatabaseEvento.addChildEventListener(new ChildEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println("Estoy entrando aqui");
Log.i("gguwu", "Ayudaaaa");
GenericTypeIndicator<Map<String,String>> ImagenesType = new GenericTypeIndicator<Map<String, String>>() { };
Map<String,String> Imagenes = dataSnapshot.getValue(ImagenesType);
if (Imagenes!=null ) {
for (String imagen: Imagenes.values()) {
System.out.println(imagen);
viewHolder.setImagen(getApplicationContext(), imagen);
}
}
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError)
}

Neither startEdit or setOnEditCommit getting called

I followed this example mentioned on this link -
UITableView - Better Editing through Binding?
I changed it a bit accordingly
Model class -
public static class TableData {
private String firstName, lastName;
private TableData(String first, String last) {
this.firstName = first;
this.lastName = last;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Custom Cell factory -
public static class TextFieldCellFactory implements Callback<TableColumn<TableData, String>, TableCell<TableData, String>> {
#Override
public TableCell<TableData, String> call(TableColumn<TableData, String> param) {
TextFieldCell textFieldCell = new TextFieldCell();
return textFieldCell;
}
public static class TextFieldCell extends TableCell<TableData, String> {
private TextField textField;
private StringProperty boundToCurrently = null;
private String newval = "";
public TextFieldCell() {
textField = new TextField();
textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
System.out.println("key pressed");
commitEdit(textField.getText());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
textField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
// commitEdit(newValue);
System.out.println("" + newValue);
newval = newValue;
}
});
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if(!newValue){
System.out.println("losing focus" + newval);
//commichange();
commitEdit(textField.getText());
}
}
});
this.setGraphic(textField);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
// Show the Text Field
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.setText(item);
} else {
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
}
setting onEditCommit-
c1.setOnEditCommit(
new EventHandler<TableColumn.CellEditEvent<TableData, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<TableData, String> t) {
System.out.println("ON edit commit" + t);
((TableData) t.getTableView().getItems().get(
t.getTablePosition().getRow())).setFirstName(t.getNewValue());
}
}
);
Problem 1- I want to know what happens when commitEdit() is called. Does it invoke setOnEditCommit ? If it does then why its not invoking setOnEditCoommit
Problem 2 - Why its not going into setOnEditCommit?
Problem 3 I applied startEdit just to check if its enters that field. But that method also is not getting invoked.
Can anyone specify what i am missing here.I don't want a workaround. I need to understand whats the reason behind it
P.S I have removed the binding properties as given in the link.
Your table never enters an editing state (because you never ask it to). Because the cell never has isEditing() return true, the default commitEdit() method becomes a no-op.
You need the TableView to know that it has to start editing a cell when the text field in that cell receives focus. You can do this by modifying the focus listener on the text field:
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
getTableView().edit(getIndex(), getTableColumn());
} else {
commitEdit(textField.getText());
}
}
});

Implementing an ObservableValue

I have this object:
public class Oggetto{
private int value;
private boolean valid;
public Oggetto(int value, boolean valid) {
this.value = value;
this.valid = valid;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public boolean isValid() {
return valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
}
and I would like implement an Observable object that fires events when something inside changes
Here the observable object:
public class OggettoOsservabile implements ObservableValue<Oggetto>{
private Oggetto value;
OggettoOsservabile(int i, boolean b) {
this.value=new Oggetto(i, b);
}
#Override
public void addListener(ChangeListener<? super Oggetto> listener) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public void removeListener(ChangeListener<? super Oggetto> listener) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public Oggetto getValue() {
return value;
}
#Override
public void addListener(InvalidationListener listener) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public void removeListener(InvalidationListener listener) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
i dont know how to proceed in order to detect a change in the class "Oggetto" and send a notification to the registeres listener.
OggettoOsservabile oggetto = new OggettoOsservabile(1, false);
oggetto.addListener(new ChangeListener<Oggetto>() {
public void changed(ObservableValue<? extends Oggetto> observable, Oggetto oldValue, Oggetto newValue) {
System.out.println("changed " + oldValue + "->" + newValue);
}
});
Implement your Oggetto class using standard JavaFX Properties:
import javafx.beans.property.BooleanProperty ;
import javafx.beans.property.IntegerProperty ;
import javafx.beans.property.SimpleBooleanProperty ;
import javafx.beans.property.SimpleIntegerProperty ;
public class Oggetto {
private final IntegerProperty value = new SimpleIntegerProperty() ;
public final IntegerProperty valueProperty() {
return value ;
}
public final int getValue() {
return value.get();
}
public final void setValue(int value) {
this.value.set(value);
}
private final BooleanProperty valid = new SimpleBooleanProperty();
public final BooleanProperty validProperty() {
return valid ;
}
public final boolean isValid() {
return valid.get();
}
public final void setValid(boolean valid) {
this.valid.set(valid);
}
public Oggetto(int value, boolean valid) {
setValue(value);
setValid(valid);
}
}
This may be all you need, as you can just observe the individual properties. But if you want a class that notifies invalidation listeners if either property changes, you can extend ObjectBinding:
import javafx.beans.binding.ObjectBinding ;
public class OggettoObservable extends ObjectBinding {
private final Oggetto value ;
public OggettoObservable(int value, boolean valid) {
this.value = new Oggetto(value, valid);
bind(this.value.valueProperty(), this.value.validProperty());
}
#Override
public Oggetto computeValue() {
return value ;
}
}
import javafx.beans.InvalidationListener;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
public class VerySimply implements ObservableValue<Integer> {
private int newValue;
public ChangeListener<Integer> listener = new ChangeListener<Integer>() {
#Override
public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue) {
System.out.println(" :) "+ newValue.intValue());
}
};
#Override
public void addListener(ChangeListener<? super Integer> listener) {
}
#Override
public void removeListener(ChangeListener<? super Integer> listener) {
}
#Override
public Integer getValue() {
return newValue;
}
#Override
public void addListener(InvalidationListener listener) {
}
#Override
public void removeListener(InvalidationListener listener) {
}
public void setNewValue(int newValue) {
int oldValue = this.newValue;
this.newValue = newValue;
listener.changed(this,oldValue,this.newValue);
}
}

Resources