How to build TreeView from Arraylist - java

I'm looking for a solution how to build a JavaFX TreeView from an ArrayList. I have this ArrayList witch contains connection name, database server name and list of tables:
public List<ConnectionsListObj> connListObj = new ArrayList<>();
public class ConnectionsListObj {
private String connectionName;
private String dbgwName;
private String tableName;
public ConnectionsListObj(String connectionName, String dbgwName, String tableName) {
this.connectionName = connectionName;
this.dbgwName = dbgwName;
this.tableName = tableName;
}
public String getConnectionName() {
return connectionName;
}
public void setConnectionName(String connectionName) {
this.connectionName = connectionName;
}
public String getDbgwName() {
return dbgwName;
}
public void setDbgwName(String dbgwName) {
this.dbgwName = dbgwName;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
}
I need some kind of a loop which looks into the tree and generates tree using this code:
TreeItem<String> treeItemConnections = new TreeItem<> ("Connections");
TreeItem<String> nodeItemDBGW = new TreeItem<>("DBGW 1");
treeItemConnections.getChildren().add(nodeItemDBGW);
TreeItem<String> nodeItemTable = new TreeItem<>("Table 1");
nodeItemDBGW.getChildren().add(nodeItemTable);
TreeView<String> treeView = new TreeView<>(treeItemConnections);
StackPane root = new StackPane();
root.getChildren().add(treeView);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("java-buddy.blogspot.com");
primaryStage.setScene(scene);
primaryStage.show();
The question is how I can make a loop which looks into the ArrayList and constructs the three? And also when I select on a node I want to get the type of the node.

Why not just put the ConnectionsListObj objects in the tree? I think TreeView calls toString() on the objects for the text in each tree node so just return the string you want to show from ConnectionsListObj.toString(). Then when you get the selected item by calling myTreeView.getSelectionModel().getSelectedItems() you get an instance of ConnectionsListObj which should have all the data you need.
Loops in java look like the following for your case:
for(ConnectionsListObj connection : connListObj) {
nodeItemDBGW.getChildren().add(connection);
}
or...
nodeItemDBGW.getChildren().addAll(connListObj);

Related

JavaFX ArrayList in class empty when sent to another controller

I have an inventory class with an ObservableArray of type Product, each product containing an arraylist of parts. On the main controller I pre-populate with some data, and system output shows that the values are all there and the array of parts are present. The problem I am running in to is when I click a button to open a new controller and populate the product values into fields, all the product fields except arraylist have data, it's as if the arraylist is being wiped in that copy.
Product Class
private ArrayList<Part> associatedParts = new ArrayList<>();
private int productID;
private String name;
private double price;
private int inStock;
private int min;
private int max;
public void addAssociatedPart(Part part) {
associatedParts.add(part);
}
public ArrayList<Part> getAllAssociatedParts() {
return associatedParts;
}
Part Class
private int partID;
private String name;
private double price;
private int inStock;
private int min;
private int max;
Inventory Class
private static ObservableList<Product> products = FXCollections.observableArrayList();
public ObservableList<Product> getAllProducts() {
return products;
}
public void addPart(Part part) {
allParts.add(part);
}
public void addProduct(Product prod) {
products.add(prod);
}
public Part lookupPart(int partid) {
if (partid > allParts.size() || partid < 1) {
return null;
}
else {
return allParts.get(partid-1);
}
}
MainController
#Override
public void initialize(URL url, ResourceBundle rb) {
if (firstLoad) {
inventory.addPart(new Part(1,"Widget",1.13,5,1,8));
inventory.addPart(new Part(2,"Sprocket",2.88,5,1,8));
inventory.addPart(new Part(3,"Gear",3.46,5,1,8));
inventory.addProduct(new Product(1,"Dohicky",13.34,3,1,5));
inventory.lookupProduct(1).addAssociatedPart(inventory.lookupPart(1));
inventory.lookupProduct(1).addAssociatedPart(inventory.lookupPart(2));
inventory.addProduct(new Product(2,"Thingamajig",24.12,3,1,5));
inventory.lookupProduct(1).addAssociatedPart(inventory.lookupPart(2));
inventory.lookupProduct(1).addAssociatedPart(inventory.lookupPart(3));
}
tblProducts.setItems(inventory.getAllProducts());
}
#FXML
private void handleAddMod(ActionEvent event) throws IOException {
FXMLLoader loader = new FXMLLoader();
Stage stage = new Stage();
Parent root = null;
if (event.getSource() == btnProdMod && tblProducts.getSelectionModel().getSelectedItem() != null) {
Product prod = tblProducts.getSelectionModel().getSelectedItem();
loader.setLocation(getClass().getResource("ModifyProduct.fxml"));
root = loader.load();
loader.<ModifyProductController>getController().displayProd(prod);
}
if (root != null) {
Scene scene = new Scene(root);
stage.setScene(scene);
stage.initModality(Modality.APPLICATION_MODAL);
stage.showAndWait();
}
}
At this point, System.out.println() on any given product will return valid information, and even on the product getallassociatedparts size shows that it is populated.
ModifyProductController
public void displayProd (Product prod) {
System.out.println(prod.getName());
System.out.println(prod.getAllAssociatedParts().size());
}
At this point, the product name (and any other fields within it) will show, but there is nothing in the associatedParts list.
For instance, if I passed part 1 as seen above, the system output shows me Widget, followed by a 0.
The solution is for me to chill out and take a break from the code, #fabian pointed out it worked fine when he tried it, so when looking back over the code I noticed that I had accidentally assigned all parts to product 1, and like an idiot I had only tested on product 2 (last on list so closer to the button? not sure lol)
After updating the main controller parts to lookupProduct(2) instead of 1, it worked just fine:
inventory.lookupProduct(2).addAssociatedPart(inventory.lookupPart(2));
inventory.lookupProduct(2).addAssociatedPart(inventory.lookupPart(3));

Is there anyway to generate UI controllers (radio button, input fields, etc) using a loop in java

I'm a beginner in java. I want to create a javafx form which contains a few input elements such as radio buttons, input types, etc.. created using a loop.
A pseudo code as follows,
for (Suit **suit** : suits){
final TextField **suit** = new TextField();
lastName.setPromptText("Enter your last name.");
GridPane.setConstraints(lastName, 0, 1);
grid.getChildren().add(lastName);
}
The problem i came across is how to define the variable name of each element based on elements of the array.
The easiest way to do this is, if the class you want to edit contains javafx properties:
public class Person {
private final StringProperty firstName;
private final StringProperty familyName;
public String getFirstName() {
return firstName.get();
}
public String getFamilyName() {
return familyName.get();
}
public StringProperty firstNameProperty() {
return firstName;
}
public StringProperty familyNameProperty() {
return familyName;
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public void setFamilyName(String familyName) {
this.familyName.set(familyName);
}
public Person(String firstName, String familyName) {
this.firstName = new SimpleStringProperty(firstName);
this.familyName = new SimpleStringProperty(familyName);
}
#Override
public String toString() {
return familyName.get() + ", " + firstName.get();
}
}
You could simply use binding/listeners in this case to bind the properties of the ui to the properties of the person object:
Example:
Person[] persons = new Person[] {
new Person("Frank", "Miller"),
new Person("Jane", "Doe"),
new Person("Luke", "Skywalker"),
new Person("Leia", "Organa")
};
Button btn = new Button();
btn.setText("Print");
btn.setOnAction((ActionEvent event) -> {
System.out.println("-------------------------");
Arrays.stream(persons).forEach(System.out::println);
});
GridPane root = new GridPane();
int row = 0;
for (Person person : persons) {
row = addPerson(root, row, person);
}
GridPane.setConstraints(btn, 0, row);
root.getChildren().add(btn);
/**
* Adds a Person to a GridPane
* #param pane the pane to add to.
* #param startRow the first row used by this person
* #param person the person to add.
* #return first row below the Nodes added for the person.
*/
static int addPerson(GridPane pane, int startRow, Person person) {
// use binding
TextField firstNameText = new TextField();
firstNameText.textProperty().bindBidirectional(person.firstNameProperty());
// use listeners
TextField familyNameText = new TextField(person.getFamilyName());
familyNameText.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
// you could do e.g. type conversion here
person.setFamilyName(newValue);
});
// this is only necessary, if you want to change the family name of a person
// from the code and the result should be visible in the ui
person.familyNameProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
// you could do e.g. type conversion here
familyNameText.setText(newValue);
});
Label firstNameLabel = new Label("First Name");
GridPane.setConstraints(firstNameLabel, 0, startRow);
GridPane.setConstraints(firstNameText, 1, startRow);
Label familyNameLabel = new Label("Family Name");
GridPane.setConstraints(familyNameLabel, 2, startRow);
GridPane.setConstraints(familyNameText, 3, startRow);
pane.getChildren().addAll(firstNameLabel, firstNameText, familyNameLabel, familyNameText);
return startRow+1;
}
Note that a GridPane may not be the best way to add a list of persons to a scene your problem.
See Oracle Tutorial: JavaFX: Working with JavaFX UI Components: 13 Table View for an example using a TableView.

Does PropertyValueFactory need valueProperty method in model or not?

I am trying to make a table with a TableView and fill it based on a list of Actors object. The Actor Model is bellow.
public class Actor {
private SimpleIntegerProperty actorId;
private SimpleStringProperty firstName;
private SimpleStringProperty lastName;
private SimpleStringProperty email;
public Actor(int id, String first, String last, String e){
actorId = new SimpleIntegerProperty(id);
firstName = new SimpleStringProperty(first);
lastName = new SimpleStringProperty(last);
email = new SimpleStringProperty(e);
}
public void setActorId(int id){
actorId.set(id);
}
public int getActorId(){
return actorId.get();
}
public void setFirstName(String name){
firstName.set(name);
}
public String getFirstName(){
return firstName.get();
}
public void setLastName(String last){
lastName.set(last);
}
public String getLastName(){
return lastName.get();
}
public void setEmail(String e){
email.set(e);
}
public String getEmail(){
return email.get();
}
}
And here is my TableVeiw class
public class SakilaApp extends Application {
private TableView<Actor> actorTable = new TableView<Actor>();
private final ObservableList<Actor> actorData = FXCollections.observableArrayList(
new Actor(1, "Mohsen","Parsa", "Mohseh.parsa313#gmail.com"),
new Actor(2, "Morteza","Ghasemi", "Morteza.Ghasemi#gmail.com"),
new Actor(3, "Mohammad","Fetrat", "Mohammad.Fetrat#gmail.com"),
new Actor(4, "Nader","AhmadYar", "Nader.AhmadYar#gmail.com" )
);
#SuppressWarnings("unchecked")
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(600);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
actorTable.setEditable(true);
TableColumn<Actor, Integer> idCol = new TableColumn<Actor, Integer>("Actor ID");
idCol.setCellValueFactory(
new PropertyValueFactory<Actor, Integer>("actorId"));
idCol.setPrefWidth(60);
TableColumn<Actor, String> firstNameCol = new TableColumn<Actor, String>("First Name");
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Actor, String>("firstName"));
firstNameCol.setPrefWidth(100);
TableColumn<Actor, String> lastNameCol = new TableColumn<Actor, String>("Last Name");
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Actor, String>("lastName"));
lastNameCol.setPrefWidth(100);
TableColumn<Actor, String> emailCol = new TableColumn<Actor, String>("Email");
emailCol.setCellValueFactory(
new PropertyValueFactory<Actor, String>("email"));
emailCol.setPrefWidth(200);
TableColumn<Actor, String> lastUpdateCol = new TableColumn<Actor, String>("Last Update");
lastUpdateCol.setCellValueFactory(
new PropertyValueFactory<Actor, String>("lastUpdate"));
lastUpdateCol.setPrefWidth(100);
actorTable.getColumns().addAll(idCol, firstNameCol, lastNameCol, emailCol);
actorTable.setItems(actorData);
final VBox actorBox = new VBox();
actorBox.setSpacing(5);
actorBox.setPadding(new Insets(10, 0, 0, 10));
actorBox.getChildren().addAll(label, actorTable);
((Group) scene.getRoot()).getChildren().addAll(actorBox);
stage.setScene(scene);
stage.show();
}
public static void main(String args[]){
Application.launch(args);
}
}
My problem is that as they mentioned in this article
How to use the PropertyValueFactory correctly?
new PropertyValueFactory<Actor, Integer>("actorId")
will lookup for :
Actor.actorIdProperty()
but as you can see in Actor model there is no any methods with the name of
IntegerProperty actorIdProperty()
My question is, Do we need such method or not?
if it is necessary, why this code works correctly?
Depends on what you mean by "work" :-)
As long as the TableView is read-only, getters/setters are enough: data is shown as expected. As soon as the TableView is editable, the data won't be updated automatically. In the latter case, you'll have the option to either install a custom commit handler or expose the properties which will allow internal magic to work.
As you have them anyway, I see no reason not to (and not follow buggy example in the tutorial)
The JavaDoc of PropertyValueFactory states that you need a field called
SimpleIntegerProperty actorIdProperty;
so really I think you should.
However, looking at the code of PropertyValueFactory I notice that it falls back to a getter if the property field isn't available:
if (propertyRef.hasProperty()) {
return propertyRef.getProperty(rowData);
} else {
T value = propertyRef.get(rowData);
return new ReadOnlyObjectWrapper<T>(value);
}
so that is why your code works as is.
If I were you I would follow the JavaDoc and rename your field to actorIdProperty because you never know when they could change the implementation.

Getting Data in JList via JDBC - The missing link

I've been stuck at a seemingly simple problem for hours and I just can't find the solution. I'm trying to implement a very simple Forum in Java and I'm trying to load the entrys at the moment.
My forum is a JList that is filled with JPanels and that accepts entries via the JLists DefaultListModel and the addMessage method. So if I add an entry without the database it looks like this:
MessageList m = new MessageList();
m.addMessage("NAME AUTOR", "<html><body style='width: 675px;'>Lorem ipsum dolor sit amet.", "22.01.13", "SOA");
The messageList class looks like this:
public class MessageList extends JList{
DefaultListModel messageModel = new DefaultListModel();
MessageRenderer messageRenderer = new MessageRenderer();
public MessageList( ){
this.setCellRenderer(messageRenderer);
this.setModel(messageModel);
}
public void addMessage(String author, String text, String date, String tag){
messageModel.addElement(new Message(author, text, date, tag));
}
}
I've also written the Code for getting an ArrayList (called allBtr) with the Message Objects (called ConBeitrag) from the database:
ArrayList<ConBeitrag> allBtr = new ArrayList<ConBeitrag>();
ConBeitrag conBtr = new ConBeitrag();
try {
allBtr = conBtr.getAllBtr();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
The message objects look like this:
public class ConBeitrag {
private int beitragid;
private int projektid;
private int mitarbeiterid;
private String beitragText;
private String erstellt_am;
private String geaendert_am;
private String schlagwort1;
private String schlagwort2;
private MdBeitrag mdBtr = new MdBeitrag();
public ConBeitrag (){
}
public ConBeitrag(int beitragid, int projektid, int mitarbeiterid, String beitragText, String erstellt_am, String geaendert_am){
this.beitragid = beitragid;
this.projektid = projektid;
this.mitarbeiterid = mitarbeiterid;
this.erstellt_am = erstellt_am;
this.geaendert_am = geaendert_am;
this.beitragText = beitragText;
this.schlagwort1 = schlagwort1;
this.schlagwort2 = schlagwort2;
}
public ArrayList<ConBeitrag> getAllBtr() throws SQLException{
MdBtrInterface modInt;
modInt = new MdBeitrag();
ArrayList<ConBeitrag> AlBtr = modInt.getAllBtr();
for(ConBeitrag object: AlBtr){
System.out.println(object.beitragText);
}
return AlBtr;
}
}
Now what would be the smartest way to get the ArrayList into a form that I can pass into the addMessage method? I've kind of approached this from the GUI end, then from the database end, and now I'm stuck in the middle.
Overwritten toString() method:
#Override
public String toString() {
return mitarbeiterid + beitragstext + erstellt_am + schlagwort1 + schlagwort2;
}
"The messages are stored inside the ArrayList as Objects if that helps. So if I run "System.out.println(allBtr);" it gives me "[ConBeitrag#48f4104f, ConBeitrag#f5ad7f4, ConBeitrag#1517dc0c]"
You need to override the toString method in your ConGeitrag class. Something like this.
public class ConBeitrag {
...
#Override
public String toString(){
return author + ", " + text + ", " + date + ", " + tag;
}
}
You can make the return any format you want. Test this one out and make changes as desired to the format.
Try this out as a Helper method (after you've overridden the toString)
public JList createJList(ResultSet rs){
DefaultListModel model = new DefaultListModel();
while (rs.next()){
String author = rs.getString("author"); // Just an example. You may
String text = rs.getString("text"); // need to retrieve your
String date = rs.getString("date"); // data differently
String tag = rs.getString("tag");
Message message = new Message(author, text, date, tag);
model.addElement(message);
}
JList list = new JList(model);
return list;
}
I don't really see a need for a Custom JList for this situation.
Test run: output : 3testtestnullnull. Besides the formatting, it works fine
public class ConBeitragTest {
public static void main(String[] args) {
ConBeitrag con = new ConBeitrag(1, 2, 3, "test", "test", "test");
System.out.println(con);
}
}
class ConBeitrag {
private int beitragid;
private int projektid;
private int mitarbeiterid;
private String beitragText;
private String erstellt_am;
private String geaendert_am;
private String schlagwort1;
private String schlagwort2;
public ConBeitrag() {
}
public ConBeitrag(int beitragid, int projektid, int mitarbeiterid, String beitragText, String erstellt_am, String geaendert_am) {
this.beitragid = beitragid;
this.projektid = projektid;
this.mitarbeiterid = mitarbeiterid;
this.erstellt_am = erstellt_am;
this.geaendert_am = geaendert_am;
this.beitragText = beitragText;
this.schlagwort1 = schlagwort1; // This is null
this.schlagwort2 = schlagwort2; // This is null
}
#Override
public String toString() {
return mitarbeiterid + beitragText + erstellt_am + schlagwort1 + schlagwort2;
}
}

JavaFX 2.0 TableViewBuilder, how to use

Given a Person class:
public class Person {
private StringProperty firstName;
private StringProperty lastName;
public Person(String firstName, String lastName){
setFirstName(firstName);
setLastName(lastName);
}
//SETTERS
public final void setFirstName(String value) { firstNameProperty().set(value); }
public final void setLastName(String value) { lastNameProperty().set(value); }
//GETTERS
public String getFirstName() { return firstNameProperty().get(); }
public String getLastName() { return lastNameProperty().get(); }
//PROPERTY GETTERS
public StringProperty firstNameProperty() {
if (firstName == null) firstName = new SimpleStringProperty(this, "firstName");
return firstName;
}
public StringProperty lastNameProperty() {
if (lastName == null) lastName = new SimpleStringProperty(this, "lastName");
return lastName;
}
}
I recreated the JavaFX API example on TableView:
public class TestTableViewBuilder extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
final ObservableList<Person> data = FXCollections.observableArrayList(
new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson"),
new Person("Ethan", "Williams"),
new Person("Emma", "Jones"),
new Person("Michael", "Brown")
);
TableView<Person> table = new TableView<Person>();
table.setItems(data);
TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name");
firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
TableColumn<Person,String> lastNameCol = new TableColumn<Person,String>("Last Name");
lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
table.getColumns().setAll(firstNameCol, lastNameCol);
Group root = new Group();
root.getChildren().add(table);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
I've been trying without success to use the TableViewBuilder to recreate the same table. Anyone have an idea how to use JavaFX 2.0 TableViewBuilder to create a TableView with an existing ObservableList?
Here is a sample:
import javafx.application.Application;
import javafx.collections.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
public class TableViewBuilderExample extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) {
final ObservableList<?> data = FXCollections.observableArrayList(
new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson")
);
stage.setScene(
new Scene(
TableViewBuilder.create().items((ObservableList<Object>) data).columns(
TableColumnBuilder.create().text("First Name").cellValueFactory(new PropertyValueFactory("firstName")).build(),
TableColumnBuilder.create().text("Last Name").cellValueFactory(new PropertyValueFactory("lastName")).build()
).build()
)
);
stage.show();
}
}
There are some strange things going on with the generic type usage in the Builders. I would have liked instead to say something like TableViewBuilder<Person>.create(), but TableViewBuilder has a recursive type as a second generic type parameter which must be supplied to it, so I could not get that strategy to work. The code above is next best thing I could come up with, but it still have some strange typing going on with the ObservableList<?> definition of the data and the need to cast the data to an ObservableList<Object> in the Builder.
Based on Sergey's insight for a type parameterization syntax for the builders I was able to create the following builder which will work with a data type of ObservableList<Person>
TableViewBuilder.<Person>create().items(data).columns(
TableColumnBuilder.<Person, String>create()
.text("First Name").cellValueFactory(new PropertyValueFactory("firstName"))
.build(),
TableColumnBuilder.<Person, String>create()
.text("Last Name").cellValueFactory(new PropertyValueFactory("lastName"))
.build()
).build()
After this exercise, I would be even more inclined to checkout the DataFX project if I had to do this kind of stuff a lot . . .
The trick here is in the fact that Builders are created by factories named create, so you have to parametrize them, not the Builder class name itself which only plays namespace role here.
This way:
TableViewBuilder.<Person>create().build();

Categories

Resources