У меня есть этот фрагмент кода для настройки 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 с теми же результатами. Я бы хотел, чтобы данные отображались только в первой строке.
Разобрался с этим:
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?
Спасибо за ответ, для этого пришлось учиться на лету и, по-видимому, видеть много плохих ответов.
Первое, что вам нужно исправить, это использование 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 за довольно короткий период времени. Экземпляр валюты особенно полезен.
У меня есть один вопрос: на какую переменную item ссылается часть кода setText? Когда я использую его, IDE просто говорит мне создать локальную переменную.
Неважно, просто нужно было вставить входную переменную вместо элемента.
теперь моя проблема в том, что я использую один ComboBox для переключения между тремя разными подклассами, поэтому методы get не одинаковы для каждого подкласса. Я перечисляю типы сбережений, чеков и ссуд в одном поле со списком типа «Счет».
@ThomasHutton Поскольку ComboBox может отображать только один тип, и этот тип нельзя изменить, я рекомендую создать три экземпляра ComboBox, по одному для каждого типа. Поместите их все в один StackPane и передайте true методу setVisible того, который вы хотите отобразить, и вызовите setVisible(false)
для тех, которые вы не хотите отображать.
на самом деле это именно то, что я в итоге сделал! Спасибо за ответ. Однако я не устанавливал их в StackPane (они просто прикреплены к панели привязки окна друг над другом), у нас заканчивается время на этот проект, поэтому я могу сделать это, если мы закончим все остальное
Похоже, вы создаете новый
StringProperty
каждый раз, когда вызывается фабрика. Вместо этого повторно используйте один экземпляр, например .