如何在单元格';JavaFx中的updateItem方法?

如何在单元格';JavaFx中的updateItem方法?,javafx,tableview,javafx-8,tablecell,Javafx,Tableview,Javafx 8,Tablecell,我希望有一个带有自定义图形的TableCell,该图形根据值的变化进行动画制作,其中动画类型取决于变化的性质,因此我需要知道前一个值以与当前值进行比较 以下是典型的自定义表格单元格(Kotlin代码): 我只能想到一个丑陋的解决方法,所以我想知道是否有一些惯用的方法来获取旧的单元格值 以下是一个示例应用程序: 导入javafx.application.application 导入javafx.beans.property.SimpleDoubleProperty 导入javafx.collec

我希望有一个带有自定义
图形的
TableCell
,该图形根据值的变化进行动画制作,其中动画类型取决于变化的性质,因此我需要知道前一个值以与当前值进行比较

以下是典型的自定义表格单元格(Kotlin代码):

我只能想到一个丑陋的解决方法,所以我想知道是否有一些惯用的方法来获取旧的单元格值

以下是一个示例应用程序:

导入javafx.application.application
导入javafx.beans.property.SimpleDoubleProperty
导入javafx.collections.FXCollections
导入javafx.scene.scene
导入javafx.scene.control*
导入javafx.scene.control.cell.PropertyValueFactory
导入javafx.stage.stage
导入kotlinx.coroutines.experimental.delay
导入kotlinx.coroutines.experimental.launch
导入tornadofx*
福班{
val barProperty=SimpleDoubleProperty()
变量条:双
get()=barProperty.get()
设置(值)=barProperty.set(值)
}
类FooApp:Application(){
覆盖乐趣开始(初级阶段:阶段){
val foos=FXCollections.observearraylist(
Foo().apply{bar=42.0}
)
val表格=表格视图(foos)
val barColumn=TableColumn(“条形”)
barColumn.cellValueFactory=PropertyValueFactory(“bar”)
barColumn.setCellFactory{
FooTableCell{“%.2f”.format(it)}
}
table.columns.add(barColumn)
val场景=场景(表,400.0,200.0)
primaryStage.scene=场景
primaryStage.title=“表格单元格”
primaryStage.show()
发射{
while(isActive){
延迟(500)
val oldFoo=foos[0]
//用新实例替换旧的Foo实例,
//更新'bar'字段的值:
foos[0]=Foo().apply{
bar=oldFoo.bar-1.0+Math.random()*2.0
}
//因为对字段的更改不能被可观察列表检测到
//因此不会传播到表中。这不会导致
//一个明显的变化:
//foos[0]。bar=foos[0]。bar-1.0+Math.random()*2.0
}
}
}
}
类FooTableCell(私有val格式:(T)->字符串):TableCell(){
初始化{
contentDisplay=contentDisplay.GRAPHIC\u仅限
itemProperty().addListener(ChangeListener{obs,oldItem,newItem->
if(newItem!=null&&oldItem!=null&&newItem!=oldItem){
//这从来都不是真的。
println(!!!旧:$oldItem,新:$newItem)
}否则{
println(“更改侦听器:\nOld:$oldItem,New:$newItem\n”)
}
})
}
重写fun updateItem(项:T?,空:布尔值){
val oldItem=this.item
super.updateItem(项,空)
if(item!=null&&oldItem!=null&&item!=oldItem){
//这从来都不是真的。
println(!!!旧:$oldItem,新:$item)
}否则{
println(“updateItem:\nOld:$oldItem,New:$item\n”)
}
if(空| |项==null){
图形=空
text=null
}else if(tableRow!=null){
val cell=此
图形=标签()。应用{
textProperty().bindproduction(cell.textProperty())
}
文本=格式(项目)
}
}
}
趣味主线(args:Array){
launch(FooApp::class.java,*args)
}

通过
updateItem()
方法的默认实现更改
项的实际值,因此只需在调用默认实现之前获取值:

public class MyTableCell<S,T> extends TableCell<S,T> {

    @Override
    protected void updateItem(T item, boolean empty) {
        T oldItem = getItem();
        super.updateItem(item, empty) ;
        // ...
    }
}
以下是一个SSCCE,演示了这两种技术:

import java.util.Random;
import java.util.function.Function;

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
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.HBox;
import javafx.stage.Stage;

public class TableCellWithChange extends Application {

    public static class ChangeAwareCell<S,T> extends TableCell<S,T> {

        public ChangeAwareCell() {
            itemProperty().addListener((obs, oldItem, newItem) -> {
                System.out.printf("In listener, value for %s changed from %s to %s%n", getTableRow().getItem(), oldItem, newItem);
            });
        }

        @Override
        protected void updateItem(T item, boolean empty) {
            T oldItem = getItem();
            super.updateItem(item, empty);
            if (empty) {
                setText(null);
            } else {
                setText(item.toString());
                System.out.printf("Change in %s from %s to %s %n", getTableView().getItems().get(getIndex()), oldItem, item);
            }
        }
    }

    @Override
    public void start(Stage primaryStage) {

        TableView<Item> table = new TableView<>();
        TableColumn<Item, String> itemCol = column("Item", Item::nameProperty);
        table.getColumns().add(itemCol);

        TableColumn<Item, Number> valueCol = column("Value", Item:: valueProperty);
        table.getColumns().add(valueCol);

        valueCol.setCellFactory(tc -> new ChangeAwareCell<>());

        TableColumn<Item, Void> changeCol = new TableColumn<>();
        changeCol.setCellFactory(tc -> new TableCell<>() {
            private Button incButton = new Button("^");
            private Button decButton = new Button("v");
            private HBox graphic = new HBox(2, incButton, decButton);
            {
                incButton.setOnAction(e -> {
                    Item item = getTableRow().getItem();
                    item.setValue(item.getValue()+1);
                });
                decButton.setOnAction(e -> {
                    Item item = getTableRow().getItem();
                    item.setValue(item.getValue()-1);
                });
            }
            @Override
            protected void updateItem(Void item, boolean empty) {
                super.updateItem(item, empty);
                if (empty) {
                    setGraphic(null);
                } else {
                    setGraphic(graphic);
                }
            }
        });
        table.getColumns().add(changeCol);

        Random rng = new Random();
        for (int i = 1 ; i <= 20  ; i++) {
            table.getItems().add(new Item("Item "+i, rng.nextInt(100)));
        }

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

    private <S,T> TableColumn<S,T> column(String text, Function<S, ObservableValue<T>> property) {
        TableColumn<S,T> col = new TableColumn<>(text);
        col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
        col.setPrefWidth(150);
        return col ;
    }


    public static class Item {
        private final StringProperty name = new SimpleStringProperty();
        private final IntegerProperty value = new SimpleIntegerProperty();

        public Item(String name, int value) {
            setName(name);
            setValue(value);
        }

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

        public final StringProperty nameProperty() {
            return this.name;
        }


        public final String getName() {
            return this.nameProperty().get();
        }


        public final void setName(final String name) {
            this.nameProperty().set(name);
        }


        public final IntegerProperty valueProperty() {
            return this.value;
        }


        public final int getValue() {
            return this.valueProperty().get();
        }


        public final void setValue(final int value) {
            this.valueProperty().set(value);
        }



    }

    public static void main(String[] args) {
        launch(args);
    }
}
import java.util.Random;
导入java.util.function.function;
导入javafx.application.application;
导入javafx.beans.property.IntegerProperty;
导入javafx.beans.property.SimpleIntegerProperty;
导入javafx.beans.property.SimpleStringProperty;
导入javafx.beans.property.StringProperty;
导入javafx.beans.value.observeValue;
导入javafx.scene.scene;
导入javafx.scene.control.Button;
导入javafx.scene.control.TableCell;
导入javafx.scene.control.TableColumn;
导入javafx.scene.control.TableView;
导入javafx.scene.layout.HBox;
导入javafx.stage.stage;
公共类TableCellWithChange扩展了应用程序{
公共静态类ChangeAwareCell扩展TableCell{
公共变更通知(){
itemProperty().addListener((obs、oldItem、newItem)->{
System.out.printf(“在侦听器中,%s的值从%s更改为%s%n”,getTableRow().getItem(),oldItem,newItem);
});
}
@凌驾
受保护的void updateItem(T项,布尔值为空){
T oldItem=getItem();
super.updateItem(项,空);
if(空){
setText(空);
}否则{
setText(item.toString());
System.out.printf(“将%s从%s更改为%s%n”,getTableView().getItems().get(getIndex()),oldItem,item);
}
}
}
@凌驾
公共无效开始(阶段primaryStage){
TableView table=新TableView();
TableColumn itemCol=列(“项”,项::名称属性);
table.getColumns().add(itemCol);
TableColumn valueCol=列(“值”,项::valueProperty);
table.getColumns().add(valueCol);
valueCol.setCellFactory(tc->new ChangeAwareCell());
TableColumn changeCol=新的TableColumn();
changeCol.setCellFactory(tc->new TableCell(){
私有按钮incButton=新按钮(“^”);
专用按钮decButton=新按钮(“v”);
专用HBox图形=新HBox(2,递增按钮,递减按钮);
{
因克巴托
import javafx.application.Application
import javafx.beans.property.SimpleDoubleProperty
import javafx.collections.FXCollections
import javafx.scene.Scene
import javafx.scene.control.*
import javafx.scene.control.cell.PropertyValueFactory
import javafx.stage.Stage
import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.launch
import tornadofx.*

class Foo {
    val barProperty = SimpleDoubleProperty()
    var bar: Double
        get() = barProperty.get()
        set(value) = barProperty.set(value)
}


class FooApp: Application() {
    override fun start(primaryStage: Stage) {
        val foos = FXCollections.observableArrayList(
            Foo().apply { bar = 42.0 }
        )

        val table = TableView<Foo>(foos)

        val barColumn = TableColumn<Foo, Double>("Bar")
        barColumn.cellValueFactory = PropertyValueFactory<Foo, Double>("bar")
        barColumn.setCellFactory {
            FooTableCell<Foo, Double> { "%.2f".format(it) }
        }

        table.columns.add(barColumn)

        val scene = Scene(table, 400.0, 200.0)
        primaryStage.scene = scene
        primaryStage.title = "Table Cell"
        primaryStage.show()

        launch {
            while (isActive) {
                delay(500)
                val oldFoo = foos[0]
                // Replacing the old Foo instance with a new one,
                // updating the value of the `bar` field:
                foos[0] = Foo().apply {
                    bar = oldFoo.bar - 1.0 + Math.random() * 2.0
                }
                // because a change to a field cannot be detected by an observable list
                // and so does not propagates to the table. This won't result in
                // a visible change:
                // foos[0].bar = foos[0].bar - 1.0 + Math.random() * 2.0
            }
        }
    }
}

class FooTableCell<S, T>(private val format: (T) -> String) : TableCell<S, T>() {

    init {
        contentDisplay = ContentDisplay.GRAPHIC_ONLY

        itemProperty().addListener(ChangeListener { obs, oldItem, newItem ->
            if (newItem != null && oldItem != null && newItem != oldItem) {
                // This is never true.
                println("!!! Old: $oldItem, New: $newItem")
            } else {
                println("Change listener:\nOld: $oldItem, New: $newItem\n")
            }
        })
    }

    override fun updateItem(item: T?, empty: Boolean) {
        val oldItem = this.item
        super.updateItem(item, empty)

        if (item != null && oldItem != null && item != oldItem) {
            // This is never true.
            println("!!! Old: $oldItem, New: $item")
        } else {
            println("updateItem:\nOld: $oldItem, New: $item\n")
        }

        if (empty || item == null) {
            graphic = null
            text = null
        } else if (tableRow != null) {
            val cell = this
            graphic = Label().apply {
                textProperty().bindBidirectional(cell.textProperty())
            }
            text = format(item)
        }
    }
}

fun main(args: Array<String>) {
    Application.launch(FooApp::class.java, *args)
}
public class MyTableCell<S,T> extends TableCell<S,T> {

    @Override
    protected void updateItem(T item, boolean empty) {
        T oldItem = getItem();
        super.updateItem(item, empty) ;
        // ...
    }
}
public class MyTableCell<S,T> extends TableCell<S,T> {

    public MyTableCell() {
        itemProperty().addListener((obs, oldItem, newItem) -> {
            // do animation here...
        });
    }

    @Override
    protected void updateItem(T item, boolean empty) {
        super.updateItem(item, empty);
        // other functionality here...
    }
}
import java.util.Random;
import java.util.function.Function;

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
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.HBox;
import javafx.stage.Stage;

public class TableCellWithChange extends Application {

    public static class ChangeAwareCell<S,T> extends TableCell<S,T> {

        public ChangeAwareCell() {
            itemProperty().addListener((obs, oldItem, newItem) -> {
                System.out.printf("In listener, value for %s changed from %s to %s%n", getTableRow().getItem(), oldItem, newItem);
            });
        }

        @Override
        protected void updateItem(T item, boolean empty) {
            T oldItem = getItem();
            super.updateItem(item, empty);
            if (empty) {
                setText(null);
            } else {
                setText(item.toString());
                System.out.printf("Change in %s from %s to %s %n", getTableView().getItems().get(getIndex()), oldItem, item);
            }
        }
    }

    @Override
    public void start(Stage primaryStage) {

        TableView<Item> table = new TableView<>();
        TableColumn<Item, String> itemCol = column("Item", Item::nameProperty);
        table.getColumns().add(itemCol);

        TableColumn<Item, Number> valueCol = column("Value", Item:: valueProperty);
        table.getColumns().add(valueCol);

        valueCol.setCellFactory(tc -> new ChangeAwareCell<>());

        TableColumn<Item, Void> changeCol = new TableColumn<>();
        changeCol.setCellFactory(tc -> new TableCell<>() {
            private Button incButton = new Button("^");
            private Button decButton = new Button("v");
            private HBox graphic = new HBox(2, incButton, decButton);
            {
                incButton.setOnAction(e -> {
                    Item item = getTableRow().getItem();
                    item.setValue(item.getValue()+1);
                });
                decButton.setOnAction(e -> {
                    Item item = getTableRow().getItem();
                    item.setValue(item.getValue()-1);
                });
            }
            @Override
            protected void updateItem(Void item, boolean empty) {
                super.updateItem(item, empty);
                if (empty) {
                    setGraphic(null);
                } else {
                    setGraphic(graphic);
                }
            }
        });
        table.getColumns().add(changeCol);

        Random rng = new Random();
        for (int i = 1 ; i <= 20  ; i++) {
            table.getItems().add(new Item("Item "+i, rng.nextInt(100)));
        }

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

    private <S,T> TableColumn<S,T> column(String text, Function<S, ObservableValue<T>> property) {
        TableColumn<S,T> col = new TableColumn<>(text);
        col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
        col.setPrefWidth(150);
        return col ;
    }


    public static class Item {
        private final StringProperty name = new SimpleStringProperty();
        private final IntegerProperty value = new SimpleIntegerProperty();

        public Item(String name, int value) {
            setName(name);
            setValue(value);
        }

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

        public final StringProperty nameProperty() {
            return this.name;
        }


        public final String getName() {
            return this.nameProperty().get();
        }


        public final void setName(final String name) {
            this.nameProperty().set(name);
        }


        public final IntegerProperty valueProperty() {
            return this.value;
        }


        public final int getValue() {
            return this.valueProperty().get();
        }


        public final void setValue(final int value) {
            this.valueProperty().set(value);
        }



    }

    public static void main(String[] args) {
        launch(args);
    }
}
public static class ChangeAwareCell<S,T> extends TableCell<S,T> {

    private Function<S, ObservableValue<T>> property ;
    private ObservableValue<T> lastObservableValue ;

    private ChangeListener<T> listener = (obs, oldValue, newValue) -> valueChanged(oldValue, newValue);

    public ChangeAwareCell(Function<S, ObservableValue<T>> property) {
        this.property = property ;
    }

    private void valueChanged(T oldValue, T newValue) {
        System.out.printf("Value changed from %s to %s %n", oldValue, newValue);
    }

    @Override
    protected void updateItem(T item, boolean empty) {
        super.updateItem(item, empty);
        if (lastObservableValue != null) {
            lastObservableValue.removeListener(listener);
        }
        if (empty) {
            setText(null);
        } else {
            lastObservableValue = property.apply(getTableRow().getItem());
            lastObservableValue.addListener(listener);
            setText(item.toString());
        }
    }
}
valueCol.setCellFactory(tc -> new ChangeAwareCell<>(Item::valueProperty));