Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javafx Listview Add and edit element

i want to add and edit directly an element to a listview :

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package javafx_test;

import java.util.Observable;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.StringConverter;

/**
 *
 * @author karim
 */
public class Javafx_test extends Application {

    @Override
    public void start(Stage primaryStage) {
        ObservableList<String> items = FXCollections.observableArrayList("test1", "test2");
        ListView<String> list = new ListView<>(items);

        list.setEditable(true);
        list.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {

            @Override
            public ListCell<String> call(ListView<String> param) {
                return new TextFieldListCell<>(new StringConverter<String>() {

                    @Override
                    public String toString(String object) {
                        return object;
                    }

                    @Override
                    public String fromString(String string) {
                        return string;
                    }
                });
            }
        });

        Button btn = new Button();
        btn.setText("Add String");
        btn.setOnAction((ActionEvent event) -> {
            String c = new String("test");
            list.getItems().add(list.getItems().size(), c);
            list.scrollTo(c);
            list.edit(list.getItems().size() - 1);
        });

        VBox root = new VBox(list, btn);

        Scene scene = new Scene(root);

        primaryStage.setTitle("test!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}

Everything seems correct but that not working, it like its try to modify the first item not the newly added item in the last index, i don't know why

like image 535
KarimS Avatar asked Sep 21 '15 16:09

KarimS


3 Answers

That's a bug.

There seems to be some truly horrible interplay between focus and editing. The basic problem seems to be that when a list cell loses focus, it cancels any editing. I think that by clicking on the button, you cause the focus to shift to that button, and then on the next rendering pulse the list cell sees it has lost focus and cancels editing. I can't quite explain why the first item in the list appears to go to an editing state, but I suspect it is due to some further interaction with the list's focusModel, which manages focus of individual items.

For a truly ugly hack, use an AnimationTimer to delay the call to ListView.edit(...) by an additional rendering frame. (In case you're not familiar with it, an AnimationTimer defines a handle(...) method that is invoked once on each rendering pulse; here I just count one frame and then call edit, and stop the timer.)

    btn.setOnAction((ActionEvent event) -> {
        String c = "test"+(list.getItems().size()+1);
        list.getItems().add(list.getItems().size(), c);
        list.scrollTo(list.getItems().size() - 1);
        // list.edit(list.getItems().size() - 1);

        new AnimationTimer() {

            int frameCount = 0 ;

            @Override
            public void handle(long now) {
                frameCount++ ;
                if (frameCount > 1) {        
                    list.edit(list.getItems().size() - 1);
                    stop();
                }
            }

        }.start();
    });

Calling scrollTo(...) with an index instead of an item seems more robust too (especially as you have items in there that are equal to each other :).)

Maybe someone else can come up with something a bit cleaner that this...

like image 184
James_D Avatar answered Oct 01 '22 01:10

James_D


The issue seems to be the Cells not being updated before calling edit. Since updating the cells is done during layout, calling layout before starting the edit should fix the issue:

Example:

@Override
public void start(Stage primaryStage) {
    ListView<String> listView = new ListView<>();
    listView.setEditable(true);
    listView.setCellFactory(TextFieldListCell.forListView());
    Button editButton = new Button("Add & Edit");
    editButton.setOnAction((ActionEvent event) -> {
        listView.getItems().add("");
        listView.scrollTo(listView.getItems().size() - 1);
        listView.layout();
        listView.edit(listView.getItems().size() - 1);
    });

    Scene scene = new Scene(new VBox(listView, editButton));

    primaryStage.setScene(scene);
    primaryStage.show();
}
like image 34
fabian Avatar answered Oct 01 '22 01:10

fabian


What James_D already mentioned: It seems that the index for the edit method is calculated wrong. In the following example it should not grab the correct index, but it does.

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ListEdit extends Application {

    int i = 3;

    @Override
    public void start(Stage primaryStage) {
        ObservableList<String> items = FXCollections.observableArrayList("test1", "test2");
        ListView<String> list = new ListView<>(items);
        list.setCellFactory(TextFieldListCell.forListView());
        list.setEditable(true);

        Button btn = new Button();
        btn.setText("Add String");
        btn.setOnAction((ActionEvent event) -> {
            list.getItems().add(i - 1, "test" + i);
            list.edit(i - 2);
            i++;
        });

        VBox root = new VBox(list, btn);

        Scene scene = new Scene(root);

        primaryStage.setTitle("test!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Application

like image 43
aw-think Avatar answered Oct 01 '22 02:10

aw-think