Java 使用tableview的正确方法是什么?

Java 使用tableview的正确方法是什么?,java,javafx,javafx-tableview,Java,Javafx,Javafx Tableview,一段时间以来,我一直试图将我的tableview工作作为一种电子表格,通过背景线程进行更新,当单元格更新时,它会亮起几秒钟(更改样式),然后返回到原始样式。 我已经知道,我不能直接在表格单元格中存储和设置样式,我需要某种支持类来保存这些数据。但是tableview“重用”单元格(对不同的数据使用相同的单元格)的行为真的很奇怪。当所有单元格都显示在屏幕上时,它对我来说是完美无瑕的,但一旦我放置了大约100个单元格,它就开始滚动,有时样式(或设置的图形)会消失,滚动出现后,如果我禁用视图的一些顶部单

一段时间以来,我一直试图将我的tableview工作作为一种电子表格,通过背景线程进行更新,当单元格更新时,它会亮起几秒钟(更改样式),然后返回到原始样式。 我已经知道,我不能直接在表格单元格中存储和设置样式,我需要某种支持类来保存这些数据。但是tableview“重用”单元格(对不同的数据使用相同的单元格)的行为真的很奇怪。当所有单元格都显示在屏幕上时,它对我来说是完美无瑕的,但一旦我放置了大约100个单元格,它就开始滚动,有时样式(或设置的图形)会消失,滚动出现后,如果我禁用视图的一些顶部单元格,滚动后的一些其他单元格也会被禁用,等等。有什么正确的方法吗

我基本上需要的是

Background data thread ---updates--> tableview
Another thread --after few seconds removes style--> tableview
正如我现在所拥有的,我有一个模型类,它保存数据、样式和对表单元格的引用(我禁用了排序,所以应该可以),后台线程更新模型类中的数据,该模型类更改被引用单元格上的样式,并在“样式移除器”线程中注册自己,然后移除样式

我认为发布我的实际代码是没有用的,因为一旦我发现单元格被重用,我的代码就变得太复杂,有点不可读,所以我想以正确的方式完全重做

性能对我来说并不是那么重要,不会有超过100个单元格,但这种高亮显示和在tableview中有按钮必须完美地工作

这就是我的应用程序现在的样子——为了了解我需要什么。

编辑:这里是与此相关的我的链接。

合作者:

  • 在数据端,一个(视图)模型,它具有一个最近更改的属性,该属性在值更改时更新
  • 在视图端,一个自定义单元格,用于侦听最近更改的属性并根据需要更新其样式
棘手的部分是在重复使用或未使用时清理单元格状态:总是(希望!)调用的方法是
cell.updateIndex(int newIndex)
,因此这是取消/注册侦听器的地方

下面是一个可运行(虽然粗糙)的示例

import java.util.logging.Logger;
导入java.util.stream.collector;
导入java.util.stream.stream;
导入de.swingepire.fx.util.FXUtils;
导入javafx.animation.KeyFrame;
导入javafx.animation.Timeline;
导入javafx.application.application;
导入javafx.beans.property.ReadOnlyBooleanProperty;
导入javafx.beans.property.ReadOnlyBooleanWrapper;
导入javafx.beans.property.SimpleStringProperty;
导入javafx.beans.property.StringProperty;
导入javafx.beans.value.ChangeListener;
导入javafx.collections.FXCollections;
导入javafx.collections.ObservableList;
导入javafx.scene.Parent;
导入javafx.scene.scene;
导入javafx.scene.control.Button;
导入javafx.scene.control.TableCell;
导入javafx.scene.control.TableColumn;
导入javafx.scene.control.TableView;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.layout.HBox;
导入javafx.stage.stage;
导入javafx.util.Duration;
公共类TableCoreRecentlyChanged扩展应用程序{
公共静态类RecentChanged扩展了TableCell{
私有ChangeListener recentListener=(src,ov,nv)->updateRecentStyle(nv);
私人假人;
/*
*只是想看看效果。
*/
受保护的void updateRecentStyle(布尔高亮显示){
如果(突出显示){
设置样式(“-fx背景色:#99ff99”);
}否则{
设置样式(“-fx背景色:#009900”);
}
}
@凌驾
公共无效更新索引(整数索引){
if(lastDummy!=null){
lastDummy.recentlyChangedProperty().RemovelListener(recentListener);
lastDummy=null;
}
updateRecentStyle(false);
super.updateIndex(索引);
if(getTableRow()!=null&&getTableRow().getItem()!=null){
lastDummy=getTableRow().getItem();
updateRecentStyle(lastDummy.recentlyChangedProperty().get());
lastDummy.recentlyChangedProperty().addListener(recentListener);
} 
}
@凌驾
受保护的void updateItem(字符串项,布尔值为空){
if(item==getItem())返回;
super.updateItem(项,空);
如果(项==null){
super.setText(null);
super.setGraphic(空);
}否则{
super.setText(项目);
super.setGraphic(空);
}
}
}
私有父getContent(){
TableView表格=新的TableView(createData(50));
table.setEditable(true);
TableColumn=新的TableColumn(“值”);
column.setCellValueFactory(c->c.getValue().valueProperty());
column.setCellFactory(e->new-RecentChanged());
列。设置最小宽度(200);
table.getColumns().addAll(column);
int editIndex=20;
按钮更改值=新按钮(“编辑”);
changeValue.setOnAction(e->{
Dummy=table.getItems().get(editIndex);
dummy.setValue(dummy.getValue()+“x”);
});
HBox按钮=新HBox(10,更改值);
边框窗格内容=新边框窗格(表);
content.setBottom(按钮);
返回内容;
}
私有ObservableList createData(整数大小){
返回FXCollections.observableArrayList(
Stream.generate(Dummy::new)
.限制(尺寸)
.collect(Collectors.toList());
}
私有静态类虚拟{
私有静态整数计数;
ReadOnlyBooleanWrapper recentlyChanged=新建ReadOnlyBooleanWrapper(){
时间轴最近计时器;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import de.swingempire.fx.util.FXUtils;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Parent;
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.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class TableCoreRecentlyChanged extends Application {

    public static class RecentChanged extends TableCell<Dummy, String> {

        private ChangeListener<Boolean> recentListener = (src, ov, nv) -> updateRecentStyle(nv);
        private Dummy lastDummy;

        /*
         * Just to see any effect.
         */
        protected void updateRecentStyle(boolean highlight) {
            if (highlight) {
                setStyle("-fx-background-color: #99ff99");
            } else {
                setStyle("-fx-background-color: #009900");
            }
        }

        @Override
        public void updateIndex(int index) {
            if (lastDummy != null) {
                lastDummy.recentlyChangedProperty().removeListener(recentListener);
                lastDummy = null;
            }
            updateRecentStyle(false);
            super.updateIndex(index);
            if (getTableRow() != null && getTableRow().getItem() != null) {
                lastDummy = getTableRow().getItem();
                updateRecentStyle(lastDummy.recentlyChangedProperty().get());
                lastDummy.recentlyChangedProperty().addListener(recentListener);
            } 
        }

        @Override 
        protected void updateItem(String item, boolean empty) {
            if (item == getItem()) return;

            super.updateItem(item, empty);

            if (item == null) {
                super.setText(null);
                super.setGraphic(null);
            } else {
                super.setText(item);
                super.setGraphic(null);
            }
        }

    }

    private Parent getContent() {
        TableView<Dummy> table = new TableView<>(createData(50));
        table.setEditable(true);

        TableColumn<Dummy, String> column = new TableColumn<>("Value");
        column.setCellValueFactory(c -> c.getValue().valueProperty());
        column.setCellFactory(e -> new RecentChanged());
        column.setMinWidth(200);
        table.getColumns().addAll(column);

        int editIndex = 20; 

        Button changeValue = new Button("Edit");
        changeValue.setOnAction(e -> {
            Dummy dummy = table.getItems().get(editIndex);
            dummy.setValue(dummy.getValue()+"x");
        });
        HBox buttons = new HBox(10, changeValue);
        BorderPane content = new BorderPane(table);
        content.setBottom(buttons);
        return content;
    }

    private ObservableList<Dummy> createData(int size) {
        return FXCollections.observableArrayList(
                Stream.generate(Dummy::new)
                .limit(size)
                .collect(Collectors.toList()));
    }

    private static class Dummy {
        private static int count;

        ReadOnlyBooleanWrapper recentlyChanged = new ReadOnlyBooleanWrapper() {

            Timeline recentTimer;
            @Override
            protected void invalidated() {
                if (get()) {
                    if (recentTimer == null) {
                        recentTimer = new Timeline(new KeyFrame(
                                Duration.millis(2500),
                                ae -> set(false)));
                    }
                    recentTimer.playFromStart();
                } else {
                    if (recentTimer != null) recentTimer.stop();
                }
            }

        };
        StringProperty value = new SimpleStringProperty(this, "value", "initial " + count++) {

            @Override
            protected void invalidated() {
                recentlyChanged.set(true);
            }

        };

        public StringProperty valueProperty() {return value;}
        public String getValue() {return valueProperty().get(); }
        public void setValue(String text) {valueProperty().set(text); }
        public ReadOnlyBooleanProperty recentlyChangedProperty() { return recentlyChanged.getReadOnlyProperty(); }
        public String toString() {return "[dummy: " + getValue() + "]";}
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setScene(new Scene(getContent()));
     //   primaryStage.setTitle(FXUtils.version());
        primaryStage.show();
    }

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

    @SuppressWarnings("unused")
    private static final Logger LOG = Logger
            .getLogger(TableCoreRecentlyChanged.class.getName());
}