Items not showing in TableView in JavaFX - java

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");
}
}
}

Related

Javafx Link/Bind Treeview Items to ObservableList

I'm trying to find an easy way of linking a TreeView of type Download to an ObservableList of the same type.
MainController.java
public class MainController {
private ObservableList<Download> downloads = FXCollections.observableArrayList();
#FXML private TreeView<Download> $TreeDownloads;
#FXML
public void initialize() {
$TreeDownloads.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
$TreeDownloads.setNodeOrientation(NodeOrientation.LEFT_TO_RIGHT);
$TreeDownloads.setShowRoot(false);
downloads.addListener(new ListChangeListener<Download>() {
#Override
public void onChanged(Change<? extends Download> c) {
if (c.wasAdded()) {
addDownloads(c.getAddedSubList());
}
if (c.wasRemoved()) {
//
}
}
});
downloads.add(new Download("3847"));
downloads.add(new Download("3567"));
downloads.add(new Download("2357"));
}
private void addDownloads(List<? extends Download> downloads) {
downloads.forEach(download -> {
TreeItem<Download> treeItem = new TreeItem<>(download);
$TreeDownloads.getRoot().getChildren().add(treeItem);
new Thread(download::start).start();
});
}
private void removeDownloads(List<? extends Download> downloads) {
// remove treeitems from the treeview that hold these downloads
}
}
Download.java
public class Download {
private DoubleProperty progress = new SimpleDoubleProperty(0D);
private StringProperty id = new SimpleStringProperty("");
public Download(String id) {
this.id.set(id);
}
public void start() {
while (progress.getValue() < 1) {
try {
Thread.sleep(1000);
progress.add(0.1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public String toString() {
return id.getValue();
}
}
How do i implement a remove by Object(Download) mechanism, and is there an easier way to bind observablelist's items to a treeview?
Still not entirely certain what the exact problem is, all pretty straightforward:
First off, your list change listener implementation is incorrect, it must advance the subChanges before accessing its state (you did run your posted code, or not ;)
downloads.addListener(new ListChangeListener<Download>() {
#Override
public void onChanged(Change<? extends Download> c) {
// this while was missing
while (c.next()) {
if (c.wasAdded()) {
addDownloads(c.getAddedSubList());
}
if (c.wasRemoved()) {
// accessing the list of removed elements is .. plain standard api
removeDownloads(c.getRemoved());
}
}
}
});
Now implement the removal of the corresponding treeItems:
private void removeDownloads(List<? extends Download> downloads) {
// remove treeitems from the treeview that hold these downloads
List<TreeItem<Download>> treeItemsToRemove = treeDownloads.getRoot().getChildren().stream()
.filter(treeItem -> downloads.contains(treeItem.getValue()))
.collect(Collectors.toList());
treeDownloads.getRoot().getChildren().removeAll(treeItemsToRemove);
}
Asides:
java naming conventions use lowercase letters for members: treeDownloads (not $TreeDownloads)
the "verifiable" in MCVE implies being runnable as-is: the poster should be the first to verify that ;) yours wasn't due to incorrect implementation of the listener
the "minimal" in MCVE means leaving out everything that's not needed: f.i. calling the threading code - which in your first snippet was particularly distracting because violating fx' threading rule is a rather common error

JavaFX: Custom Equals Method Causes No Refresh in TableView

Here's the situation: I have an ObservableSet (because the ID field for my data must be unique) that has a listener. That listener updates an ObservableList. That ObservableList is, in turn, listened to by a TableView. (According to the comments, this is all necessary because ObservableSet can't be used to back a TableView in JavaFX.)
What we're finding, however, is that doing multiple add operations to the Set don't trigger the refresh of the TableView.
This works fine for:
Initial list population
Duplicate set entries (the listener isn't triggered, that's the intended behavior)
However, editing the value, the TableView will only trigger if there's a single add statement (in the example below, commenting out markStructure.add(new MarkStructureItem(2, 15)); makes it work fine, but then you only have a single value). If there's more than one, the TableView doesn't refresh.
This can be worked around by adding a manual refresh at the end of the Listener class (tblTable.refresh()). This is actually how it's functioned up until now, but development now requires the removal of that.
Any idea what's going on? Why is the listener on the TableView not getting triggered for subsequent adds? The data IS being put into the Set and List, that much has been determined; it's just not triggering a refresh.
Code example (not production, but a POC I put together to test and get answers):
public class TestController implements Initializable {
ObservableSet<MarkStructureItem> markStructure = FXCollections.observableSet();
ObservableList<MarkStructureItem> listMarkStructure = FXCollections.observableArrayList();
#FXML
private Pane root;
#FXML
private TableView<MarkStructureItem> tblTable;
#FXML
private TableColumn<MarkStructureItem, Integer> col1;
#FXML
private TableColumn<MarkStructureItem, Integer> col2;
#FXML
private Button btnButton;
private void resetAll() {
markStructure.clear();
markStructure.add(new MarkStructureItem(1, 25));
markStructure.add(new MarkStructureItem(2, 15));
}
#FXML
private void handleButtonAction(ActionEvent event) throws IOException {
Object source = event.getSource();
Button btnSource = (Button) source;
Stage stage = (Stage) root.getScene().getWindow();
switch (btnSource.getId()) {
case "btnButton": {
resetAll();
break;
}
}
}
class MarksUpdater<MarkStructureItem extends configurationeditor.MarkStructureItem> implements SetChangeListener {
#Override
public void onChanged(Change change) {
if (change.wasRemoved()) {
listMarkStructure.remove(change.getElementRemoved());
} else if (change.wasAdded()) {
MarkStructureItem newMarks = (MarkStructureItem) change.getElementAdded();
listMarkStructure.add(newMarks);
}
}
}
#Override
public void initialize(URL url, ResourceBundle rb) {
markStructure.addListener(new MarksUpdater<MarkStructureItem>());
col1.setCellValueFactory(
new PropertyValueFactory<MarkStructureItem, Integer>("id")
);
col2.setCellValueFactory(
new PropertyValueFactory<MarkStructureItem, Integer>("marks")
);
col2.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));
col2.setOnEditCommit(
new EventHandler<CellEditEvent<MarkStructureItem, Integer>>() {
#Override
public void handle(CellEditEvent<MarkStructureItem, Integer> t) {
((MarkStructureItem) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setMarks(t.getNewValue());
}
}
);
tblTable.setItems(listMarkStructure);
resetAll();
}
}
markStructure class:
public class MarkStructureItem {
final SimpleIntegerProperty marks;
final SimpleIntegerProperty id;
#Override
public int hashCode() {
return this.getId().hashCode();
}
#Override
public boolean equals(Object obj) {
if (obj.getClass() == this.getClass()) {
MarkStructureItem thisObj = (MarkStructureItem) obj;
return thisObj.getId() == this.getId();
}
return false;
}
public MarkStructureItem(Integer finishPosition, Integer marks) {
this.marks = new SimpleIntegerProperty(marks);
this.id = new SimpleIntegerProperty(finishPosition);
}
public Integer getId() {
return id.get();
}
public void setMarks(Integer value) {
marks.set(value);
}
public Integer getMarks() {
return marks.get();
}
public void setId(Integer value) {
id.set(value);
}
}
Issue 1:
Your equals implementation is not null-safe and you're comparing the Integers using reference equality (==) instead of equals. This will probably work as long as you're inside the range of cached values and use autoboxing, but cease to work outside that range (this range is only guarantied to be -128 to 127, see Integer.valueOf(int)).
Since you're using the Object in a Set, I recommend making the id unmodifiable anyways. Otherwise you need to remove the value from the set before modifying it and adding it back to the Set (the hash code changes), which would mess up the order in the list unless you temporarily prevent the SetChangeListener from updating the list.
Also using a primitive type will prevent issues with ==.
Issue 2:
For any changes in the marks property to be visible in the TableView, you need to provide a marksProperty() method returning the property itself. This is necessary for the TableView to listen to changes of the property.
public class MarkStructureItem {
final SimpleIntegerProperty marks;
final int id;
#Override
public int hashCode() {
return id;
}
#Override
public boolean equals(Object obj) {
return obj != null
&& obj.getClass() == this.getClass()
&& this.id == ((MarkStructureItem) obj).id; // primitive type can be compared using ==
// && this.getId().equals(((MarkStructureItem) obj).getId()); // alternative for non-primitive types
}
public MarkStructureItem(int finishPosition, Integer marks) {
this.marks = new SimpleIntegerProperty(marks);
this.id = finishPosition;
}
public int getId() {
return id;
}
public IntegerProperty marksProperty() {
return marks;
}

TableView doesn't refresh

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.

How to run two methods in sequence with the same button in javafx?

I'm trying to fill a listview with the artist and title of songs using the open() method.
To achieve this I created the artist and title ArrayLists and merged them using the create() method.
The problem is, when I try to run create() inside open() nothing happens. However, if I assign the create() method to a different button and click it after using the filechooser everything works fine.
So, I would like to know if it is possible to run the create() method after the open() method using only one button via fxml or regular java code.
public class PLController implements Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {
list.setItems(visibleList);
}
List<File> filelist = new ArrayList<File>();
ArrayList<String> title = new ArrayList<String>();
ArrayList<String> artist = new ArrayList<String>();
ObservableList<String> visibleList = FXCollections.observableArrayList();
#FXML
ListView<String> list;
#FXML
Button impButton;
public void create(){
for(int i = 0; i < title.size(); i++){
visibleList.add(artist.get(i) +" - " +title.get(i));
Collections.sort(visibleList);
}
}
public void handleMetadata(String key, Object value){
if (key.equals("title")){
title.add(value.toString());
}
if (key.equals("artist")){
artist.add(value.toString());
}
}
public void open(){
FileChooser chooser = new FileChooser();
filelist = chooser.showOpenMultipleDialog(impButton.getScene().getWindow());
for(File f:filelist){
try {
Media media = new Media(f.toURI().toURL().toString());
media.getMetadata().addListener(new MapChangeListener<String, Object>(){
#Override
public void onChanged(Change<? extends String, ? extends Object> change) {
if(change.wasAdded()) {
handleMetadata(change.getKey(), change.getValueAdded());
}
}
});
} catch (MalformedURLException e) {
e.printStackTrace();
}
}create(); //Nothing happens
}
As others have pointed out, the Media object does not have its metadata initialized immediately. (It needs to read data from the URL and populate those metadata as it receives them.) That is why the metadata are exposed as an ObservableMap. When you reach the end of your open() method, it is highly unlikely that the metadata will have been initialized, so your create() method will not see any data at that point.
What you need to do is observe the map, and update the ListView once both the artist and title are available. The best way to do this, in my opinion, is to encapsulate the information you want into a separate class:
public class Video {
private final Media media ;
private final ReadOnlyStringWrapper artist = new ReadOnlyStringWrapper("Unknown");
private final ReadOnlyStringWrapper title = new ReadOnlyStringWrapper("Title");
public Video(File file) {
try {
this.media = new Media(file.toURI().toURL().toExternalForm());
artist.bind(Bindings.createStringBinding(() -> {
Object a = media.getMetadata().get("artist");
return a == null ? "Unknown" : a.toString();
}, media.getMetadata()));
title.bind(Bindings.createStringBinding(() -> {
Object t = media.getMetadata().get("title");
return t == null ? "Unknown" : t.toString();
}, media.getMetadata()));
}
catch (Exception e) {
throw new RuntimeException("Could not create Video for "+file, e);
}
}
public ReadOnlyStringProperty titleProperty() {
return title.getReadOnlyProperty();
}
public ReadOnlyStringProperty artistProperty() {
return artist.getReadOnlyProperty();
}
public final String getTitle() {
return title.get();
}
public final String getArtist() {
return artist.get();
}
public final Media getMedia() {
return media ;
}
}
Now you can create a ListView<Video> to display the videos. Use a cell factory to display the artist and the title in the format you want. You can make sure that the observable list fires updates when either the artist or title properties change, and you can keep it sorted via a SortedList.
#FXML
private ListView<Video> list ;
private ObservableList<Video> visibleList ;
public void initialize() {
visibleList = FXCollections.observableArrayList(
// make list fire updates when artist or title change:
v -> new Observable[] {v.artistProperty(), v.titleProperty()});
list.setItems(new SortedList<>(list, Comparator.comparing(this::formatVideo)));
list.setCellFactory(lv -> new ListCell<Video>() {
#Override
public void updateItem(Video item, boolean empty) {
super.updateItem(item, empty) ;
setText(formatVideo(item));
}
});
}
#FXML
private void open() {
FileChooser chooser = new FileChooser();
List<File> fileList = chooser.showOpenMultipleDialog(impButton.getScene().getWindow());
if (fileList != null) {
fileList.stream()
.map(Video::new)
.forEach(visibleList::add);
}
}
private String formatVideo(Video v) {
if (v == null) return "" ;
return String.format("%s - %s", v.getArtist(), v.getTitle());
}
Simply creating a Media object and assigning a listener to it won't fire the code in the listener. So the title list in your code remains empty. The create() method is called, but since you are iterating over an empty list, nothing actually happens.
Use a debugger or add some logging information in such cases.
Also, you should sort the list after the for loop, not every time you add an item.

Creating Method to Reference Parent Array List

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.

Categories