I want to make a customize list view in javafx. Here I need to bind multiple component in list cell as follow, like one label, one textfield, one button under one HBox and
The answer by Anvay for some reason didnt work for me, what i had to do to fix it was just some very small tweaks:
I also had a main class (intellij auto generated).
public class MainMain extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader fxmlLoader = new
FXMLLoader(getClass().getResource("MainController.fxml"));
try
{
Parent root = fxmlLoader.load();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("Title");
primaryStage.show();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
launch(args);
}
I know this was probably obvious for most of the experts here, but these issues perplexed me for hours while i was debugging.
I understand your question. There are mainly two ways to set items in a Listview
:
1. Create the ObservableList
and set the items of the ListView
with the ObservableList
(listView.setItems(observableList)
).
2. Use the setCellFactory()
method of the ListView
class.
You would prefer to use the setCellFactory()
method, because this approach simplies the process as well as it helps to separate out the business logic and the UI (FXML).
Here is a more detailed explaination:
1. Create a new FXML file with the name listview.fxml
to contain the ListView
, and set the ListViewController
class as its controller:
File: listview.fxml:
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.ListView?>
<?import demo.ListViewController?>
<GridPane xmlns:fx="http://javafx.com/fxml" alignment="CENTER">
<ListView fx:id="listView"/>
</GridPane>
2. Create the controller and name it ListViewController
.
The controller can load the listview.fxml
file and access the listview
.
File: ListViewController.java:
package demo;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.util.Callback;
import java.io.IOException;
import java.util.Set;
public class ListViewController
{
@FXML
private ListView listView;
private Set<String> stringSet;
ObservableList observableList = FXCollections.observableArrayList();
public ListViewController()
{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/listview.fxml"));
fxmlLoader.setController(this);
try
{
Parent parent = (Parent)fxmlLoader.load();
Scene scene = new Scene(parent, 400.0 ,500.0);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
public void setListView()
{
stringSet.add("String 1");
stringSet.add("String 2");
stringSet.add("String 3");
stringSet.add("String 4");
observableList.setAll(stringSet);
listView.setItems(observableList);
listView.setCellFactory(new Callback<ListView<String>, javafx.scene.control.ListCell<String>>()
{
@Override
public ListCell<String> call(ListView<String> listView)
{
return new ListViewCell();
}
});
}
}
3. First you need to set the value of the ObservableList
. This is very important.
Then, set the items of list using the ObservableList
and call the setCellFactory()
method on the ListView
. In the given example I just take the String
values an add them to the String
set (the Set<String> stringSet
).
4. When the setCellFactory()
method is called on the ListView
, it will return the ListCell
. So for sake of simplicity, I added a class which extends the ListCell
, and the setGraphic()
method is present for the ListCell()
and will set the items of the ListCell
.
File: ListViewCell.java:
package demo;
import javafx.scene.control.ListCell;
public class ListViewCell extends ListCell<String>
{
@Override
public void updateItem(String string, boolean empty)
{
super.updateItem(string,empty);
if(string != null)
{
Data data = new Data();
data.setInfo(string);
setGraphic(data.getBox());
}
}
}
5. I just added a class which will load the listCellItem.fxml
and return the HBox
, which will contain the other components as children.
The HBox
is then set to the ListCell
.
File: listCellItem.fxml:
<?import demo.Data?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Label?>
<HBox xmlns:fx="http://javafx.com/fxml" fx:id="hBox">
<children>
<Label fx:id="label1"/>
<Label fx:id="label2"/>
</children>
</HBox>
File: Data.java:
package demo;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import java.io.IOException;
public class Data
{
@FXML
private HBox hBox;
@FXML
private Label label1;
@FXML
private Label label2;
public Data()
{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/listCellItem.fxml"));
fxmlLoader.setController(this);
try
{
fxmlLoader.load();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
public void setInfo(String string)
{
label1.setText(string);
label2.setText(string);
}
public HBox getBox()
{
return hBox;
}
}
Using this way, you can use the setCellFactory()
method to separate the things that are business logic and FXML.
Hope this is helpful.
The example above by @Anvay needs a couple of tweaks to work. These are simple things to set on-track.
The ListViewController below works with these changes. I changed "stringSet" to a list, "stringList". The controller is pretty much the sample controller provided by Scene Builder 2
public class ListViewController
{
@FXML private ResourceBundle resources;
@FXML private URL location;
@FXML private ListView listView;
private List<String> stringList = new ArrayList<>(5);
private ObservableList observableList = FXCollections.observableArrayList();
public void setListView(){
stringList.add("String 1");
stringList.add("String 2");
stringList.add("String 3");
stringList.add("String 4");
observableList.setAll(stringList);
listView.setItems(observableList);
listView.setCellFactory(
new Callback<ListView<String>, javafx.scene.control.ListCell<String>>() {
@Override
public ListCell<String> call(ListView<String> listView) {
return new ListViewCell();
}
});
}
@FXML
void initialize() {
assert listView != null : "fx:id=\"listView\" was not injected: check your FXML file 'CustomList.fxml'.";
setListView();
}
}//ListViewController
The JavaFX platform needs to be started in the main() method from a JavaFX Application. Netbeans conviently provides most of this structure from the Maven JavaFX application template.
public class MainApp extends Application {
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/fxml/CustomList.fxml"));
Scene scene = new Scene(root);
scene.getStylesheets().add("/styles/Styles.css");
stage.setTitle("CustomList");
stage.setScene(scene);
stage.show();
}
/**
* The main() method is ignored in correctly deployed JavaFX application.
*
* @param args the command line arguments
**/
public static void main(String[] args) {
launch(args);
}
}