JavaFX TableView заполняет столбцы одинаковым значением 3 раза

У меня есть этот фрагмент кода для настройки TableView.

    @Override
    public void initialize(URL location, ResourceBundle resources){
        if (App.Customers.get(App.currentcustomerindex).getAccounts().get(App.currentaccountindex).getAccounttype().equalsIgnoreCase("tmb")||App.Customers.get(App.currentcustomerindex).getAccounts().get(App.currentaccountindex).getAccounttype().equalsIgnoreCase("gold")){
            Checking workchecking = (Checking) App.Customers.get(App.currentcustomerindex).getAccounts().get(App.currentaccountindex);
            String Balance = Double.toString(workchecking.getBalance());
            String Dateopened = workchecking.getDateopened().toString();
            String Savingsid = workchecking.getSavingsaccountid();
            ObservableList<String> list = FXCollections.observableArrayList();
            list.addAll(Balance,Dateopened,Savingsid);
            tablebox.setItems(list);
            TableColumn<String,String> column1 = new TableColumn<String,String>("Balance");
            column1.setCellValueFactory(cellData -> new SimpleStringProperty(Balance));
            TableColumn<String,String> column2= new TableColumn<>("Date Opened");
            column2.setCellValueFactory(cellData -> new SimpleStringProperty(Dateopened));
            TableColumn<String,String> column3 = new TableColumn<>("Savings ID");
            column3.setCellValueFactory(cellData -> new SimpleStringProperty(Savingsid));
            tablebox.getColumns().setAll(column1,column2,column3);
        }
    }

Что создает следующий TableView:

Итак, есть идеи, что я здесь делаю не так? Я также попробовал cellData.getValue() вместо Balance, DateOpened и SavingsID в SimpleStringProperty с теми же результатами. Я бы хотел, чтобы данные отображались только в первой строке.

Похоже, вы создаете новый StringProperty каждый раз, когда вызывается фабрика. Вместо этого повторно используйте один экземпляр, например .

trashgod 14.04.2024 15:22
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
1
61
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Разобрался с этим:

    public void initialize(URL location, ResourceBundle resources){
        if (App.Customers.get(App.currentcustomerindex).getAccounts().get(App.currentaccountindex).getAccounttype().equalsIgnoreCase("tmb")||App.Customers.get(App.currentcustomerindex).getAccounts().get(App.currentaccountindex).getAccounttype().equalsIgnoreCase("gold")){
            Checking workchecking = (Checking) App.Customers.get(App.currentcustomerindex).getAccounts().get(App.currentaccountindex);
            String Balance = Double.toString(workchecking.getBalance());
            String Dateopened = workchecking.getDateopened().toString();
            String Savingsid = workchecking.getSavingsaccountid();
            ObservableList<Checking> list = FXCollections.observableArrayList();
            list.addAll(workchecking);
            tablebox.setItems(list);
            TableColumn<Checking,Double> column1 = new TableColumn<>("Balance");
            column1.setCellValueFactory(new PropertyValueFactory<>("balance"));
            TableColumn<Checking,LocalDate> column2= new TableColumn<>("Date Opened");
            column2.setCellValueFactory(new PropertyValueFactory<>("dateopened"));
            TableColumn<Checking,String> column3 = new TableColumn<>("Savings ID");
            column3.setCellValueFactory(new PropertyValueFactory<>("savingsaccountid"));
            tablebox.getColumns().setAll(column1,column2,column3);
        }
    }

значения в PropertyValueFactory — это имена соответствующих атрибутов из класса Checking.

Лучшая альтернатива приведена в разделе _Почему мне следует избегать использования PropertyValueFactory в JavaFX?

trashgod 14.04.2024 15:18

Спасибо за ответ, для этого пришлось учиться на лету и, по-видимому, видеть много плохих ответов.

Thomas Hutton 15.04.2024 08:39
Ответ принят как подходящий

Первое, что вам нужно исправить, это использование TableView<String>. Каждая строка вашей таблицы представляет экземпляр Checking, поэтому вам следует создать экземпляр TableView<Checking>. Элементы в этой таблице должны быть объектами, а не строками.

Collection<Checking> workchecking =
    App.Customers.get(App.currentcustomerindex).getAccounts();

tablebox.getItems().setAll(workchecking);

У вас есть несколько записей о проверке, поэтому чтение баланса, даты и идентификатора в данный момент бессмысленно. Не читайте ни одного из них при создании таблицы. Вместо этого читайте эти значения только тогда, когда TableColumn их запрашивает, что он будет делать, вызывая фабрики значений ячеек.

Вся суть фабрики значений ячеек заключается в том, что она извлекает значения записи в запрошенной строке. Какой ряд, спросите вы? Строка, указанная в CellDataFeatures, которая передается в фабрику значений ячеек.

Итак, вы можете просто написать:

TableColumn<Checking, String> balanceColumn = new TableColumn<>("Balance");
balanceColumn.setCellValueFactory(
    cellData -> new SimpleStringProperty(
        String.valueOf(cellData.getValue().getBalance())));

TableColumn<Checking, String> dateColumn = new TableColumn<>("Date Opened");
column2.setCellValueFactory(
    cellData -> new SimpleStringProperty(
        String.valueOf(cellData.getValue().getDateopened())));

TableColumn<Checking, String> idColumn = new TableColumn<>("Savings ID");
column3.setCellValueFactory(
    cellData -> new SimpleStringProperty(
        cellData.getValue().getSavingsaccountid()));

tablebox.getColumns().add(balanceColumn);
tablebox.getColumns().add(dateColumn);
tablebox.getColumns().add(idColumn);

В каждой из вышеупомянутых фабрик значений ячеек Checking предоставляет объект cellData.getValue() в строке, значения которой TableView собирается отобразить. В этом вся суть объекта CellDataFeatures.

Trashgod уже дал ссылку на объяснение того, почему никогда не следует использовать PropertyValueFactory. Подведем итог: с PropertyValueFactory легко допустить ошибку, и ни компилятор, ни PropertyValueFactory не сообщат вам, когда вы допустили такую ​​ошибку. Вместо этого напишите свои собственные фабрики значений ячеек, как указано выше; компилятор может проверить их корректность.

Форматирование

Идем еще дальше: не следует превращать все данные в строки. TableView позволяет пользователю сортировать по столбцам, но поскольку ваши значения являются строками, он будет сортировать Checking перед таким значением, как 155.28. Это происходит потому, что они упорядочиваются по символам, а не по числовым значениям.

Вы можете решить эту проблему, заставив фабрику значений ячеек возвращать необработанные данные, в то время как фабрика ячеек (а не фабрика значений ячеек) форматирует данные для отображения. Это работает, потому что TableView сортируется на основе необработанных данных (полученных из фабрик значений ячеек), а не на том, как данные отображаются.

Для этого присвойте каждому столбцу фактические типы данных:

TableColumn<Checking, Double> balanceColumn = new TableColumn<>("Balance");
balanceColumn.setCellValueFactory(
    cellData -> new SimpleDoubleProperty(
        cellData.getValue().getBalance()));

TableColumn<Checking, LocalDate> dateColumn = new TableColumn<>("Date Opened");
column2.setCellValueFactory(
    cellData -> new SimpleObjectProperty<LocalDate>(
        cellData.getValue().getDateopened()));

TableColumn<Checking, String> idColumn = new TableColumn<>("Savings ID");
column3.setCellValueFactory(
    cellData -> new SimpleStringProperty(
        cellData.getValue().getSavingsaccountid()));

tablebox.getColumns().add(balanceColumn);
tablebox.getColumns().add(dateColumn);
tablebox.getColumns().add(idColumn);

Обратите внимание на использование 19.10 и TableColumn<Checking, Double> для обозначения типа данных, отображаемых в каждом из этих столбцов. Также обратите внимание на использование SimpleDoubleProperty и SimpleObjectProperty вместо SimpleStringProperty.

Чтобы они отображались в определенном формате, создайте один объект форматирования и используйте его в фабрике ячеек (которая отличается от фабрики значений ячеек):

NumberFormat balanceFormat = NumberFormat.getCurrencyInstance();

balanceColumn.setCellFactory(c -> new TableCell<Checking, Double>() {
    @Override
    protected void updateItem(Double balance,
                              boolean empty) {

        super.updateItem(balance, empty);

        setGraphic(null);
        if (empty || item == null) {
            setText(null);
        } else {
            setText(balanceFormat.format(balance));
        }
    }
});

Крайне важно, чтобы фабрика ячеек вызывала super.updateItem и правильно обрабатывала пустые и нулевые случаи. документация Cell.updateItem подробно объясняет это.

Кажется, вас устраивает, что ваши даты отображаются в формате по умолчанию (ISO 8601). Если вы хотите, чтобы они были локализованы, вы можете сделать для дат то же самое, что приведенный выше код делает для значений баланса:

DateTimeFormatter dateFormatter =
    DateTimeformatter.ofLocalizedDate(FormatStyle.MEDIUM);

dateColumn.setCellFactory(c -> new TableCell<Checking, LocalDate>() {
    @Override
    protected void updateItem(LocalDate date,
                              boolean empty) {

        super.updateItem(date, empty);

        setGraphic(null);
        if (empty || item == null) {
            setText(null);
        } else {
            setText(dateFormatter.format(date));
        }
    }
});

Опять же, преимуществом этого является то, что TableView будет сортировать даты в хронологическом, а не в алфавитном порядке.

Спасибо за подробный ответ, я многое узнал о Javafx за довольно короткий период времени. Экземпляр валюты особенно полезен.

Thomas Hutton 15.04.2024 08:40

У меня есть один вопрос: на какую переменную item ссылается часть кода setText? Когда я использую его, IDE просто говорит мне создать локальную переменную.

Thomas Hutton 15.04.2024 08:47

Неважно, просто нужно было вставить входную переменную вместо элемента.

Thomas Hutton 15.04.2024 08:57

теперь моя проблема в том, что я использую один ComboBox для переключения между тремя разными подклассами, поэтому методы get не одинаковы для каждого подкласса. Я перечисляю типы сбережений, чеков и ссуд в одном поле со списком типа «Счет».

Thomas Hutton 15.04.2024 09:11

@ThomasHutton Поскольку ComboBox может отображать только один тип, и этот тип нельзя изменить, я рекомендую создать три экземпляра ComboBox, по одному для каждого типа. Поместите их все в один StackPane и передайте true методу setVisible того, который вы хотите отобразить, и вызовите setVisible(false) для тех, которые вы не хотите отображать.

VGR 15.04.2024 14:19

на самом деле это именно то, что я в итоге сделал! Спасибо за ответ. Однако я не устанавливал их в StackPane (они просто прикреплены к панели привязки окна друг над другом), у нас заканчивается время на этот проект, поэтому я могу сделать это, если мы закончим все остальное

Thomas Hutton 16.04.2024 01:28

Другие вопросы по теме