Javafx TableCell.setText(字符串)不';t设置与单元格关联的数据值

Javafx TableCell.setText(字符串)不';t设置与单元格关联的数据值,javafx,tableview,cell,Javafx,Tableview,Cell,在我的特殊情况下,我有一个包含按钮的TableCell的自定义实现。此按钮调用一个方法,该方法返回要显示的字符串而不是按钮。通过使用TableCell.setText(String)将单元格中的图形设置为null,并将文本设置为字符串,可以完成可视更改 到目前为止,我已经意识到,TableCell.setText(String)不会更改与TableView中的单元格关联的数据值。它只是改变了细胞的视觉表现。在我的例子中,底层数据结构是一个表示一行的ObservableList,当然,列表中的每个

在我的特殊情况下,我有一个包含
按钮的
TableCell
的自定义实现。此按钮调用一个方法,该方法返回要显示的字符串而不是按钮。通过使用
TableCell.setText(String)
将单元格中的图形设置为null,并将文本设置为字符串,可以完成可视更改

到目前为止,我已经意识到,
TableCell.setText(String)
不会更改与
TableView
中的单元格关联的数据值。它只是改变了细胞的视觉表现。在我的例子中,底层数据结构是一个表示一行的
ObservableList
,当然,列表中的每个元素都是单元格数据

我当前的解决方案是通过以下方式设置基础值:

getTableView().getItems().get(getIndex()).set(getTableView().getColumns().indexOf(getTableColumn()), "Value");
这个很好用。但我的意思是,代码几乎不可读

似乎
TableView
TableCell
中的数据是完全分开的,因为您需要访问
TableView
来设置单元格的基础数据。有一个
TableCell.getItem()
来获取数据值,但是没有
setItem(String)
方法来设置它

我希望我能很好地解释我的问题

有更好更漂亮的方法吗?为什么不直接使用`TableCell.setText(字符串)来更改数据值呢

编辑:我将解释我试图实现的内容:

我基本上有一个表,其中一列包含一个按钮,当按下该按钮时,该按钮将向该列加载一些任意数据。加载数据后,按钮将从列中删除,并显示数据。基本上就是这样。除非对表进行排序/筛选,否则此操作正常。以下是我的实现的MCVE:

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.Duration;

public class MCVE extends Application {
    private final BooleanProperty countLoading = new SimpleBooleanProperty(this, "countLoading", false);

    @Override
    public void start(Stage stage) {
        int numOfCols = 3;

        ObservableList<ObservableList<String>> tableData = FXCollections.observableArrayList();

        // Generate dummy data.
        for (int i = 0; i < 100; i++) {
            ObservableList<String> row = FXCollections.observableArrayList();

            for (int j = 0; j < numOfCols; j++)
                row.add("Row" + i + "Col" + j);

            tableData.add(row);
        }

        TableView<ObservableList<String>> table = new TableView<ObservableList<String>>();

        // Add columns to the table.
        for (int i = 0; i < numOfCols; i++) {
            if (i == 2) {
                final int j = i;
                table.getColumns().add(addColumn(i, "Column " + i, e -> new QueueCountCell(j, countLoading)));
            } else {
                table.getColumns().add(addColumn(i, "Column " + i, null));
            }
        }

        table.getItems().addAll(tableData);

        Scene scene = new Scene(table);

        stage.setScene(scene);
        stage.show();
    }

    /**
     * Returns a simple column.
     */
    private TableColumn<ObservableList<String>, String> addColumn(int index, String name,
            Callback<TableColumn<ObservableList<String>, String>, TableCell<ObservableList<String>, String>> callback) {
        TableColumn<ObservableList<String>, String> col = new TableColumn<ObservableList<String>, String>(name);
        col.setCellValueFactory(e -> new SimpleStringProperty(e.getValue().get(index)));

        if (callback != null) {
            col.setCellFactory(callback);
        }
        return col;
    }

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

    class QueueCountCell extends TableCell<ObservableList<String>, String> {
        private final Button loadButton = new Button("Load");

        public QueueCountCell(int colIndex, BooleanProperty countLoading) {

            countLoading.addListener((obs, oldValue, newValue) -> {
                if (newValue) {
                    loadButton.setDisable(true);
                } else {
                    if (getIndex() >= 0 && getIndex() < this.getTableView().getItems().size()) {
                        loadButton.setDisable(false);
                    }
                }
            });

            final Timeline timeline = new Timeline(new KeyFrame(Duration.ZERO, e -> setText("Loading .")),
                    new KeyFrame(Duration.millis(500), e -> setText("Loading . .")),
                    new KeyFrame(Duration.millis(1000), e -> setText("Loading . . .")),
                    new KeyFrame(Duration.millis(1500)));

            timeline.setCycleCount(Animation.INDEFINITE);

            loadButton.setOnAction(e -> {
                new Thread(new Task<String>() {
                    @Override
                    public String call() throws InterruptedException {
                        // Simlute task working.
                        Thread.sleep(3000);
                        return "5";
                    }

                    @Override
                    public void running() {
                        setGraphic(null);
                        timeline.play();
                        countLoading.set(true);
                    }

                    @Override
                    public void succeeded() {
                        timeline.stop();

                        countLoading.set(false);

                        setText(getValue());
                    }

                    @Override
                    public void failed() {
                        timeline.stop();

                        countLoading.set(false);

                        setGraphic(loadButton);
                        setText(null);

                        this.getException().printStackTrace();
                    }
                }).start();
            });
        }

        @Override
        public final void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);

            if (item == null || empty) {
                setGraphic(null);
            } else {
                setGraphic(loadButton);
            }
        }
    }
}
导入javafx.animation.animation;
导入javafx.animation.KeyFrame;
导入javafx.animation.Timeline;
导入javafx.application.application;
导入javafx.beans.property.BooleanProperty;
导入javafx.beans.property.SimpleBoleAnProperty;
导入javafx.beans.property.SimpleStringProperty;
导入javafx.collections.FXCollections;
导入javafx.collections.ObservableList;
导入javafx.concurrent.Task;
导入javafx.scene.scene;
导入javafx.scene.control.Button;
导入javafx.scene.control.TableCell;
导入javafx.scene.control.TableColumn;
导入javafx.scene.control.TableView;
导入javafx.stage.stage;
导入javafx.util.Callback;
导入javafx.util.Duration;
公共类MCVE扩展应用程序{
private final BooleanProperty countLoading=新的SimpleBoleanProperty(此“countLoading”,false);
@凌驾
公众假期开始(阶段){
int numocols=3;
ObservableList tableData=FXCollections.observableArrayList();
//生成虚拟数据。
对于(int i=0;i<100;i++){
ObservableList行=FXCollections.observableArrayList();
对于(int j=0;jnewQueueCountCell(j,countLoading));
}否则{
table.getColumns().add(addColumn(i,“Column”+i,null));
}
}
table.getItems().addAll(tableData);
场景=新场景(表);
舞台场景;
stage.show();
}
/**
*返回一个简单列。
*/
private TableColumn addColumn(int索引、字符串名称、,
回调(回调){
TableColumn col=新的TableColumn(名称);
col.setCellValueFactory(e->new SimpleStringProperty(e.getValue().get(index));
if(回调!=null){
setCellFactory上校(回拨);
}
返回列;
}
公共静态void main(字符串[]args){
发射();
}
类QueueCountCell扩展了TableCell{
专用最终按钮加载按钮=新按钮(“加载”);
公共QueueCountCell(int-colIndex,BooleanProperty countLoading){
countLoading.addListener((obs、oldValue、newValue)->{
如果(新值){
loadButton.setDisable(真);
}否则{
如果(getIndex()>=0&&getIndex()setText(“加载”),
新关键帧(持续时间.millis(500),e->setText(“加载…”),
新的关键帧(Duration.millis(1000),e->setText(“加载…”),
新关键帧(持续时间.millis(1500));
timeline.setCycleCount(Animation.unfinite);
加载按钮。设置操作(e->{
新线程(新任务(){
@凌驾
公共字符串调用()引发InterruptedException{
//简单的任务工作。
睡眠(3000);
返回“5”;
}
@凌驾
公营机构{
设置图形(空);
timeline.play();
countLoading.set(真);
}
@凌驾
公营机构{
timeline.stop();
countLoading.set(假);
setText(getValue());
}
@凌驾
public void失败(){
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleObjectProperty;

public class LazyLoadingData<T> {

    public enum LoadingState { NOT_LOADED, LOADING, LOADED }

    private final ObjectProperty<T> data = new SimpleObjectProperty<>(null);
    private final ReadOnlyObjectWrapper<LoadingState> loadingState 
        = new ReadOnlyObjectWrapper<>(LoadingState.NOT_LOADED);

    public LazyLoadingData(T data) {

        // listeners to keep properties consistent with each other:

        this.data.addListener((obs, oldData, newData) -> {
            if (newData == null) {
                loadingState.set(LoadingState.NOT_LOADED);
            } else {
                loadingState.set(LoadingState.LOADED);
            }
        });
        this.loadingState.addListener((obs, oldState, newState) -> {
            if (newState != LoadingState.LOADED) {
                this.data.set(null);
            }
        });

        this.data.set(data);
    }

    public LazyLoadingData() {
        this(null);
    }

    public void startLoading() {
        loadingState.set(LoadingState.LOADING);
    }

    public final ObjectProperty<T> dataProperty() {
        return this.data;
    }


    public final T getData() {
        return this.dataProperty().get();
    }


    public final void setData(final T data) {
        this.dataProperty().set(data);
    }


    public final ReadOnlyObjectProperty<LoadingState> loadingStateProperty() {
        return this.loadingState.getReadOnlyProperty();
    }


    public final LazyLoadingData.LoadingState getLoadingState() {
        return this.loadingStateProperty().get();
    }

}
import java.util.List;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import javafx.concurrent.Task;

public class LazyLoadingDataController {
    // data model:
    private final List<List<LazyLoadingData<String>>> data ;

    private final Random rng = new Random();

    private final Executor exec = Executors.newCachedThreadPool(r -> {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t ;
    });

    public LazyLoadingDataController(List<List<LazyLoadingData<String>>> data) {
        this.data = data ;
    }

    public void loadData(int column, int row) {
        Task<String> loader = new Task<String>() {
            @Override
            protected String call() throws InterruptedException {
                int value = rng.nextInt(1000);
                Thread.sleep(3000);
                return "Data: "+value;
            }
        };
        data.get(row).get(column).startLoading();
        loader.setOnSucceeded(e -> data.get(row).get(column).setData(loader.getValue()));
        exec.execute(loader);
    }
}
import java.util.List;

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.value.ChangeListener;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.util.Duration;

public class LazyLoadingDataCell<T> 
    extends TableCell<List<LazyLoadingData<T>>, LazyLoadingData<T>>{

    private final Button loadButton = new Button("Load");


    private final Timeline loadingAnimation = new Timeline(
            new KeyFrame(Duration.ZERO, e -> setText("Loading")),
            new KeyFrame(Duration.millis(500), e -> setText("Loading.")),
            new KeyFrame(Duration.millis(1000), e -> setText("Loading..")),
            new KeyFrame(Duration.millis(1500), e -> setText("Loading..."))
    );

    public LazyLoadingDataCell(LazyLoadingDataController controller, int columnIndex) {
        loadingAnimation.setCycleCount(Animation.INDEFINITE);
        loadButton.setOnAction(e -> controller.loadData(columnIndex, getIndex()));


        // listener for observing either the dataProperty()
        // or the loadingStateProperty() of the current item:
        ChangeListener<Object> listener = (obs, oldState, newState) -> doUpdate();

        // when the item changes, remove and add the listener:
        itemProperty().addListener((obs, oldItem, newItem) -> {
            if (oldItem != null) {
                oldItem.dataProperty().removeListener(listener);
                oldItem.loadingStateProperty().removeListener(listener);
            }
            if (newItem != null) {
                newItem.dataProperty().addListener(listener);
                newItem.loadingStateProperty().addListener(listener);
            }
            doUpdate();
        });

    }

    @Override
    protected void updateItem(LazyLoadingData<T> item, boolean empty) {
        super.updateItem(item, empty);
        doUpdate();
    }

    private void doUpdate() {
        if (isEmpty() || getItem() == null) {
            setText(null);
            setGraphic(null);
        } else {
            LazyLoadingData.LoadingState state = getItem().getLoadingState();
            if (state == LazyLoadingData.LoadingState.NOT_LOADED) {
                loadingAnimation.stop();
                setText(null);
                setGraphic(loadButton);
            } else if (state == LazyLoadingData.LoadingState.LOADING) {
                setGraphic(null);
                loadingAnimation.play();
            } else if (state == LazyLoadingData.LoadingState.LOADED) {
                loadingAnimation.stop();
                setGraphic(null);
                setText(getItem().getData().toString());
            }           
        }
    }

}
import java.util.List;

import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;

public class LazyLoadingTableExample extends Application {

    private final int numCols = 3 ;
    private final int numRows = 100 ;

    @Override
    public void start(Stage primaryStage) {

        TableView<List<LazyLoadingData<String>>> table = new TableView<>();

        // data model:
        ObservableList<List<LazyLoadingData<String>>> data 
            = FXCollections.observableArrayList();

        table.setItems(data);

        LazyLoadingDataController controller = new LazyLoadingDataController(data);

        // build data:
        for (int i = 0; i < numRows; i++) {

            ObservableList<LazyLoadingData<String>> row 
                = FXCollections.observableArrayList();

            for (int j = 0 ; j < numCols - 1 ; j++) {
                row.add(new LazyLoadingData<>("Cell ["+j+", "+i+"]"));
            }
            row.add(new LazyLoadingData<>());

            data.add(row);
        }

        for (int i = 0 ; i < numCols ; i++) {
            table.getColumns().add(createColumn(controller, i));
        }

        Scene scene = new Scene(table, 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private TableColumn<List<LazyLoadingData<String>>,LazyLoadingData<String>> 
        createColumn(LazyLoadingDataController controller, int columnIndex) {

        TableColumn<List<LazyLoadingData<String>>,LazyLoadingData<String>> col 
            = new TableColumn<>("Column "+columnIndex);

        col.setCellValueFactory(cellData -> 
            new SimpleObjectProperty<>(cellData.getValue().get(columnIndex)));

        col.setCellFactory(tc -> 
            new LazyLoadingDataCell<>(controller, columnIndex));

        return col ;
    }

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