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);
}
}