adding text and image each celllist cell in MGWT - mgwt

Can you help me on adding of both text and image to the cell list cell in mgwt
I did only for text,but failing to place both text and image.waiting for your valuable response.

In a cell you can use whatever markup you want to use. This is no different from GWT standard cell widgets and their cells.
This is a basic example taken from the mgwt showcase and modified to include an img tag in the markup:
public abstract class BasicCell<T> implements Cell<T> {
private static Template TEMPLATE = GWT.create(Template.class);
public interface Template extends SafeHtmlTemplates {
#SafeHtmlTemplates.Template("<div>{0} <img src="{1}"/></div>")
SafeHtml content(String text, String imgUrl);
}
#Override
public void render(SafeHtmlBuilder safeHtmlBuilder, final T model) {
safeHtmlBuilder.append(TEMPLATE.content("text", "someUrl.jgp"));
}
public abstract String getDisplayString(T model);
#Override
public boolean canBeSelected(T model) {
return false;
}
}

I'm posting all the steps to work with CellList of MGWT by considering MyContacts bean. I think this will helps alot to the beginners. suppose the bean MyContacts having two properties named contactImagePath and contactPersonName. Now I'm showing all the MyContacts List in the cellList showing both image icon (generally having small dimensions, because it should be displayed in cell) and contact name. code will be..
public abstract class MyContactsCell<T> implements Cell<T> {
private static Template TEMPLATE = GWT.create(Template.class);
private String styleName;
public MynaContextBasicCell() {
styleName = "";
}
public interface Template extends SafeHtmlTemplates {
#SafeHtmlTemplates.Template("<div class=\"{0}\">" +
"<table>" +
"<tr>" +
"<td ><img style='float: left;' src=\"{1}\"></img></td> " +
"<td > </td> " +
"<td ><b>{2}</b></td> " +
"</tr>" +
"</table>"+
"</div>")
SafeHtml content(String classes, String contactImagePath,String contactPersonName);
}
#Override
public void render(SafeHtmlBuilder safeHtmlBuilder, final T model) {
safeHtmlBuilder.append(TEMPLATE.content(styleName, getContactImagePath(model), getContactPersonName(model)));
}
public abstract String getContactImagePath(T model);
public abstract String getContactPersonName(T model);
#Override
public boolean canBeSelected(T model) {
return false;
}
public void setStylename(String name) {
if (name == null) {
name = "";
}
styleName = name;
}
}
now define subclass of above
public class MyContactsCellSubType extends MyContactsCell<MyContacts> {
#Override
public String getContactImagePath(MyContacts model) {
return model.getContactImagePath();
}
#Override
public String getContactPersonName(MyContacts model) {
return model.getContactPersonName();
}
#Override
public boolean canBeSelected(MyContacts model) {
return true;
}
}
in your view,
MyContactsCellSubType contactsCellSubType = new MyContactsCellSubType();
CellList<MyContacts> contactsCellList = new CellList<MyContacts>(contactsCellSubType);
lets say 'myContactList' is the list of MyContacts that is available in your view .
contactsCellList.render(myContactList);
after rendering the list to contactsCellList and it to a scroll panel
contactsCellList.setRound(true);
myScrollPanel.setWidget(contactsCellList);
myScrollPanel.setScrollingEnabledX(false);;
myScrollPanel.setSize("100%", "100%");
add myScrollPanel to your view's main panel.

Related

Override TreeTableCell rendering in JavaFX

I am trying to change the rendering of a TextFieldTreeTableCell to show a string as a Hyperlink as opposed to plaintext, to no avail. It seems as though it should be doable using setSkin, but something like
setSkin((new HyperLink()).getSkin());
or
setSkin((new HyperLink(getItem())).getSkin());
does not help. Any insight on how this could be done?
What you are doing wrong
You are not using the right function to customize your cell: setSkin is is used for creating custom control skins and is generic to all kinds of controls not just cells, you should a use a cell factory instead.
You are not using the right superclass: TextFieldTreeTableCell is for creating a cell which contains a label that can be made into an editable TextField when you click on it. Such functionality is not useful when you want to "display a non-editable, clickable URL".
Approach you should use
Cells have a specific method for controlling their rendering which is preferred to the skin mechanism when working with cells. This cell specific mechanism is called a cell factory and is documented with an example in the Cell documentation.
TreeTableColumns allow you to set a cell factory on the column to control the rendering of the column cells. The relevant code for rendering a Hyperlink in a cell is below:
emailColumn.setCellFactory(param -> new TreeTableCell<Employee, String>() {
private Hyperlink link = new Hyperlink();
{
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
return;
}
link.setText(item);
link.setVisited(getTreeTableRow().getItem().isVisited());
link.setOnAction(event -> {
getTreeTableRow().getItem().setVisited(true);
sendLabel.setText("Send mail to: " + item);
});
setGraphic(link);
}
});
Sample Application
In the screen shot below, the user has just linked on the hyperlink for anna.black#example.com.
The sample code is a modified version of the code from the Oracle TreeTableView tutorial. The addition of a visited property to the Employee class is necessary to keep track of which items in the TreeTableView have been clicked on, so that the Hyperlink visited property can be appropriately set when the cell is updated.
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.*;
public class TreeTableViewSample extends Application {
private List<Employee> employees = Arrays.asList(
new Employee("Ethan Williams", "ethan.williams#example.com"),
new Employee("Emma Jones", "emma.jones#example.com"),
new Employee("Michael Brown", "michael.brown#example.com"),
new Employee("Anna Black", "anna.black#example.com"),
new Employee("Rodger York", "roger.york#example.com"),
new Employee("Susan Collins", "susan.collins#example.com"));
private final ImageView depIcon = new ImageView (
new Image("http://icons.iconarchive.com/icons/custom-icon-design/flatastic-10/16/Bear-icon.png")
);
final TreeItem<Employee> root =
new TreeItem<>(new Employee("Sales Department", ""), depIcon);
public static void main(String[] args) {
Application.launch(TreeTableViewSample.class, args);
}
final Label sendLabel = new Label();
#Override
public void start(Stage stage) {
root.setExpanded(true);
employees.forEach((employee) -> root.getChildren().add(new TreeItem<>(employee)));
stage.setTitle("Tree Table View Sample");
final Scene scene = new Scene(new VBox(), 400, 400);
scene.setFill(Color.LIGHTGRAY);
VBox sceneRoot = (VBox) scene.getRoot();
TreeTableColumn<Employee, String> empColumn =
new TreeTableColumn<>("Employee");
empColumn.setPrefWidth(150);
empColumn.setCellValueFactory(
(TreeTableColumn.CellDataFeatures<Employee, String> param) ->
new ReadOnlyStringWrapper(param.getValue().getValue().getName())
);
TreeTableColumn<Employee, String> emailColumn =
new TreeTableColumn<>("Email");
emailColumn.setPrefWidth(190);
emailColumn.setCellValueFactory(
(TreeTableColumn.CellDataFeatures<Employee, String> param) ->
new ReadOnlyStringWrapper(param.getValue().getValue().getEmail())
);
emailColumn.setCellFactory(param -> new TreeTableCell<Employee, String>() {
private Hyperlink link = new Hyperlink();
{
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
return;
}
link.setText(item);
link.setVisited(getTreeTableRow().getItem().isVisited());
link.setOnAction(event -> {
getTreeTableRow().getItem().setVisited(true);
sendLabel.setText("Send mail to: " + item);
});
setGraphic(link);
}
});
TreeTableView<Employee> treeTableView = new TreeTableView<>(root);
treeTableView.getColumns().setAll(empColumn, emailColumn);
sceneRoot.getChildren().addAll(treeTableView, sendLabel);
stage.setScene(scene);
stage.show();
}
public class Employee {
private SimpleStringProperty name;
private SimpleStringProperty email;
private SimpleBooleanProperty visited;
public SimpleStringProperty nameProperty() {
if (name == null) {
name = new SimpleStringProperty(this, "name");
}
return name;
}
public SimpleStringProperty emailProperty() {
if (email == null) {
email = new SimpleStringProperty(this, "email");
}
return email;
}
private Employee(String name, String email) {
this.name = new SimpleStringProperty(name);
this.email = new SimpleStringProperty(email);
this.visited = new SimpleBooleanProperty(false);
}
public String getName() {
return name.get();
}
public void setName(String fName) {
name.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
public void setVisited(boolean visited) {
this.visited.set(visited);
}
public boolean isVisited() {
return visited.get();
}
}
}

Sort JPA relational entity lists

I'm using PrimeFaces to display information in a DataTable. The data in the datatable is actually a List of entity objects which is generated by OpenJPA. And the entities have relations to other entities. This means that there are Lists inside Lists.
For example, an entity called Authors, which has many Books. The data List<Authors> is listed in the datatable. And in order to sort the List i use Collections.sort() which of course doesn't work when i try to sort on the Authors book title. Because the title field is an instance of the Book class.
How do i go about to sort the Lists when there are relationships like this?
Thanks in advance.
Here is an example of sorting by books title, you need to sort your books list :
public static void main(String args[]) {
List<Author> list = new ArrayList<Author>();
for (int i=0;i<5;i++){
Author a = new Author();
a.setName("author"+i);
List<Book> books = new ArrayList<Book>();
for(int j=0;j<4;j++){
Random r = new Random();
char c = (char) (r.nextInt(26) + 'a');
Book b = new Book();
b.setTitle(c+"title");
books.add(b);
}
a.setBooks(books);
list.add(a);
}
/*
* At this point of time you have Authors list which you want to sort by book title.
* So you can do something like below if you want to do it through Collections.sort
*/
for(Author a : list){
Collections.sort(a.getBooks(), new Comparator<Book>(){
#Override
public int compare(Book o1, Book o2) {
return o1.getTitle().compareToIgnoreCase(o2.getTitle());
}});
}
System.out.println(list);
}
Author and Book used in above example:
import java.util.List;
public class Author {
private List<Book> books;
private String name;
#Override
public String toString() {
return "Author [books=" + books + ", name=" + name + "]";
}
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Book.java:
public class Book {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
#Override
public String toString() {
return "Book [title=" + title + "]";
}
}

Dynamic JavaFX Bindings

My application has to deal with dynamic bindings.
I have a list of Java Bean objects that has a lot of attributes that must be editted.
There are multiple types of objects with different attributes.
I create a TreeView to list the objects.
Each time I select one object in the TreeView, I update a second container in the screen where I create dynamically the Labels and TextFields that are bound to the current object's properties.
I use JavaBeanStringProperty, JavaBeanIntegerProperty, and other objects of this family to create a property to interact with the Java Bean. That works perfectly.
I link each of those JavaBeanProperty objects to their corresponding TextField's TextAttribute so I can update the Bean when the UI is changed and vice-versa.
The problem is: Everytime I select a new Java Bean in the TreeView, all the objects previously created dynamically seem to be still alive. It will work for the first time I select the Bean and edit that, but for the second time further, it won't work.
I tried creating a list of created bindings so I can unbind them when selecting a new Bean, however it is not possible, since StringProperties and IntegerProperties do not share a common interface so I can unbind them.
Does anyone have an idea on how to deal with that?
Example:
Beans and their properties:
Bean1: name (String), amount (integer)
Bean2: name (String), type (String)
Bean3: name (String), address (string)
If I select the Bean1, I clear the container, and add those new objects to that:
A TextField to represent the name, a JavaBeanStringProperty to interface with the Bean, and bind it bidirectinally with the text field's TextProperty.
A TextField to represent the amount, a JavaBeanIntegerProperty to interface with the Bean, and bind it bidirectionally with the text field's TextProperty using a NumberConverter.
When I select the Bean2, I clear the container, and add those new objects to that:
A TextField to represent the name, a JavaBeanStringProperty to interface with the Bean, and bind it bidirectinally with the text field's TextProperty.
A TextField to represent the type, a JavaBeanStringProperty to interface with the Bean, and bind it bidirectionally with the text field's TextProperty.
When I select the Bean3, I clear the container, and add those new objects to that:
A TextField to represent the name, a JavaBeanStringProperty to interface with the Bean, and bind it bidirectinally with the text field's TextProperty.
A TextField to represent the address, a JavaBeanStringProperty to interface with the Bean, and bind it bidirectionally with the text field's TextProperty.
Here is a complete code example:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.ref.WeakReference;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.adapter.JavaBeanIntegerProperty;
import javafx.beans.property.adapter.JavaBeanIntegerPropertyBuilder;
import javafx.beans.property.adapter.JavaBeanStringProperty;
import javafx.beans.property.adapter.JavaBeanStringPropertyBuilder;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.converter.NumberStringConverter;
public class Main extends Application {
//=============================================================================================
public abstract class Bean {
public abstract void createUI(Pane container);
}
//=============================================================================================
public class Bean1 extends Bean {
private String name;
private int amount;
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public Bean1(String name, int amount) {
this.name = name;
this.amount = amount;
}
public String toString() { return name; }
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
System.out.println("Bean 1 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly added.");
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
System.out.println("Bean 1 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly removed.");
}
public String getName() { return name; }
public void setName(String name) {
String last = this.name;
this.name = name;
pcs.firePropertyChange("name", last, this.name);
System.out.println("Bean 1: name changed: " + last + " -> " + this.name);
}
public int getAmount() { return amount; }
public void setAmount(int amount) {
int last = this.amount;
this.amount = amount;
pcs.firePropertyChange("amount", last, this.amount);
System.out.println("Bean 1: amount changed: " + last + " -> " + this.amount);
}
public void createUI(Pane container) {
HBox nameContainer = new HBox();
Label nameLabel = new Label("name: ");
nameLabel.setPrefWidth(80);
TextField nameValue = new TextField();
nameValue.setPrefWidth(140);
try {
JavaBeanStringProperty wrapper = new JavaBeanStringPropertyBuilder().bean(this).name("name").build();
nameValue.textProperty().bindBidirectional(wrapper);
} catch (NoSuchMethodException e) {
e.printStackTrace();
System.out.println("Exception binding Bean 1 name property.");
}
nameContainer.getChildren().addAll(nameLabel, nameValue);
HBox amountContainer = new HBox();
Label amountLabel = new Label("amount: ");
amountLabel.setPrefWidth(80);
TextField amountValue = new TextField();
amountValue.setPrefWidth(140);
try {
JavaBeanIntegerProperty wrapper = new JavaBeanIntegerPropertyBuilder().bean(this).name("amount").build();
Bindings.bindBidirectional(amountValue.textProperty(), wrapper, new NumberStringConverter());
} catch (NoSuchMethodException e) {
e.printStackTrace();
System.out.println("Exception binding Bean 1 amount property.");
}
amountContainer.getChildren().addAll(amountLabel, amountValue);
container.getChildren().clear();
container.getChildren().addAll(nameContainer, amountContainer);
}
}
//=============================================================================================
public class Bean2 extends Bean {
private String name;
private String type;
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public Bean2(String name, String type) {
this.name = name;
this.type = type;
}
public String toString() { return name; }
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
System.out.println("Bean 2 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly added.");
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
System.out.println("Bean 2 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly removed.");
}
public String getName() { return name; }
public void setName(String name) {
String last = this.name;
this.name = name;
pcs.firePropertyChange("name", last, this.name);
System.out.println("Bean 2: name changed: " + last + " -> " + this.name);
}
public String getType() { return type; }
public void setType(String type) {
String last = this.type;
this.type = type;
pcs.firePropertyChange("type", last, this.type);
System.out.println("Bean 2: type changed: " + last + " -> " + this.type);
}
public void createUI(Pane container) {
HBox nameContainer = new HBox();
Label nameLabel = new Label("name: ");
nameLabel.setPrefWidth(80);
TextField nameValue = new TextField();
nameValue.setPrefWidth(140);
try {
JavaBeanStringProperty wrapper = new JavaBeanStringPropertyBuilder().bean(this).name("name").build();
nameValue.textProperty().bindBidirectional(wrapper);
} catch (NoSuchMethodException e) {
e.printStackTrace();
System.out.println("Exception binding Bean 2 name property.");
}
nameContainer.getChildren().addAll(nameLabel, nameValue);
HBox typeContainer = new HBox();
Label typeLabel = new Label("type: ");
typeLabel.setPrefWidth(80);
TextField typeValue = new TextField();
typeValue.setPrefWidth(140);
try {
JavaBeanStringProperty wrapper = new JavaBeanStringPropertyBuilder().bean(this).name("type").build();
typeValue.textProperty().bindBidirectional(wrapper);
} catch (NoSuchMethodException e) {
e.printStackTrace();
System.out.println("Exception binding Bean 2 type property.");
}
typeContainer.getChildren().addAll(typeLabel, typeValue);
container.getChildren().clear();
container.getChildren().addAll(nameContainer, typeContainer);
}
}
//=============================================================================================
public class Bean3 extends Bean {
private String name;
private String address;
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public Bean3(String name, String address) {
this.name = name;
this.address = address;
}
public String toString() { return name; }
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
System.out.println("Bean 3 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly added.");
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
System.out.println("Bean 3 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly removed.");
}
public String getName() { return name; }
public void setName(String name) {
String last = this.name;
this.name = name;
pcs.firePropertyChange("name", last, this.name);
System.out.println("Bean 3: name changed: " + last + " -> " + this.name);
}
public String getAddress() { return address; }
public void setAddress(String address) {
String last = this.address;
this.address = address;
pcs.firePropertyChange("type", last, this.address);
System.out.println("Bean 3: address changed: " + last + " -> " + this.address);
}
public void createUI(Pane container) {
HBox nameContainer = new HBox();
Label nameLabel = new Label("name: ");
nameLabel.setPrefWidth(80);
TextField nameValue = new TextField();
nameValue.setPrefWidth(140);
try {
JavaBeanStringProperty wrapper = new JavaBeanStringPropertyBuilder().bean(this).name("name").build();
nameValue.textProperty().bindBidirectional(wrapper);
} catch (NoSuchMethodException e) {
e.printStackTrace();
System.out.println("Exception binding Bean 3 name property.");
}
nameContainer.getChildren().addAll(nameLabel, nameValue);
HBox addressContainer = new HBox();
Label addressLabel = new Label("type: ");
addressLabel.setPrefWidth(80);
TextField addressValue = new TextField();
addressValue.setPrefWidth(140);
try {
JavaBeanStringProperty wrapper = new JavaBeanStringPropertyBuilder().bean(this).name("address").build();
addressValue.textProperty().bindBidirectional(wrapper);
} catch (NoSuchMethodException e) {
e.printStackTrace();
System.out.println("Exception binding Bean 3 address property.");
}
addressContainer.getChildren().addAll(addressLabel, addressValue);
container.getChildren().clear();
container.getChildren().addAll(nameContainer, addressContainer);
}
}
//=============================================================================================
private class TreeItemRefresher implements PropertyChangeListener {
private String property;
private WeakReference<TreeItem<Bean>> treeItem;
TreeItemRefresher(String property, TreeItem<Bean> treeItem) {
this.property = property;
this.treeItem = new WeakReference<>(treeItem);
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (property.equals(evt.getPropertyName())) {
// Workaround to repaint the tree item when its value object changes.
TreeItem<Bean> item = treeItem.get();
if (item != null) {
item.setExpanded(false);
item.setExpanded(true);
}
}
}
}
//=============================================================================================
private TreeView<Bean> treeView = new TreeView<>();
private VBox container = new VBox();
//=============================================================================================
#SuppressWarnings("unchecked")
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Dynamic Bindings tests.");
HBox mainContainer = new HBox();
container.setPadding(new Insets(10));
// Creating beans.
Bean1 bean1 = new Bean1("Bean 1", 10);
Bean2 bean2 = new Bean2("Bean 2", "Type O");
Bean3 bean3 = new Bean3("Bean 3", "10, Central Park Av.");
// Creating TreeView
treeView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
treeView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TreeItem<Bean>>() {
#Override
public void changed(ObservableValue<? extends TreeItem<Bean>> arg0, TreeItem<Bean> oldValue, TreeItem<Bean> newValue) {
Bean newItem = newValue.getValue();
newItem.createUI(container);
}
});
TreeItem<Bean> bean1item = new TreeItem<Bean>(bean1);
bean1.addPropertyChangeListener(new TreeItemRefresher("name", bean1item));
TreeItem<Bean> bean2item = new TreeItem<Bean>(bean2);
bean2.addPropertyChangeListener(new TreeItemRefresher("name", bean2item));
TreeItem<Bean> bean3item = new TreeItem<Bean>(bean3);
bean3.addPropertyChangeListener(new TreeItemRefresher("name", bean3item));
bean1item.setExpanded(true);
treeView.setRoot(bean1item);
bean1item.getChildren().addAll(bean2item, bean3item);
mainContainer.getChildren().addAll(treeView, container);
Scene scene = new Scene(mainContainer, 500, 300, Color.WHITE);
primaryStage.setScene(scene);
primaryStage.show();
}
//=============================================================================================
public Main() {
}
//=============================================================================================
public static void main(String[] args) {
launch(Main.class, args);
}
}
Notice sometimes changes to a field is not handled perfectly.
Notice also the number of PropertyChangeListeners are always increasing as you change the selected Bean. I know why that happens, but I really don't know how to deal with that.
Is there a better way to do it?
Notice I can't change the Bean objects. They are not under my control.
Thanks very much.
Did you try using JFXtras BeanPathAdaptor? It is a wonderful library for dynamic bindings based on POJOS.
You just have to use
beanPathAdaptor.setBean(myNewBean);
When you want to change your bean!
Here is a link:
http://ugate.wordpress.com/2012/07/30/javafx-programmatic-pojo-expression-bindings-part-iii/
EDIT
The other way I see is to define 3 custom components in JavaFX, one for each type. In them you define the complete binding to your bean with the view binded to your bean and add an input method such as "setBean1(Bean1 bean)"
Then, on selection you instanciate this component, show it and set its content with "setBean1(...)". You can do the same with the other types. Of course all of the bindings are static now, but the creation and use of components are dynamics, it is component-oriented and is high-coupled (view-model).
To optimize it, you can keep all the already created components in memory and use them again after to prevent from paying the instanciation of the view again, best way should be setting them to visible==false when you don't use them, you could even make an eager instanciation of all possible cases of components at initialization if you prefer.

JavaFX 2 TableView how to update cell when object is changed

I'm creating a TableView to show information regarding a list of custom objects (EntityEvents).
The table view must have 2 columns.
First column to show the corresponding EntityEvent's name.
The second column would display a button. The button text deppends on a property of the EntityEvent. If the property is ZERO, it would be "Create", otherwise "Edit".
I managed to do it all just fine, except that I can't find a way to update the TableView line when the corresponding EntityEvent object is changed.
Very Important: I can't change the EntityEvent class to use JavaFX properties, since they are not under my control. This class uses PropertyChangeSupport to notify listeners when the monitored property is changed.
Note:
I realize that adding new elements to the List would PROBABLY cause the TableView to repaint itself, but that is not what I need. I say PROBABLY because I've read about some bugs that affect this behavior.
I tried using this approach to force the repaint, by I couldn't make it work.
Does anyone knows how to do it?
Thanks very much.
Here is a reduced code example that illustrates the scenario:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Callback;
public class Main extends Application {
//=============================================================================================
public class EntityEvent {
private String m_Name;
private PropertyChangeSupport m_NamePCS = new PropertyChangeSupport(this);
private int m_ActionCounter;
private PropertyChangeSupport m_ActionCounterPCS = new PropertyChangeSupport(this);
public EntityEvent(String name, int actionCounter) {
m_Name = name;
m_ActionCounter = actionCounter;
}
public String getName() {
return m_Name;
}
public void setName(String name) {
String lastName = m_Name;
m_Name = name;
System.out.println("Name changed: " + lastName + " -> " + m_Name);
m_NamePCS.firePropertyChange("Name", lastName, m_Name);
}
public void addNameChangeListener(PropertyChangeListener listener) {
m_NamePCS.addPropertyChangeListener(listener);
}
public int getActionCounter() {
return m_ActionCounter;
}
public void setActionCounter(int actionCounter) {
int lastActionCounter = m_ActionCounter;
m_ActionCounter = actionCounter;
System.out.println(m_Name + ": ActionCounter changed: " + lastActionCounter + " -> " + m_ActionCounter);
m_ActionCounterPCS.firePropertyChange("ActionCounter", lastActionCounter, m_ActionCounter);
}
public void addActionCounterChangeListener(PropertyChangeListener listener) {
m_ActionCounterPCS.addPropertyChangeListener(listener);
}
}
//=============================================================================================
private class AddPersonCell extends TableCell<EntityEvent, String> {
Button m_Button = new Button("Undefined");
StackPane m_Padded = new StackPane();
AddPersonCell(final TableView<EntityEvent> table) {
m_Padded.setPadding(new Insets(3));
m_Padded.getChildren().add(m_Button);
m_Button.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
// Do something
}
});
}
#Override protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(m_Padded);
m_Button.setText(item);
}
}
}
//=============================================================================================
private ObservableList<EntityEvent> m_EventList;
//=============================================================================================
#SuppressWarnings("unchecked")
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Table View test.");
VBox container = new VBox();
m_EventList = FXCollections.observableArrayList(
new EntityEvent("Event 1", -1),
new EntityEvent("Event 2", 0),
new EntityEvent("Event 3", 1)
);
final TableView<EntityEvent> table = new TableView<EntityEvent>();
table.setItems(m_EventList);
TableColumn<EntityEvent, String> eventsColumn = new TableColumn<>("Events");
TableColumn<EntityEvent, String> actionCol = new TableColumn<>("Actions");
actionCol.setSortable(false);
eventsColumn.setCellValueFactory(new Callback<CellDataFeatures<EntityEvent, String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<EntityEvent, String> p) {
EntityEvent event = p.getValue();
event.addActionCounterChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent event) {
// TODO: I'd like to update the table cell information.
}
});
return new ReadOnlyStringWrapper(event.getName());
}
});
actionCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<EntityEvent, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<EntityEvent, String> ev) {
String text = "NONE";
if(ev.getValue() != null) {
text = (ev.getValue().getActionCounter() != 0) ? "Edit" : "Create";
}
return new ReadOnlyStringWrapper(text);
}
});
// create a cell value factory with an add button for each row in the table.
actionCol.setCellFactory(new Callback<TableColumn<EntityEvent, String>, TableCell<EntityEvent, String>>() {
#Override
public TableCell<EntityEvent, String> call(TableColumn<EntityEvent, String> personBooleanTableColumn) {
return new AddPersonCell(table);
}
});
table.getColumns().setAll(eventsColumn, actionCol);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
// Add Resources Button
Button btnInc = new Button("+");
btnInc.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent ev) {
System.out.println("+ clicked.");
EntityEvent entityEvent = table.getSelectionModel().getSelectedItem();
if (entityEvent == null) {
System.out.println("No Event selected.");
return;
}
entityEvent.setActionCounter(entityEvent.getActionCounter() + 1);
// TODO: I expected the TableView to be updated since I modified the object.
}
});
// Add Resources Button
Button btnDec = new Button("-");
btnDec.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent ev) {
System.out.println("- clicked.");
EntityEvent entityEvent = table.getSelectionModel().getSelectedItem();
if (entityEvent == null) {
System.out.println("No Event selected.");
return;
}
entityEvent.setActionCounter(entityEvent.getActionCounter() - 1);
// TODO: I expected the TableView to be updated since I modified the object.
}
});
container.getChildren().add(table);
container.getChildren().add(btnInc);
container.getChildren().add(btnDec);
Scene scene = new Scene(container, 300, 600, Color.WHITE);
primaryStage.setScene(scene);
primaryStage.show();
}
//=============================================================================================
public Main() {
}
//=============================================================================================
public static void main(String[] args) {
launch(Main.class, args);
}
}
Try the javafx.beans.property.adapter classes, particularly JavaBeanStringProperty and JavaBeanIntegerProperty. I haven't used these, but I think you can do something like
TableColumn<EntityEvent, Integer> actionCol = new TableColumn<>("Actions");
actionCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<EntityEvent, Integer> ev) {
return new JavaBeanIntegerPropertyBuilder().bean(ev.getValue()).name("actionCounter").build();
});
// ...
public class AddPersonCell extends TableCell<EntityEvent, Integer>() {
final Button button = new Button();
public AddPersonCell() {
setPadding(new Insets(3));
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
button.setOnAction(...);
}
#Override
public void updateItem(Integer actionCounter, boolean empty) {
if (empty) {
setGraphic(null);
} else {
if (actionCounter.intValue()==0) {
button.setText("Create");
} else {
button.setText("Add");
}
setGraphic(button);
}
}
}
As I said, I haven't used the Java bean property adapter classes, but the idea is that they "translate" property change events to JavaFX change events. I just typed this in here without testing, but it should at least give you something to start with.
UPDATE: After a little experimenting, I don't think this approach will work if your EntityEvent is really set up the way you showed it in your code example. The standard Java beans bound properties pattern (which the JavaFX property adapters rely on) has a single property change listener and an addPropertyChangeListener(...) method. (The listeners can query the event to see which property changed.)
I think if you do
public class EntityEvent {
private String m_Name;
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private int m_ActionCounter;
public EntityEvent(String name, int actionCounter) {
m_Name = name;
m_ActionCounter = actionCounter;
}
public String getName() {
return m_Name;
}
public void setName(String name) {
String lastName = m_Name;
m_Name = name;
System.out.println("Name changed: " + lastName + " -> " + m_Name);
pcs.firePropertyChange("name", lastName, m_Name);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
public int getActionCounter() {
return m_ActionCounter;
}
public void setActionCounter(int actionCounter) {
int lastActionCounter = m_ActionCounter;
m_ActionCounter = actionCounter;
System.out.println(m_Name + ": ActionCounter changed: " + lastActionCounter + " -> " + m_ActionCounter);
pcs.firePropertyChange("ActionCounter", lastActionCounter, m_ActionCounter);
}
}
it will work with the adapter classes above. Obviously, if you have existing code calling the addActionChangeListener and addNameChangeListener methods you would want to keep those existing methods and the existing property change listeners, but I see no reason you can't have both.

Alternative for cloning in LWUIT Component object

Situation:-
In my code I have to use the LWUIT Component object for the listview controls. The controls are dynamic and hence can be in any number.
Right now I am creating Component objects according to the controls(in numbers) i.e.- for every control to be created first the Component object is creating.
This process slows down the rendering of the listview when the controls are increasing.
Solution:-
If I create the Component object and use it in a loop for all the controls it is taking the reference of the object and hence displays all the listview items(controls) with the same data.
Now I am able to think of one last option of Cloning my object and using it to create the controls.
Problem:-
But I can't find any way in LWUIT by which I can achieve the copying of object.
What can be the alternatives in LWUIT to solve this problem?
P.S.-The listview items are of same type, but with different data.
Use a List component and the Renderer design pattern to create a "rubber stamp" component where you can display a large number of elements easily. See an explanation of this in the Codename One blog.
Create these classes first :
public class ListUtil {
private Vector data = new Vector();
private Content[] contents;
public ListUtil(Vector vData)
{
data = vData;
contents = new Content[vData.size()];
}
public List createList(Display display, CListCell renderer, ActionListener listener)
{
CList theList;
for(int i = 0; i < data.size(); i++)
{
contents[i] = new Content(String.valueOf(data.elementAt(i)));
}
theList = new CList(display, contents, renderer, listener);
return theList;
}
}
public class Content
{
private String row;
public Content(String row)
{
this.row = row;
}
public String getRow()
{
return (row);
}
}
public class CListCell extends Container implements ListCellRenderer {
private Label focus = new Label("");
public CListCell()
{
super();
// create and add the components here among the components which will display data
}
public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected)
{
Content entry = null;
if (value instanceof Content)
entry = (Content)value;
componentDisplayingDataAddedIntoThisListCellRenderer.setText(entry.getRow());
return this;
}
public Component getListFocusComponent(List arg0)
{
return focus;
}
}
public class CList extends List {
private Display disp;
public CList(Display display, Object[] data, CListCell renderer, ActionListener actionListener)
{
super(data);
setListCellRenderer(renderer);
setIgnoreFocusComponentWhenUnfocused(true);
addActionListener(actionListener);
setScrollAnimationSpeed(getScrollAnimationSpeed()/4);
disp = display;
}
public void pointerReleased(int x,int y)
{
if (isEnabled() && hasFocus())
super.pointerReleased(x, y);
}
public void keyReleased(int keyCode)
{
if (isEnabled() && hasFocus())
{
if (disp.getGameAction(keyCode) == Display.GAME_FIRE)
pointerReleased(getX(),getY());
else
super.keyReleased(keyCode);
}
}
}
To create your List and add it to a Form :
public class myForm extends Form implements ActionListener
{
private Vector listSource = // your vector of data
private CListCell renderer = new CListCell();
private List theList = (new ListUtil(listSource)).createList(Display.getInstance(),renderer, this);
...
public void actionPerformed(ActionEvent evt)
{
if (evt.getSource() == theList)
doSomething();
}
}

Resources