So I had asked a question similar to this, but I don't think the answer I got worked with what I was trying to do.
Say I have this class:
Java Code
public class Section
{
private String sDocumentTitle;
private String sHeadingTitle;
private String sText;
public ArrayList<Section> aSiblingSection = new ArrayList<Section>();
public ArrayList<Section> aChildSection = new ArrayList<Section>();
public ArrayList<image> aImages = new ArrayList<image>();
public void setName(String docTitle)
{
//set passed parameter as name
sDocumentTitle = docTitle;
}
public void addName (String docTitle)
{
//adds remaining Title String together
sDocumentTitle += (" " + docTitle);
}
public String getName()
{
//return the set name
return sDocumentTitle;
}
public void setSection(String section)
{
//set passed parameter as name
sHeadingTitle = section;
}
public void addSection(String section)
{
//adds section parts together
sHeadingTitle += ("" + section);
}
public String getSection()
{
//return the set name
return sHeadingTitle;
}
public void setText(String text)
{
//set passed parameter as name
sText = text;
}
public void addText(String text)
{
//adds
sText += (" " + text);
}
public String getText()
{
//return the set name
return sText;
}
public ArrayList getChildSection()
{
return aChildSection;
}
}
And a child section initialized in this manner in a driver class...
Section aSection = new Section();
aMainSection.get(0).aChildSection.add(aSection);
Essentially, could someone give me an idea of how I would I add a method in the section class which returns the parents from an array list of 'aChildSection'?
Add a constructor
private final Section parent;
public Section(Section parent) {
this.parent = parent;
}
public Section getParent() {
return parent;
}
When you add a child
Section aSection = new Section(aMainSection.get(0));
aMainSection.get(0).aChildSection.add(aSection);
With your model, you can't. Add a parent section:
private Section parent;
and set that for every child session (it will be null in the parent session)
I guess, each section (except the main section) has one parent. The trick is, that a section needs to know it's parent section.
A widely used pattern is to set the parent with the constructor and add some logic to the constructor so that it will register the section as parent's child automatically:
public Section(Section parent) {
this.parent = parent; // remember your parent
parent.addChild(this); // register yourself as your parent's child
}
Then use this code to add a section:
Section mainSection = aMainSection.get(0); // ugly!!
Section section = new Section(mainSection);
Refactoring tip - declare all your fields private and implement getters. Even better if those getters don't return the internal lists but just values from the list.
Related
I am still new to Vaadin so, please bear with it.
We are currently migrating from Vaadin framework 8.0 to 8.3.2. One of the reasons of doing is that there's a requirement of using tree for the menu. Since 8.0 doesn't have tree, the workaround for generating a menu is by instantiating an inner Button class with the help of an Enum class in a loop (for user permission control):
public final class ValoMenuItemButton extends Button {
private static final String STYLE_SELECTED = "selected";
private final DashboardViewType view;
public ValoMenuItemButton(final DashboardViewType view) {
this.view = view;
setPrimaryStyleName("valo-menu-item");
setIcon(view.getIcon());
setCaption(view.getViewName().substring(0, 1).toUpperCase()
+ view.getViewName().substring(1));
DashboardEventBus.register(this);
addClickListener(new ClickListener() {
#Override
public void buttonClick(final ClickEvent event) {
UI.getCurrent().getNavigator()
.navigateTo(view.getViewName());
}
});
}
#Subscribe
public void postViewChange(final PostViewChangeEvent event) {
removeStyleName(STYLE_SELECTED);
if (event.getView() == view) {
addStyleName(STYLE_SELECTED);
}
}
}
The enum class structure is built in this manner:
AUDIT("Receipt Validation", RcptValidation.class, FontAwesome.BAR_CHART_O, false),
AUDIT1("Matching - Receipt not in SYCARDA", RcptNotInSycarda.class, FontAwesome.BAR_CHART_O, false),
AUDIT2("Matching - Receipt not in POS", RcptNotInPos.class, FontAwesome.BAR_CHART_O, false),
AUDIT3("Missing Sequence", MissSequence.class, FontAwesome.BAR_CHART_O, false),
AUDIT4("*Debug Purposes", LineAmtVsTotal.class, FontAwesome.BAR_CHART_O, false);
private DashboardViewType(final String viewName,
final Class<? extends View> viewClass, final Resource icon,
final boolean stateful) {
this.viewName = viewName;
this.viewClass = viewClass;
this.icon = icon;
this.stateful = stateful;
}
So far, I've not found any examples that are written around the v8 framework, while most of the sample code that I've seen are based on v7 framework.
I've attempted to write something like this, but the tree sub menus do not come out as it is (I've left out the expand and collapse event as that can be handled later).
My attempted code on the tree is this:
TreeData <String> treeData = new TreeData();
treeData.addRootItems("Dashboard","Sales","Sales Pattern","Top SKUs","Audit");
// The loop starts here (for DashboardViewType view: DashboardViewType.values)
if(enabled){
if(StringUtils.startsWith(view.getViewName(), "SALES")){
if (StringUtils.contains(view.getViewName(),"SALES_PATTERN")){
treeData.addItem( "Sales Pattern", view.getViewName());
}else{ treeData.addItem( "Sales", view.getViewName());
}
}else if (StringUtils.startsWith(view.getViewName(), "TOP_SKUS")){
treeData.addItem( "Top SKUs", view.getViewName());
}else if (StringUtils.startsWith(view.getViewName(), "AUDIT")){
treeData.addItem( "Audit", view.getViewName());
}else if (StringUtils.startsWith(view.getViewName(), "DASHBOARD")){
treeData.addItem( "Dashboard", view.getViewName());
}
DashboardEventBus.register(view);
}
// loop ends here
Tree<String> tree = new Tree<>("Sycarda Dashboard");
tree.setDataProvider(new TreeDataProvider<>(treeData));
tree.setItemIconGenerator(item -> { return FontAwesome.BAR_CHART_O; });
tree.expand("Sales","Sales Pattern","Top SKUs","Audit");
tree.addSelectionListener(e -> new Button.ClickListener() {
#Override public void buttonClick(Button.ClickEvent event) {
DashboardEventBus.register(event);
UI.getCurrent().getNavigator().navigateTo(event.getClass().getName());
}
});
This was posted originally at the Vaadin forum, but since there were no answers to that, I am putting it here. I'd appreciate if there's any input or another approach for this problem.
Thanks in advance.
In Vaadin 8 you can simply define the "get children" method when adding the data. In your case the enum class should provide some method like "getSubItems", which you could then set as the value provider. The following example shows it in a similar way, where "rootItems" is simply the same as your top level enum instances and MenuItem the same as your enumeration.
static {
rootItems = Arrays.asList(...);
}
#PostConstruct
private void init() {
Tree<MenuItem> tree = new Tree<>();
tree.setItems(rootItems, MenuItem::getSubItems);
}
private class MenuItem {
private String name;
private Resource icon;
private Collection<MenuItem> subItems;
public Collection<MenuItem> getSubItems() {
return subItems;
}
// ... other getter and constructor omitted;
}
Someone has shown an example and it is similar to what Stefan mentioned. In context with my requirement, the steps involved include:
Create a wrapper class that includes:
private DashboardViewType view;
private Resource icon;
private boolean stateful;
private Class<? extends View> viewClass;
private String viewName;
//Create the get / set methods for those attributes above
//Constructor for the wrapper class is below.
public TreeMenuItem(DashboardViewType view){
this.view = view;
}
For the Enum class additional main menu items are added. Default main class can be used since you can't put a null.
public enum DashboardViewType {
SALES("Sales",DashboardView.class,FontAwesome.HOME,false),
SALES_PATTERN("Sales Pattern",DashboardView.class,FontAwesome.HOME,false),
TOP_SKUs("Top SKUs",DashboardView.class,FontAwesome.HOME,false),
AUDIT("Audit",DashboardView.class,FontAwesome.HOME,false)
}
The tree is built in this manner:
private Component buildTree(){
Tree<TreeMenuItem> tree = new Tree<>();
TreeData<TreeMenuItem> treeData = new TreeData<>();
//This is for items that have no child.
TreeMenuItem dashboardItem = new TreeMenuItem(DashboardViewType.DASHBOARD);
dashboardItem.setIcon(VaadinIcons.HOME_O);
dashboardItem.setStateful(DashboardViewType.DASHBOARD.isStateful());
dashboardItem.setViewName(DashboardViewType.DASHBOARD.getViewName());
treeData.addItem(null, dashboardItem);
for (DashboardViewType type : DashboardViewType.values()) {
TreeMenuItem menuItem = new TreeMenuItem(type);
menuItem.setIcon(VaadinIcons.HOME_O);
menuItem.setViewName(type.getViewName());
menuItem.setStateful(false);
treeData.addItem(null, menuItem);
getSubMenuItems(type).forEach(subView -> {
TreeMenuItem subItem = new TreeMenuItem(subView);
subItem.setViewName(subView.getViewName().substring(0, 1).toUpperCase()
+ subView.getViewName().substring(1));
subItem.setIcon(subView.getIcon());
subItem.setStateful(subView.isStateful());
subItem.setView(subView);
subItem.setViewClass(subView.getViewClass());
treeData.addItem(menuItem, subItem);
});
}
}
tree.setDataProvider(new TreeDataProvider<>(treeData));
tree.setItemIconGenerator(TreeMenuItem::getIcon);
tree.setItemCaptionGenerator(TreeMenuItem::getViewName);
tree.addItemClickListener((Tree.ItemClick<TreeMenuItem> event) -> {
DashboardEventBus.register(event.getItem().getView()); UI.getCurrent().getNavigator().navigateTo(event.getItem().getViewName());
});
}
The logic to create subviews:
private List getSubMenuItems(DashboardViewType type) {
List<DashboardViewType> dashboardList;
switch(type){
case TOP_SKUs:
dashboardList = new LinkedList<>(Arrays.asList(DashboardViewType.TOP_SKUs1,
DashboardViewType.TOP_SKUs2,
DashboardViewType.TOP_SKUs3,
DashboardViewType.TOP_SKUs4));
filterByUserLevel(dashboardList,subACL4);
return dashboardList;
case AUDIT:
dashboardList = new LinkedList<>(Arrays.asList(DashboardViewType.AUDIT1,
DashboardViewType.AUDIT2,
DashboardViewType.AUDIT3,
DashboardViewType.AUDIT4,
DashboardViewType.AUDIT5));
filterByUserLevel(dashboardList,subACL5);
return dashboardList;
case DASHBOARD:
break;
default:
break;
}
return Collections.emptyList();
}
Add additional cases if required so. After that, the function controls remove the elements that are not part of the user level:
private List<DashboardType> filterByUserLevel(List<DashboardType>list, String u){
if(list.size() == subACL.length()){
for(int i=0; i<list.size(); i++){
if(StringUtils.substring(subACL, i, i+1).equalsIgnoreCase("0")){
list.remove(i);
}
}
Collections.sort(list);
return list;
//this removes unwanted sub-menu items according current user level.
}
}
Hi guys,this is my first question on StackOverflow
I am kind of new to java and I need to solve this uml diagram .
I got a solution from one of my classmates but I don't think it's correct and I did it my way. My question is which one of the solutions is correct? I know that the type of relation is an association one . Not an inheritance
Her code
class Sensor {
protected int value;
protected String location;
public Sensor() { // default constructor
value = 0;
location = "North-West";
}
public Sensor(int value, String location) { // overridden constructor
this.value = value;
this.location = location;
}
protected int getValue() { // value getter
return value;
}
protected void setValue(int v) { // value setter
this.value = v;
}
protected void displaySenzorInfo() { // display information on the sensor
System.out.println("Temperature is " + value + ", located " + location + ".");
}
}
class Controller extends Sensor {
protected String name;
public Controller(String name) { // overridden constructor
this.name = name;
}
public Controller(String name, int value, String location) { // overridden
// instructor
this.name = name;
super.value = value;
super.location = location;
}
public Controller() { // default constructor, which creates a new Sensor()
//Sensor s = new Sensor();
}
protected void checkTemperature() { // checks temperature of sensor
System.out.println("Temperature of " + name + " is " + super.value + ", located at " + super.location + ".");
}
}
public class E3 {
public static void main(String[] args) {
Controller control = new Controller();
control.displaySenzorInfo();
Controller c = new Controller("Pizza", 30, "North");
c.checkTemperature();
}
}
My code
class Sensor{
int value;
String location;
Sensor(){
value=0;
location="Sibiu";
}
Sensor(int value,String location){
this.value=value;
this.location=location;
}
int getValue(){
return value;
}
void setValue(int v){
this.value=v;
}
void displaySenzorInfo(){
System.out.println("Temperature is " + value + ", located " + location + ".");
}
}
class Controller{
Sensor tempSensor;
String name;
Controller(){
name="Sibiu";
tempSensor=30;
}
Controller (String name,Sensor tempSensor){
this.name=name;
this.tempSensor=tempSensor;
}
void checkTemperature(Sensor tempSensor){
if (tempSensor>=30)
System.out.println("the temperature is too high!");
else
System.out.println("the temp is too low" );
}
}
public class E3{
public static void main(String []args){
Sensor s1=new Sensor();
Controller c1=new Controller();
c1.displaySenzorInfo();
Controller c2=new Controller(30,"Oliver");
}
}
Please guys. If you have some suggestions or if you see any problems in m program tell me. I know that I will have some errors because I didn't work at this exercise in any IDE because I am at work and I don't have any . Thank you!!!
your solution is the correct one. As you mentioned already, it is an association and not an inheritance. You can see how an inheritance looks like on wikipedia: https://en.wikipedia.org/wiki/Class_diagram
Though overall coding (MyCode) for relationship from the given diagram is OK, I have following observations. (Her code) - Inheritance is not correct. Unidirectional association is correct.
If this is diagram is only for exercise purpose its OK, otherwise it will violate data hiding and encourage client classes to violate encapsulation (Using somebody else's data directly)
tempSensor=30;is not correct for data type.
if (tempSensor>=30) is incorrect for data type and even if you correct, it violates encapsulation (works on somebody else's data) as an effect of first violation of making instance variables non-private. classes should work on their own data.
Even if for some reason we accept above violation, checkTemperature(Sensor tempSensor) makes use of fresh instance of Sensor (for every call), which is not the one obtained from association relationship. This method should not have parameter, it should work on this.tempSensor (with accepted data leakage). Ideally this is indication that data and its behavior are getting separated and design needs to be corrected.
In case the diagram can not be changed then just remove the parameter in checkTemperature() and take care of data types as shown above.
But I would suggest change at Design level as follows for better encapsulation.
public class SensorNew {
private static final double UPPER_THRESHOLD = 25;
private static final double LOWER_THRESHOLD = 20;
private String location;
private Controller controller;
public SensorNew(String location, Controller controller) {
this.location = location;
this.controller = controller;
}
public int getCurrentTemp() {
// obtain from sensor hardware
return 10; // Just example
}
private void makePeriodicCheck(){
double currentTemp = getCurrentTemp();
if (currentTemp > UPPER_THRESHOLD){
controller.coolDown();
} else if (currentTemp < LOWER_THRESHOLD){
controller.heatUp();
} else {
controller.stopIfRunning();
}
}
public void displaySenzorInfo() { // replace by toString()
System.out.println("Temperature is " + getCurrentTemp()
+ ", located " + location + ".");
}
}
public class ControllerNew {
private String name;
// Need to maintain the state of Controller
// either by variable or State design pattern (preferred)
public ControllerNew(String name, Sensor tempSensor) {
this.name = name;
}
public void coolDown() {
// action depending upon current state of controller
}
public void heatUp() {
// action depending upon current state of controller
}
public void stopIfRunning() {
// action depending upon current state of controller
}
}
The advantage is that we do not have to provide public getXX() setXX() methods to these classes. Hence it maintains encapsulation.
So I have this class "Member" :
package pkgData;
import java.io.Serializable;
public class Member implements Comparable<Member>, Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String name;
private String city;
public Member(String nameOfMember,String location) {
super();
this.name = nameOfMember;
this.city=location;
}
public String getNameOfMember() {
return name;
}
public String getLocationOfMember() {
return city;
}
public void setNameOfMember(String nameOfMember) {
this.name = nameOfMember;
}
#Override
public String toString() {
return name +", " + city;
}
#Override
public int compareTo(Member o) {
int result =this.getNameOfMember().compareTo(o.getNameOfMember());
if(result==0){
result = this.getLocationOfMember().compareTo(o.getLocationOfMember());
}
return result;
}
}
And I have a JComboBox which is EDITABLE and the model of the ComboBox is DefaultComboBoxModel.
So the problem is that if I cast the selectedItem:
Member nameOfMember = (Member)memberModel.getSelectedItem();
if(nameOfMember== null)
throw new Exception("please select a name and a location");
It only checks if the entered string is empty. If I enter a string like "Name, Location" I always get the exception that String cannot be cast to Member. Which String to I have to enter that the String can be cast to Member?
Here is my JComboBox:
private JComboBox<Member> getComboBoxMember() {
if (comboBoxMember == null) {
comboBoxMember = new JComboBox<Member>();
comboBoxMember.setEditable(true);
comboBoxMember.setModel(memberModel);
}
return comboBoxMember;
}
and here the global variables:
private DefaultComboBoxModel<Member> memberModel;
private JComboBox<Member> comboBoxMember;
String nameOfMember = (String) memberModel
.getSelectedItem();if(nameOfMember==null)throw new Exception("please select a name and a location");else
{
String[] parts = nameOfMember.split(",");
String part1 = parts[0]; // name
String part2 = parts[1]; // location
Member member=new Member(part1, part2);
}
String split & cast method
What you can do is first of all test if the string you get is null, or if it matches well you format. Then, you can create a new object with these elements.
Here's a small example code :
String memberData = (String)memberModel.getSelectedItem();
if(memberData == null || memberData.split(", ")[0].isEmpty() || memberData.split(", ")[1].isEmpty()) {
throw new Exception("Data is incorrect, please provide name and location separated with ", ");
}
Member member = new Member(memberData.split(", ")[0], memberData.split(", ")[1]);
JComboBox method
With Java 7 happened a new possibility of extension to JComboBox, which can now be generically parameterized (as for ArrayLists) in the form JComboBox<Type>. Thus, the objects you can get with getSelectedItem() can now be casted to the generic type you gave in parameter to JComboBox. The only problem is that, when a JComboBox is edited, as in your case, the data is casted to a simple String.
What you can do in your listener method (I will use ActionListener) is the following :
class ItemAction implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
try {
//In case the user has not modified the object
Member member = (Member)box.getSelectedItem();
//Just an example here
if(member != null) {
System.out.println(member.toString());
}
} catch(ClassCastException ex) {
//In case the object has been modified
String data = (String)box.getSelectedItem();
//Apply first method here
}
}
}
But the problem with this method is that you end up using the first method still.
I have a Table View pinTable in FXMLand two Table Columns i.e. latColumn, longColumn. I have followed the following method to populate the table :
https://docs.oracle.com/javafx/2/ui_controls/table-view.htm
Snippets are as follows:
FXML Controller Class:
#FXML
public TableView pinTable;
#FXML
public TableColumn latColumn, longColumn;
public final ObservableList<PinList> dataSource = new FXMLCollections.observableArrayList();
#Override
public void initialize(URL url, ResourceBundle rb)
{
initTable();
}
private void initTable()
{
latColumn.setCellValueFactory(new PropertyValueFactory<PinList,String>("latPin"));
longColumn.setCellValueFactory(new PropertyValueFactory<PinList,String>("longPin"));
pinTable.setItems(dataSource);
}
#FXML
private void addButtonClicked(MouseEvent event)
{
if(latText.getText().equals(""))
{
System.out.println("Lat Empty");
}
else if(longText.getText().equals(""))
{
System.out.println("Long Empty");
}
else
{
latVal = Double.parseDouble(latText.getText());
longVal = Double.parseDouble(longText.getText());
dataSource.add(new PinList(latText.getText(),longText.getText(),descriptionText.getText()));
pinTable.getItems().clear();
pinTable.getItems().addAll(dataSource);
for(PinList p: dataSource)
{
System.out.print(p.getLatPin() + " " + p.getLongPin() + " " + p.getDescriptionPin() + "\n");
}
}
}
I have a PinList class which is as follows:
public class PinList
{
private final SimpleStringProperty latPin;
private final SimpleStringProperty longPin;
private String descriptionPin;
public PinList(String _latPin, String _longPin, String _descriptionPin)
{
this.latPin = new SimpleStringProperty(_latPin);
this.longPin = new SimpleStringProperty(_longPin);
this.descriptionPin = _descriptionPin;
}
public String getLatPin()
{
return latPin.getValue();
}
public StringProperty latPinProperty()
{
return latPin;
}
public String getLongPin()
{
return longPin.getValue();
}
public StringProperty longPinProperty()
{
return longPin;
}
public String getDescriptionPin()
{
return descriptionPin;
}
}
All these seem to be fine. But, when I click Add button, nothing happens. No row is created in the table and the println inside the addButtonClicked event handler doesn't execute, or it is executed with no data in the dataSource whatsoever. Any help will be appreciated.
The problem is you confused the Application class with your FXML controller class. Even though this FXML class extends Application the overridden start() method is not being invoked anywhere in your code. Put some printlns to verify this. The FXML controller class can optionally have a initialize() (and additionally can implement Initializable but not mandatory). This method will be invoked by FXMLLoader after the fxml file is loaded by it. So the correct code should be:
public void initialize( URL url, ResourceBundle rb ) {
initTable();
}
and delete start() method and remove extending from Application.
In your initTable() method you do
pinTable.setItems(dataSource);
So now the list held internally by pinTable in its items property is the very same list as dataSource (they are the identical by reference).
Now in your event handler method you do
dataSource.add(new PinList(...));
which adds a new item to dataSource (which is the same list as the table's items)
and then
pinTable.getItems().clear();
which removes all elements from the table's items list. So the table's items list is now empty (has no elements). Of course, since this is the very same list as dataSource, you have also removed all items from dataSource: it is the same (now empty) list as pinTable.getItems().
and now you do
pinTable.getItems().addAll(dataSource);
which copies all the items in dataSource (there are none) into pinTable.getItems() (which is the same list). So this actually duplicates all items in the list, but since you emptied the list, you still have an empty list.
Just remove the lines
pinTable.getItems().clear();
pinTable.getItems().addAll(dataSource);
All you want here is:
#FXML
private void addButtonClicked(MouseEvent event)
{
if(latText.getText().equals(""))
{
System.out.println("Lat Empty");
}
else if(longText.getText().equals(""))
{
System.out.println("Long Empty");
}
else
{
latVal = Double.parseDouble(latText.getText());
longVal = Double.parseDouble(longText.getText());
dataSource.add(new PinList(latText.getText(),longText.getText(),descriptionText.getText()));
for(PinList p: dataSource)
{
System.out.print(p.getLatPin() + " " + p.getLongPin() + " " + p.getDescriptionPin() + "\n");
}
}
}
I've got a project written in JavaFX and I'm trying to get a refresh on a tableview without result.
I've googled around and tried some examples I've found but it still doesn't work.
I populate a tableview with information each row in this table can have new comments added to by double click on the row. The a new Tabpane is opened and the new comment can be added there. On close of this tabpane I'd like the one I clicked from to be refreshed.
I must be doing something wrong. I just don't know what.
In my StoreController
private void populateTableView(List<Store> stores) {
ObservableList<Store> data = FXCollections.observableArrayList(stores);
storeNumberColumn.setCellValueFactory(
new PropertyValueFactory<Store, String>("id"));
storePhoneColumn.setCellValueFactory(
new PropertyValueFactory<Store, String>("phoneNbr"));
chainColumn.setCellValueFactory(
new PropertyValueFactory<Store, String>("chainId"));
commentColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Store, ImageView>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Store, ImageView> p) {
Integer numberOfComments = p.getValue().getCommentsCount();
ReadOnlyObjectWrapper wrapper = null;
if (numberOfComments == 0) {
wrapper = null;
} else if (numberOfComments == 1) {
wrapper = new ReadOnlyObjectWrapper(new ImageView(COMMENT_SINGLE_FLAG_SOURCE));
} else {
wrapper = new ReadOnlyObjectWrapper(new ImageView(COMMENT_DOUBLE_FLAG_SOURCE));
}
return wrapper;
}
});
storeTable.setItems(data);
sortTable(storeTable, missedColumn);
}
#FXML
public void handleTableAction(MouseEvent event) {
if (event.getClickCount() == 2) {
showNewCommentStage();
}
}
private void showNewCommentStage() {
initCommentController();
Store store
= storeTable.getSelectionModel().selectedItemProperty().getValue();
commentController.showNewStage(commentPane, store);
}
It seems like the call-function doesn't get called when the commentpane is closed.
CommentController
public void showNewStage(Pane pane, Store store) {
this.store = store;
initStage(pane);
windowHandler = new WindowHandler(stage);
effectHandler.playEffect(pane);
constructCommentHeaders();
List<Comment> comments;
comments = commentService.listByStoreId(store.getId());
populateCommentTable(comments);
}
Like I said I've tried a lot of the solutions found here on Stackoverflow but with no results. The Tableview doesn't refresh. The Stores and the Comments are in different database tables if that's important
Can someone point me in the right direction?
Thanks!
****EDIT****
The Store.class
public class Store extends CommentEntity {
private String id;
private String chainId;
private String phoneNbr;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getChainId() {
return chainId;
}
public void setChainId(String chainId) {
this.chainId = chainId;
}
public String getPhoneNbr() {
return phoneNbr;
}
public void setPhoneNbr(String phoneNbr) {
this.phoneNbr = phoneNbr;
}
#Override
public String toString() {
return "Store{" + "id=" + id + ", chainId=" + chainId + '}';
}
#Override
public String getCommentIdentifier() {
return id;
}
}
The CommentEntity.Class
public abstract class CommentEntity {
private int commentsCount;
public int getCommentsCount() {
return commentsCount;
}
public void setCommentsCount(int commentsCount) {
this.commentsCount = commentsCount;
}
public abstract String getCommentIdentifier();
}
Thank you for input, I hadn't even reflected over the ImageView / String.
Two issues:
First, you need to distinguish between the data the cells in your column are displaying, and the cells that actually display those data. The cellValueFactory determines the data that are displayed. The PropertyValueFactory is a cellValueFactory implementation that references a JavaFX Property, so when you call
storeNumberColumn.setCellValueFactory(new PropertyValueFactory<Store, String>("id"));
it effectively tells the cells in the storeNumberColumn to call the idProperty() method on the Store object in the current row to get the data for the cell. (If no such method exists, it will try to use getId() as a backup plan.)
By default, you get a cellFactory that displays text resulting from calling toString() on the data generated by the cellValueFactory. In the case where your data are simply Strings, this is usually what you need. In other cases, you often need to provide a cellFactory of your own to get the correct way to display the data.
In your case, the data for the commentColumn are simply the number of comments. You are going to display that by choosing an image based on that numeric value.
So you should have
TableColumn<Store, Number> commentColumn = new TableColumn<>("Comments");
For the cellValueFactory, you can just use
commentColumn.setCellValueFactory(new PropertyValueFactory<>("commentsCount"));
Then you need a cellFactory that displays the appropriate ImageView:
commentColumn.setCellFactory(new Callback<TableColumn<Store, Number>, new TableCell<Store, Number>>() {
#Override
public TableCell<Store, Number>() {
private ImageView imageView = new ImageView();
#Override
public void updateItem(Number numberOfComments, boolean empty) {
super.updateItem(count, empty) ;
if (empty) {
setGraphic(null);
} else {
if (numberOfComments.intValue() == 0) {
setGraphic(null);
} else if (numberOfComments.intValue() == 1) {
imageView.setImage(new Image(COMMENT_SINGLE_FLAG_SOURCE));
setGraphic(imageView);
} else {
imageView.setImage(new Image(COMMENT_DOUBLE_FLAG_SOURCE));
setGraphic(imageView);
}
}
}
}
});
The second issue is actually about the update. A TableView keeps its contents "live" by observing JavaFX properties that are provided by the cellValueFactory as ObservableValues. If the value might change while the table is displayed, you must provide an actual property that can be observed: using a ReadOnlyObjectWrapper is no good (because it's read only, so it's wrapped value will not change). The PropertyValueFactory will also return a ReadOnlyObjectWrapper if you do not have JavaFX property accessor methods (i.e. if it is only using getXXX() methods to access the data). So your model class must provide JavaFX Properties.
You can make an immediate fix to this by updating CommentEntity to use an IntegerProperty:
public abstract class CommentEntity {
private final IntegerProperty commentsCount = new SimpleIntegerProperty();
public final int getCommentsCount() {
return commentsCountProperty().get();
}
public final void setCommentsCount(int commentsCount) {
commentsCountProperty().set(commentsCount);
}
public IntegerProperty commensCountProperty() {
return commentsCount ;
}
public abstract String getCommentIdentifier();
}
I would also strongly recommend updating the Store class to use JavaFX Properties in a similar manner.