使用自定义datepicker tablecell javafx过滤表时问题

发布于 2025-02-11 05:42:31 字数 11985 浏览 2 评论 0 原文

我正在尝试使用DatePicker制作自定义的表单元格。并在表中过滤的列表,该列表根据单选按钮选择过滤内容。当我选择一个单元的日期时,一切正常。

ScreenShot 1

但是,当试图过滤和发射谓词时,日期向下看表的底部。重新选择旧的广播按钮时,它会出现在正确的行中。

ScreenShot 2

有什么想法吗?

我的控制器:

import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.Callback;
import javafx.util.converter.IntegerStringConverter;

import java.net.URL;
import java.time.LocalDate;
import java.util.ResourceBundle;

public class Controller implements Initializable {

    @FXML
    private TableColumn<SetterGetter, String> colStatus;
    @FXML
    private TableColumn<SetterGetter, Integer> colCode;

    @FXML
    private TableColumn<SetterGetter, LocalDate> colDueDate;

    @FXML
    private TableColumn<SetterGetter, String> colName;

    @FXML
    private RadioButton rdAll;

    @FXML
    private RadioButton rdDelayed;

    @FXML
    private RadioButton rdDone;

    @FXML
    private TableView<SetterGetter> tableTasks;

    private RadioButton selectedRadioButton;

    ObservableList<SetterGetter> mainTaskList = FXCollections.observableArrayList();
    FilteredList<SetterGetter> tableTaskList = new FilteredList<>(mainTaskList, p -> true);

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {

        selectedRadioButton = rdAll;

        colCode.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<SetterGetter, Integer>>() {
            @Override
            public void handle(TableColumn.CellEditEvent<SetterGetter, Integer> event) {
                SetterGetter row = event.getRowValue();
                row.setId(event.getNewValue());
            }
        });

        colName.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<SetterGetter, String>>() {
            @Override
            public void handle(TableColumn.CellEditEvent<SetterGetter, String> event) {
                SetterGetter row = event.getRowValue();
                row.setName(event.getNewValue());
            }
        });

        colStatus.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<SetterGetter, String>>() {
            @Override
            public void handle(TableColumn.CellEditEvent<SetterGetter, String> event) {
                SetterGetter row = event.getRowValue();
                row.setStatus(event.getNewValue().equals("منجز") ? 0 : 1);
            }
        });
        colCode.setCellValueFactory(b -> new SimpleIntegerProperty(b.getValue().getId()).asObject());
        colDueDate.setCellValueFactory(b -> b.getValue().getDate());
        colName.setCellValueFactory(b -> new SimpleStringProperty(b.getValue().getName()));
        colStatus.setCellValueFactory(b -> new SimpleStringProperty(
                b.getValue().getStatus() == 0 ? "منجز"
                        : "لم ينجز بعد")
        );

        colCode.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));
        colName.setCellFactory(TextFieldTableCell.forTableColumn());
        colStatus.setCellFactory(ComboBoxTableCell.forTableColumn(FXCollections.observableArrayList(
                "منجز",
                "لم ينجز بعد")
                )
        );
        colDueDate.setCellFactory(new Callback<TableColumn<SetterGetter, LocalDate>, TableCell<SetterGetter, LocalDate>>() {
            @Override
            public TableCell<SetterGetter, LocalDate> call(TableColumn<SetterGetter, LocalDate> setterGetterStringTableColumn) {
                return new DatePickerTableCell();
            }
        });

        mainTaskList.addAll(
                new SetterGetter(0, null, null, 1),
                new SetterGetter(1, null, null, 1)
        );

        tableTasks.setItems(tableTaskList);
    }

    @FXML
    void addCheck(ActionEvent event) {
        mainTaskList.add(new SetterGetter(0,
                null,
                null,
                0)
        );
    }

    @FXML
    void selectRadioButton(ActionEvent event) {
        if (event.getSource() != selectedRadioButton) {
            RadioButton newRadio = (RadioButton) event.getSource();
            newRadio.setStyle("-fx-font-family:arial;-fx-font-size:14;-fx-font-weight:bold;-fx-border-color:red;-fx-border-radius:20;");
            selectedRadioButton.setStyle("-fx-font-family:arial;-fx-font-size:14;-fx-font-weight:bold;");
            selectedRadioButton = newRadio;
        }
        firePredicate();
    }

    private void firePredicate() {
        tableTaskList.setPredicate(p -> {
            if (selectedRadioButton.equals(rdDone) && p.getStatus() != 0)
                return false;
            else if (selectedRadioButton.equals(rdDelayed) && p.getStatus() != 1)
                return false;
            else return true;
        });
    }

}

datePickerTableCell类:

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TableCell;

import java.time.LocalDate;

public class DatePickerTableCell extends TableCell<SetterGetter, LocalDate> {
    private final DatePicker datePicker = new DatePicker();

    public DatePickerTableCell() {

        super();
    }

    @Override
    public void startEdit() {
        super.startEdit();
        setGraphic(datePicker);
        setText(null);
        datePicker.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                commitEdit(datePicker.getValue());
            }
        });
    }

     @Override
      public void commitEdit(LocalDate s) {
          super.commitEdit(s);
          setText(s.toString());
          setGraphic(null);
          setItem(s);
     }


    @Override
    public void cancelEdit() {
        super.cancelEdit();
        setText(datePicker.getValue() == null ? null : datePicker.getValue().toString());
        setGraphic(null);
    }
}

Settergetter类:

import javafx.beans.property.ObjectProperty;

import java.time.LocalDate;

public class SetterGetter {
    int id;
    String name;
    ObjectProperty<LocalDate> date;
    int status;

    public SetterGetter(int id, String name, ObjectProperty<LocalDate> date, int status) {
        this.id = id;
        this.name = name;
        this.date = date;
        this.status = status;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public ObjectProperty<LocalDate> getDate() {
        return date;
    }

    public void setDate(ObjectProperty<LocalDate> date) {
        this.date = date;
    }
}

主类:

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setScene(new Scene(root, 565, 551));
        primaryStage.show();
    }


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

FXML文件:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="551.0" prefWidth="565.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
   <children>
      <TableView fx:id="tableTasks" editable="true" layoutX="16.0" layoutY="163.0" nodeOrientation="LEFT_TO_RIGHT" prefHeight="400.0" prefWidth="554.0" style="-fx-font-family: arial; -fx-font-size: 15;" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0">
         <columns>
            <TableColumn fx:id="colDueDate" prefWidth="112" style="-fx-font-family: arial; -fx-font-size: 15;" text="التاريخ" />
            <TableColumn fx:id="colStatus" prefWidth="112" style="-fx-font-family: arial; -fx-font-size: 15;" text="الحالة" />
            <TableColumn fx:id="colName" prefWidth="112" style="-fx-font-family: arial; -fx-font-size: 15;" text="الأسم" />
            <TableColumn fx:id="colCode" prefWidth="112" style="-fx-font-family: arial; -fx-font-size: 15;" text="الكود" />
         </columns>
         <columnResizePolicy>
            <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
         </columnResizePolicy>
      </TableView>
      <HBox alignment="CENTER" layoutX="160.0" layoutY="27.0" spacing="15.0">
         <children>
            <RadioButton fx:id="rdDelayed" mnemonicParsing="false" onAction="#selectRadioButton" style="-fx-font-family: arial; -fx-font-size: 14; -fx-font-weight: bold;" text="لم ينجز بعد">
               <padding>
                  <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
               </padding>
               <toggleGroup>
                  <ToggleGroup fx:id="radioGroup" />
               </toggleGroup>
            </RadioButton>
            <RadioButton fx:id="rdDone" mnemonicParsing="false" onAction="#selectRadioButton" style="-fx-font-family: arial; -fx-font-size: 14; -fx-font-weight: bold;" text="منجز" toggleGroup="$radioGroup">
               <padding>
                  <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
               </padding>
            </RadioButton>
            <RadioButton fx:id="rdAll" mnemonicParsing="false" onAction="#selectRadioButton" selected="true" style="-fx-font-family: arial; -fx-font-size: 14; -fx-font-weight: bold; -fx-border-color: red; -fx-border-radius: 20 20 20 20;" text="الكل" toggleGroup="$radioGroup">
               <padding>
                  <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
               </padding>
            </RadioButton>
         </children>
      </HBox>
      <Button layoutX="473.0" layoutY="31.0" mnemonicParsing="false" onAction="#addCheck" prefHeight="34.0" prefWidth="77.0" style="-fx-font-family: arial; -fx-font-size: 18;" text="إضافة">
         <font>
            <Font name="Arial" size="17.0" />
         </font>
      </Button>
   </children>
</AnchorPane>

i'm trying to make custom table cell with a DatePicker. and having Filtered list in the table that filters the contents depending on radio button selection. when i select a date for a cell everything works fine.

Screenshot 1

screenshot

but when trying to filter and fire the predicate, the date looks going down to the bottom of the table. and when re-selecting the old radio button, it appears in the correct row.

Screenshot 2

screenshot 2

any Ideas?

my controller :

import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.Callback;
import javafx.util.converter.IntegerStringConverter;

import java.net.URL;
import java.time.LocalDate;
import java.util.ResourceBundle;

public class Controller implements Initializable {

    @FXML
    private TableColumn<SetterGetter, String> colStatus;
    @FXML
    private TableColumn<SetterGetter, Integer> colCode;

    @FXML
    private TableColumn<SetterGetter, LocalDate> colDueDate;

    @FXML
    private TableColumn<SetterGetter, String> colName;

    @FXML
    private RadioButton rdAll;

    @FXML
    private RadioButton rdDelayed;

    @FXML
    private RadioButton rdDone;

    @FXML
    private TableView<SetterGetter> tableTasks;

    private RadioButton selectedRadioButton;

    ObservableList<SetterGetter> mainTaskList = FXCollections.observableArrayList();
    FilteredList<SetterGetter> tableTaskList = new FilteredList<>(mainTaskList, p -> true);

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {

        selectedRadioButton = rdAll;

        colCode.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<SetterGetter, Integer>>() {
            @Override
            public void handle(TableColumn.CellEditEvent<SetterGetter, Integer> event) {
                SetterGetter row = event.getRowValue();
                row.setId(event.getNewValue());
            }
        });

        colName.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<SetterGetter, String>>() {
            @Override
            public void handle(TableColumn.CellEditEvent<SetterGetter, String> event) {
                SetterGetter row = event.getRowValue();
                row.setName(event.getNewValue());
            }
        });

        colStatus.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<SetterGetter, String>>() {
            @Override
            public void handle(TableColumn.CellEditEvent<SetterGetter, String> event) {
                SetterGetter row = event.getRowValue();
                row.setStatus(event.getNewValue().equals("منجز") ? 0 : 1);
            }
        });
        colCode.setCellValueFactory(b -> new SimpleIntegerProperty(b.getValue().getId()).asObject());
        colDueDate.setCellValueFactory(b -> b.getValue().getDate());
        colName.setCellValueFactory(b -> new SimpleStringProperty(b.getValue().getName()));
        colStatus.setCellValueFactory(b -> new SimpleStringProperty(
                b.getValue().getStatus() == 0 ? "منجز"
                        : "لم ينجز بعد")
        );

        colCode.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));
        colName.setCellFactory(TextFieldTableCell.forTableColumn());
        colStatus.setCellFactory(ComboBoxTableCell.forTableColumn(FXCollections.observableArrayList(
                "منجز",
                "لم ينجز بعد")
                )
        );
        colDueDate.setCellFactory(new Callback<TableColumn<SetterGetter, LocalDate>, TableCell<SetterGetter, LocalDate>>() {
            @Override
            public TableCell<SetterGetter, LocalDate> call(TableColumn<SetterGetter, LocalDate> setterGetterStringTableColumn) {
                return new DatePickerTableCell();
            }
        });

        mainTaskList.addAll(
                new SetterGetter(0, null, null, 1),
                new SetterGetter(1, null, null, 1)
        );

        tableTasks.setItems(tableTaskList);
    }

    @FXML
    void addCheck(ActionEvent event) {
        mainTaskList.add(new SetterGetter(0,
                null,
                null,
                0)
        );
    }

    @FXML
    void selectRadioButton(ActionEvent event) {
        if (event.getSource() != selectedRadioButton) {
            RadioButton newRadio = (RadioButton) event.getSource();
            newRadio.setStyle("-fx-font-family:arial;-fx-font-size:14;-fx-font-weight:bold;-fx-border-color:red;-fx-border-radius:20;");
            selectedRadioButton.setStyle("-fx-font-family:arial;-fx-font-size:14;-fx-font-weight:bold;");
            selectedRadioButton = newRadio;
        }
        firePredicate();
    }

    private void firePredicate() {
        tableTaskList.setPredicate(p -> {
            if (selectedRadioButton.equals(rdDone) && p.getStatus() != 0)
                return false;
            else if (selectedRadioButton.equals(rdDelayed) && p.getStatus() != 1)
                return false;
            else return true;
        });
    }

}

DatePickerTableCell class:

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TableCell;

import java.time.LocalDate;

public class DatePickerTableCell extends TableCell<SetterGetter, LocalDate> {
    private final DatePicker datePicker = new DatePicker();

    public DatePickerTableCell() {

        super();
    }

    @Override
    public void startEdit() {
        super.startEdit();
        setGraphic(datePicker);
        setText(null);
        datePicker.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                commitEdit(datePicker.getValue());
            }
        });
    }

     @Override
      public void commitEdit(LocalDate s) {
          super.commitEdit(s);
          setText(s.toString());
          setGraphic(null);
          setItem(s);
     }


    @Override
    public void cancelEdit() {
        super.cancelEdit();
        setText(datePicker.getValue() == null ? null : datePicker.getValue().toString());
        setGraphic(null);
    }
}

SetterGetter class:

import javafx.beans.property.ObjectProperty;

import java.time.LocalDate;

public class SetterGetter {
    int id;
    String name;
    ObjectProperty<LocalDate> date;
    int status;

    public SetterGetter(int id, String name, ObjectProperty<LocalDate> date, int status) {
        this.id = id;
        this.name = name;
        this.date = date;
        this.status = status;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public ObjectProperty<LocalDate> getDate() {
        return date;
    }

    public void setDate(ObjectProperty<LocalDate> date) {
        this.date = date;
    }
}

Main class :

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setScene(new Scene(root, 565, 551));
        primaryStage.show();
    }


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

fxml file:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="551.0" prefWidth="565.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
   <children>
      <TableView fx:id="tableTasks" editable="true" layoutX="16.0" layoutY="163.0" nodeOrientation="LEFT_TO_RIGHT" prefHeight="400.0" prefWidth="554.0" style="-fx-font-family: arial; -fx-font-size: 15;" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0">
         <columns>
            <TableColumn fx:id="colDueDate" prefWidth="112" style="-fx-font-family: arial; -fx-font-size: 15;" text="التاريخ" />
            <TableColumn fx:id="colStatus" prefWidth="112" style="-fx-font-family: arial; -fx-font-size: 15;" text="الحالة" />
            <TableColumn fx:id="colName" prefWidth="112" style="-fx-font-family: arial; -fx-font-size: 15;" text="الأسم" />
            <TableColumn fx:id="colCode" prefWidth="112" style="-fx-font-family: arial; -fx-font-size: 15;" text="الكود" />
         </columns>
         <columnResizePolicy>
            <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
         </columnResizePolicy>
      </TableView>
      <HBox alignment="CENTER" layoutX="160.0" layoutY="27.0" spacing="15.0">
         <children>
            <RadioButton fx:id="rdDelayed" mnemonicParsing="false" onAction="#selectRadioButton" style="-fx-font-family: arial; -fx-font-size: 14; -fx-font-weight: bold;" text="لم ينجز بعد">
               <padding>
                  <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
               </padding>
               <toggleGroup>
                  <ToggleGroup fx:id="radioGroup" />
               </toggleGroup>
            </RadioButton>
            <RadioButton fx:id="rdDone" mnemonicParsing="false" onAction="#selectRadioButton" style="-fx-font-family: arial; -fx-font-size: 14; -fx-font-weight: bold;" text="منجز" toggleGroup="$radioGroup">
               <padding>
                  <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
               </padding>
            </RadioButton>
            <RadioButton fx:id="rdAll" mnemonicParsing="false" onAction="#selectRadioButton" selected="true" style="-fx-font-family: arial; -fx-font-size: 14; -fx-font-weight: bold; -fx-border-color: red; -fx-border-radius: 20 20 20 20;" text="الكل" toggleGroup="$radioGroup">
               <padding>
                  <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
               </padding>
            </RadioButton>
         </children>
      </HBox>
      <Button layoutX="473.0" layoutY="31.0" mnemonicParsing="false" onAction="#addCheck" prefHeight="34.0" prefWidth="77.0" style="-fx-font-family: arial; -fx-font-size: 18;" text="إضافة">
         <font>
            <Font name="Arial" size="17.0" />
         </font>
      </Button>
   </children>
</AnchorPane>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

花心好男孩 2025-02-18 05:42:31

您的演示中缺少多件事。其中,@Jewelsea在评论中已经提到了一个。

问题1:

我注意到您尚未添加日期列的提交事件。您需要将值(日期)持续到行对象中,以在过滤时获得正确的结果。

初始化方法中的代码将是这样的:

colDueDate.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<SetterGetter, LocalDate>>() {
    @Override
    public void handle(TableColumn.CellEditEvent<SetterGetter, LocalDate> event) {
        SetterGetter row = event.getRowValue();
        if(row.date==null){
            row.setDate(new SimpleObjectProperty<>());
        }
        row.date.set(event.getNewValue());
    }
});

问题2

正如@Jewelsea所述,在定义自定义单元时,您需要覆盖TableCell的UpdateItem()方法。 DatePickerTableCell中的代码将为:

@Override
protected void updateItem(LocalDate item, boolean empty) {
    super.updateItem(item, empty);
    if (isEditing()) {
        setText(null);
        setGraphic(datePicker);
    } else {
        setGraphic(null);
        if (item != null) {
            setText(item.toString());
        } else {
            setText(null);
        }
    }
}

UpdateEtem中的丢失IF-ELSE条件是您实际发行单元格值的主要原因。

虚拟流将单元格重新放置,并可以将其放置在任何地方。我们有责任确保在调用更新时正确渲染。如果缺少IF-ELSE条件,则可以看到用错误的值使用的单元格。

问题#3 不完全是问题;)

在Settergetter类中使用适当的命名惯例/可观察性属性的命名惯例。

public LocalDate getDate() {
    return date.get();
}

public void setDate(LocalDate date) {
    this.date.set(date);
}

public ObjectProperty<LocalDate> dateProperty() {
    return date;
}

There are multiple things missing in your demo. Among that, one is already mentioned by @jewelsea in his comment.

Issue#1:

I noticed that you have not added a commit event for the date column. You need to persist the value(date) into the row object to get correct results when filtering.

Your code in the initialize method will be something like this:

colDueDate.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<SetterGetter, LocalDate>>() {
    @Override
    public void handle(TableColumn.CellEditEvent<SetterGetter, LocalDate> event) {
        SetterGetter row = event.getRowValue();
        if(row.date==null){
            row.setDate(new SimpleObjectProperty<>());
        }
        row.date.set(event.getNewValue());
    }
});

Issue#2:

As mentioned by @jewelsea, you need to override the updateItem() method of the TableCell when defining a custom cell. The code in the DatePickerTableCell will be like:

@Override
protected void updateItem(LocalDate item, boolean empty) {
    super.updateItem(item, empty);
    if (isEditing()) {
        setText(null);
        setGraphic(datePicker);
    } else {
        setGraphic(null);
        if (item != null) {
            setText(item.toString());
        } else {
            setText(null);
        }
    }
}

The missing if-else condition in the updateItem is the main cause for your actual issue of the cell value to be in inappropriate place.

A VirtualFlow reuses the cells and can place it any where. It is our duty to ensure that it renders correctly when the updateItem is called. If the if-else condition is missing, you can see cell being used with wrong value.

Issue#3: Not exactly an issue ;)

Use proper naming conventions for the setter/getter of observable properties in the SetterGetter class.

public LocalDate getDate() {
    return date.get();
}

public void setDate(LocalDate date) {
    this.date.set(date);
}

public ObjectProperty<LocalDate> dateProperty() {
    return date;
}
夕嗳→ 2025-02-18 05:42:31

问题的基本问题是隐藏在日期列中包含值的数据行时的视觉伪像。该人工制品的原因是自定义单元实现中 updateItem(s,boolean)的缺失替代,这在重复使用该单元时会导致不正确的单元格状态(如注释和 sai's 答案)。解决方案是实现该方法。

锤击中:updateItem在单元格中指定, indexedCell的直接后代既不是listcell也不是listcell,tree-/tablecell也不是treecell-覆盖它可以做任何有用的事情。这意味着每个自定义单元的实现都扩展了这些必须自己做到这一点。 API文档可以覆盖更新的方法,以允许单元格的完整自定义不太正确。

问题可能是:如何正确覆盖它?其目的是根据项目更新单元格的视觉状态。有两个基本规则:

  • 始终调用super 做自定义的事情之前,
  • 如果有任何示例,则必须颠倒任何设置的内容

protected void updateItem(T item, boolean empty) {
    super.updateItem(item, empty);

    if (empty || item == null) {
        setText(null);
        setGraphic(null);
    } else {
        setText(item.toString());
        setGraphic(getGraphic(item));
    }
}

注意:这与API doc中的示例略有不同在实际设置与项目相关的图形时(无需重置空状态的图形没有意义;)

下一个问题可能是编辑:通常,用于编辑的单元格仅具有输入控件(FI a Textfield)编辑模式。这意味着在切换编辑状态时显示/隐藏这些内容。这样做的责任在于编辑lifecylce方法(start-/cancel-/consitedIt)。

所有应用于下面的自定义datepickertablecell:

  • datePicker是要进行编辑的输入控件,并设置为图形
  • 编辑时,在接收到Picker的操作时进行了图形编辑:请注意,将处理程序设置在constructor
  • UpdateEm中,以配置两种输入,以配置两者控制和文本,独立于编辑状态,
  • 显示/隐藏在编辑生命周期方法中仅通过根据需要设置单元格的contentDisplay来控制;请注意,如果超级没有切换到编辑模式警告,我们会退出启动

:这种严格的关注点(依赖性视觉效果与依赖视觉效果)是可能的,因为FX17可以修复了一堆具有单元格后单元格式状态的错误使用(请参阅发行说明,fi jdk-8264127 对于ListCell, jdk-8265206 for tree-/tablecell, jdk-8265210 对于treecell及相关))

一种替代性自定义datepickertablecell :(

public static class DatePickerTableCellEx1<S> extends TableCell<S, LocalDate> {
    private final DatePicker datePicker = new DatePicker();

    public DatePickerTableCellEx1() {
        datePicker.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                commitEdit(datePicker.getValue());
            }
        });
        setGraphic(datePicker);
        setContentDisplay(ContentDisplay.TEXT_ONLY);
    }

    @Override
    protected void updateItem(LocalDate item, boolean empty) {
        super.updateItem(item, empty);
        if (empty || item == null) {
            setText(null);
            datePicker.setValue(null);
        } else {
            setText(datePicker.getConverter().toString(item));
            datePicker.setValue(item);
        }
    }

    @Override
    public void startEdit() {
        super.startEdit();
        if (!isEditing()) return;
        setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
    }

    @Override
    public void commitEdit(LocalDate s) {
        super.commitEdit(s);
        setContentDisplay(ContentDisplay.TEXT_ONLY);
    }

    @Override
    public void cancelEdit() {
        super.cancelEdit();
        setContentDisplay(ContentDisplay.TEXT_ONLY);
    }
}

主要是无关的)围绕属性的API设计的注释:

通常,属性价值不应为无效,并且

 // the field
 Property<Music> music; 

 // API
 Property<Music> gimmeTheMusic() {

 // usage
 Property<Music> music = radio.gimmeTheMusic();
 assertNotNull(music);
 music.addListener(...  // turn radio down/up);

 // __DO NOT__: it will break the usage
 void change(Property<Music> music) {
     this.music = music;
 }

在此中 无关紧要,并且不可能无效。位置,TableColumn的默认提交处理程序将按预期工作,并且无需安装自定义提交处理程序。

The base problem in the question is the visual artefact when hiding a data row that contains a value in the date column. The reason for that artefact is the missing override of updateItem(S, boolean) in the custom cell implementation which leads to incorrect cell state when that cell is re-used (as already noted in comments and Sai's answer). The solution is to implement the method.

Hammering in: updateItem is specified in Cell and none of the direct descendants of IndexedCell - that is neither ListCell nor Tree-/TableCell nor TreeCell - override it to do anything useful. Which implies that each and every custom cell implementation extending those has to do it itself. The api doc the updateItem method can be overridden to allow for complete customisation of the cell is not quite correct.

Problem might be: how to override it correctly? Its purpose is to update the visual state of the cell based on the item. There are two basic rules:

  • always call super before doing custom stuff
  • whatever is set if there is an item must be reversed if there is none

Example:

protected void updateItem(T item, boolean empty) {
    super.updateItem(item, empty);

    if (empty || item == null) {
        setText(null);
        setGraphic(null);
    } else {
        setText(item.toString());
        setGraphic(getGraphic(item));
    }
}

Note: this is slightly different from the example in the api doc in actually setting an item-related graphic (without, resetting the graphic for the empty state doesn't make sense ;)

Next problem might be editing: typically, a cell meant for editing has an input control (f.i. a TextField) only while in editing mode. This implies showing/hiding those when toggling editing state. The responsibility for doing so resides in the edit lifecylce methods (start-/cancel-/commitEdit).

All applied to a custom DatePickerTableCell below:

  • a DatePicker is the input control for editing and is set as the graphic
  • editing is committed when receiving an action from the picker: note that the handler is set once in the constructor
  • updateItem is implemented to configure both input control and text, independent of editing state
  • the showing/hiding is controlled exclusively in the edit lifecycle methods by setting the cell's contentDisplay as needed; note that we back out off startEdit if super didn't switch into editing mode

Caveat: this strict separation of concerns (editing dependent visuals vs item dependent visuals) is possible since fx17 which fixes a bunch of bugs with cell editing state after cell re-use (see the release notes, f.i. JDK-8264127 for ListCell, JDK-8265206 for Tree-/TableCell, JDK-8265210 for TreeCell and related)

An alternative custom DatePickerTableCell:

public static class DatePickerTableCellEx1<S> extends TableCell<S, LocalDate> {
    private final DatePicker datePicker = new DatePicker();

    public DatePickerTableCellEx1() {
        datePicker.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                commitEdit(datePicker.getValue());
            }
        });
        setGraphic(datePicker);
        setContentDisplay(ContentDisplay.TEXT_ONLY);
    }

    @Override
    protected void updateItem(LocalDate item, boolean empty) {
        super.updateItem(item, empty);
        if (empty || item == null) {
            setText(null);
            datePicker.setValue(null);
        } else {
            setText(datePicker.getConverter().toString(item));
            datePicker.setValue(item);
        }
    }

    @Override
    public void startEdit() {
        super.startEdit();
        if (!isEditing()) return;
        setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
    }

    @Override
    public void commitEdit(LocalDate s) {
        super.commitEdit(s);
        setContentDisplay(ContentDisplay.TEXT_ONLY);
    }

    @Override
    public void cancelEdit() {
        super.cancelEdit();
        setContentDisplay(ContentDisplay.TEXT_ONLY);
    }
}

(Mostly unrelated) note on api design around Properties:

Typically, property valued fields should be not null and immutable

 // the field
 Property<Music> music; 

 // API
 Property<Music> gimmeTheMusic() {

 // usage
 Property<Music> music = radio.gimmeTheMusic();
 assertNotNull(music);
 music.addListener(...  // turn radio down/up);

 // __DO NOT__: it will break the usage
 void change(Property<Music> music) {
     this.music = music;
 }

With that in place, TableColumn's default commit handler will work as expected and there's no need to install a custom commit handler.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文