How to display changes in the Textfield of an Object live in ListView?

35 Views Asked by At

i am currently coding a small Zettelkasten-app* in Java and Building most of the GUI in scenebuilder.


Further Description irrelevant for the Question, read italic parts only if interested: (The app gives you the possibility of taking notes that are saved in an SQLite3 table and defining buzzwords along the way, which your other notes are then searched for, and you are handed a list of Zettel containing the same buzzwords. You can also group Zettel into collections)


I have a ListView, that allows you to choose one of the Zettel, and its header and text are loaded into TextFields for editing. I want the ListView to display the changes in a Zettel live during typing (the text in the database is updated with every keystroke).

With my current solution, the Thumb or Cell of the Zettel in the LV is only ever updated, when i click on another one.

Here are my bits of code responsible for that functionality.

Controller-Class:

//variables
 @FXML
    public TextField headerZettel;

    @FXML
    public TextArea textZettel;
@FXML
    private ListView<Zettel> zettelList;
    private ObservableList<Zettel> zettelData = FXCollections.observableArrayList();
 public void initializeZettelList() {
        // get ZettelData from database and add to list
        zettelList.setItems(zettelData);

        //CellFactory for manipulation of the listcells
        zettelList.setCellFactory(param -> new ListCell<>() {
            private final VBox vbRoot = new VBox();
            private final Label lblHeader = new Label();
            private final Label lblText = new Label();
            private Zettel zettel;

            {
                lblHeader.setStyle("-fx-font-weight: bold;");
                vbRoot.setSpacing(10);
                vbRoot.getChildren().addAll(lblHeader, lblText);
            }

            @Override
            protected void updateItem(Zettel z, boolean empty) {
                super.updateItem(z, empty);

                if (z != null && !empty) {

                    lblHeader.setText(z.getHeader());
                    lblText.setText(z.getText().split("\n")[0]);
                    setGraphic(vbRoot);
                    zettel = z; // Save the reference to the associated Zettel object
                } else {
                    setGraphic(null);
                    zettel = null; // Clear the reference when the cell is empty
                }
            }
        });
        // ChangeListener to load and handle Zettel-Objects
        zettelList.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Zettel>() {
            @Override
            public void changed(ObservableValue<? extends Zettel> observable, Zettel oldValue, Zettel newValue) {
                if (newValue != null) {
                    currentZettelId = newValue.getZettelId();
                    headerZettel.setText(newValue.getHeader());
                    textZettel.setText(newValue.getText());
                } else {
                    currentZettelId = null;
                    headerZettel.clear();
                    textZettel.clear();
                }
            }
        });
}

in here is the Stream with the ZettelData:

public void updateZettelInDatabase() {
        String header = getHeaderZettelText();
        String text = getTextZettelText();
        if (currentZettelId != null) {
            Zettel zettelToUpdate = zettelData.stream()
                    .filter(z -> Arrays.equals(z.getZettelId(), currentZettelId))
                    .findFirst()
                    .orElse(null);

            if (zettelToUpdate != null) {
                zettelToUpdate.setHeader(header);
                zettelToUpdate.setText(text);

                //Update Zettel-Object in database
                ZettelUpdater zettelUpdater = new ZettelUpdater();
                zettelUpdater.updateZettel(header, text, currentZettelId);
            } else {
                System.out.println("Keine aktuelle ZettelId vorhanden.");
            }

        }
    }

From the Database-Class:

 public static ObservableList<Zettel> getZettelData(){
        ObservableList<Zettel> zettelData = FXCollections.observableArrayList();

        try (Connection conn = DriverManager.getConnection(connectionString);
             PreparedStatement stmt = conn.prepareStatement("SELECT ZettelId, Header, Text, Date FROM zettel");
             ResultSet rs = stmt.executeQuery()) {

            DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");

            while (rs.next()) {
                byte[] zettelId = rs.getBytes("ZettelId");
                String header = rs.getString("Header");
                String text = rs.getString("Text");
                String dateString = rs.getString("Date");


                LocalDate date = LocalDate.parse(dateString, dateFormatter);

                Zettel zettel = new Zettel();
                zettel.setZettelId(zettelId);
                zettel.setHeader(header);
                zettel.setText(text);
                zettel.setDate(date);

                zettelData.add(zettel);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return zettelData;
    }

this is how it looks

Any hints or Ideas, how to make it work live?

Generally i am also very happy for all kinds of critique, as i am very new to java, and this is my first project to finish my course. :)

Thank you for your help, you're too kind.

Herzl Ich

2

There are 2 best solutions below

0
McLutzifer On

Not entirely sure if I understand the problem, but I guess that you can achieve what you want with adding dedicated listeners to headerZettel and textZettel respectively. And then it seems, since you are listening to header and text constantly you can get rid of updateZettelInDatabase() something like

headerZettel.textProperty().addListener((observable, oldValue, newValue) -> ...etc

... but I'm new to Java as well so please be careful with my advice

0
cas edo On

So i tried everything from refactoring the classes to use StringProperties instead of Strings, implementing other Updating-logic, and Binding the ListView and zettelData together and not overwriting the binding anywhere. Nothing worked. What helped in the end, was this line of code:

Platform.runLater(() -> zettelList.refresh());

int this method that saves the edits made on key tipped

 @FXML
    void saveOnChangeZettel(KeyEvent event){
        if (currentZettelId != null) { // Überprüfe, ob eine aktuelle ZettelId vorhanden ist
            updateZettelInDatabase();
            checkForBuzzwordsInCurrentZettel(currentZettelId);

            Platform.runLater(() -> zettelList.refresh());
        }
    }