JavaFX ListView con oggetti custom

Mattepuffo's logo
JavaFX ListView con oggetti custom

JavaFX ListView con oggetti custom

Per certi versi, JavaFX, mi ricorda un pò la programmazione Android.

E' tutto molto diverso da come si fa in Swing.

Vediamo un esempio di ListView con oggetti custom.

Cominciamo proprio dalla classe contenitore:

import javafx.beans.property.SimpleStringProperty;

public class FilmSearch {

    private SimpleStringProperty overview;
    private SimpleStringProperty title;
    private SimpleStringProperty posterPath;
    private SimpleStringProperty releaseDate;
    private SimpleStringProperty id;

    public FilmSearch(String overview, String title, 
                      String posterPath, String releaseDate, 
                      String id) {
        this.overview = new SimpleStringProperty(overview);
        this.title = new SimpleStringProperty(title);
        this.posterPath = new SimpleStringProperty(posterPath);
        this.releaseDate = new SimpleStringProperty(releaseDate);
        this.id = new SimpleStringProperty(id);
    }

    public String getOverview() {
        return overview.get();
    }

    public SimpleStringProperty overviewProperty() {
        return overview;
    }

    public void setOverview(String overview) {
        this.overview.set(overview);
    }

    public String getTitle() {
        return title.get();
    }

    public SimpleStringProperty titleProperty() {
        return title;
    }

    public void setTitle(String title) {
        this.title.set(title);
    }

    public String getPosterPath() {
        return posterPath.get();
    }

    public SimpleStringProperty posterPathProperty() {
        return posterPath;
    }

    public void setPosterPath(String posterPath) {
        this.posterPath.set(posterPath);
    }

    public String getReleaseDate() {
        return releaseDate.get();
    }

    public SimpleStringProperty releaseDateProperty() {
        return releaseDate;
    }

    public void setReleaseDate(String releaseDate) {
        this.releaseDate.set(releaseDate);
    }

    public String getId() {
        return id.get();
    }

    public SimpleStringProperty idProperty() {
        return id;
    }

    public void setId(String id) {
        this.id.set(id);
    }
}

Fin qui nulla di particolare.

Adesso dobbiamo caricare una ObservableList; come la carichiamo importa relativamente poco.

L'importante è che capiate il concetto di base; nel mio caso interrogo un servizio remoto che da informazioni sul film cercato.

Nel mio controller avrò una cosa del genere:

public class ControllerAdd {

    @FXML
    private TextField txtCercaFilm;

    @FXML
    private ListView listSearch;

    @FXML
    private void cercaFilm() {
        String ricerca = txtCercaFilm.getText();
        if (!ricerca.isEmpty()) {
            try {
                String searchUrl = Config.BASE_URL + ricerca;
                LoadingDialog loadingDialog = new LoadingDialog();
                Task<Void> task = new Task<Void>() {
                    @Override
                    public Void call() throws InterruptedException, UnirestException {
                        HttpResponse response = Unirest.get(searchUrl).asJson();
                        JSONObject obj = new JSONObject(response);
                        String status = obj.get("status").toString();
                        if (status.equals("200")) {
                            ArrayList<FilmSearch> list = new ArrayList<>();
                            JSONObject body = obj.getJSONObject("body");
                            JSONArray arr = body.getJSONArray("array");
                            JSONArray results = arr.getJSONObject(0).getJSONArray("results");
                            for (int i = 0; i < results.length(); i++) {
                                JSONObject tmp = results.getJSONObject(i);
                                list.add(new FilmSearch(
                                        tmp.getString("overview"),
                                        tmp.getString("original_title"),
                                        tmp.getString("poster_path"),
                                        tmp.getString("release_date"),
                                        String.valueOf(tmp.getInt("id"))
                                ));
                            }
                            ObservableList<FilmSearch> obl = FXCollections.observableArrayList(list);
                            listSearch.setItems(obl);
                            listSearch.setCellFactory(filmSearchCell -> new FilmSearchCell());
                        } else {
                            GenericDialog.showDialog("Si è verificato un errore!", Alert.AlertType.ERROR);
                        }
                        return null;
                    }
                };

                loadingDialog.activateProgressBar(task);
                task.setOnSucceeded(event -> {
                    loadingDialog.getDialogStage().close();
                });
                task.setOnCancelled(event -> {
                    loadingDialog.getDialogStage().close();
                });

                loadingDialog.getDialogStage().show();
                Thread thread = new Thread(task);
                thread.start();
            } catch (InterruptedException e) {
                GenericDialog.showDialog(e.getMessage(), Alert.AlertType.WARNING);
            }
        } else {
            GenericDialog.showDialog("Immettere un valore di ricerca!", Alert.AlertType.ERROR);
        }
    }
}

Con questo template FXML:

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.FlowPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.ListView?>

<VBox xmlns="http://javafx.com/javafx/8.0.112" 
xmlns:fx="http://javafx.com/fxml/1"
      fx:controller="com.mp.film.ControllerAdd">
    <children>
        <FlowPane>
            <children>
                <TextField fx:id="txtCercaFilm" promptText="Cerca..." 
text="zootopia" onAction="#cercaFilm">
                    <FlowPane.margin>
                        <Insets right="10.0"/>
                    </FlowPane.margin>
                </TextField>
                <Button fx:id="btnCerca" mnemonicParsing="false" 
onAction="#cercaFilm" text="Cerca" />
            </children>
            <padding>
                <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
            </padding>
        </FlowPane>
    </children>
    <children>
        <ListView fx:id="listSearch"></ListView>
    </children>
</VBox>

Non è finita qui ovviamente, perchè dobbiamo implementare il nostro ListCell (FilmSearchCell):

public class FilmSearchCell extends ListCell<FilmSearch> {

    @FXML
    private Label lblTitle;

    @FXML
    private Label lblOverview;

    @FXML
    private GridPane gridPane;

    @FXML
    private ImageView poster;

    private FXMLLoader mLLoader;

    @Override
    protected void updateItem(FilmSearch fs, boolean empty) {
        super.updateItem(fs, empty);
        if (empty || fs == null) {
            setText(null);
            setGraphic(null);
        } else {
            if (mLLoader == null) {
                mLLoader = new FXMLLoader(getClass().getResource("/film_search_cell.fxml"));
                mLLoader.setController(this);
                try {
                    mLLoader.load();
                } catch (IOException e) {
                    GenericDialog.showDialog(e.getMessage(), Alert.AlertType.ERROR);
                }
            }
            lblTitle.setText(fs.getTitle());
            lblOverview.setText(fs.getOverview());
            poster.setImage(new Image(Config.BASE_POSTER + fs.getPosterPath()));
            setText(null);
            setGraphic(gridPane);
        }
    }

}

Che usa questo template FXML:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Label?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>

<GridPane fx:id="gridPane" maxHeight="-Infinity" minHeight="-Infinity" xmlns="http://javafx.com/javafx/8.0.112"
          xmlns:fx="http://javafx.com/fxml/1">
    <columnConstraints>
        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" 
prefWidth="200.0"/>
        <ColumnConstraints hgrow="SOMETIMES"/>
    </columnConstraints>
    <rowConstraints>
        <RowConstraints/>
        <RowConstraints/>
    </rowConstraints>
    <children>
        <Label fx:id="lblOverview" text="Label" wrapText="true" 
GridPane.rowIndex="1"/>
        <Label fx:id="lblTitle" text="Label" GridPane.rowIndex="0">
            <font>
                <Font name="System Bold" size="13.0"/>
            </font>
        </Label>
        <ImageView fx:id="poster" fitHeight="450.0" fitWidth="200.0"
 pickOnBounds="true" preserveRatio="true"
                   GridPane.columnIndex="1" GridPane.rowSpan="2147483647"/>
    </children>
</GridPane>

C'è parecchia carne al fuoco, ma così otteniamo un controllo completo.

Enjoy!


Condividi

Commentami!