teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

1

見直しキャンペーン中

2023/07/26 15:55

投稿

TN8001
TN8001

スコア10108

answer CHANGED
@@ -1,207 +1,208 @@
1
- [JavaFX - JavaFXのTableColumnに中身を追加したい|teratail](https://teratail.com/questions/331339)
2
- 過去質問で解決したはずの部分まで壊れているのはなんででしょうか?
3
- 試行錯誤は大変結構ですが、手あたり次第に勘で書き換えてもうまくいきません。
4
- `TableView`はもともと難しいので、1行1行どういう意味かザックリでも理解しながらやってください。
5
-
6
- まず[TableColumn<S,T>](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/scene/control/TableColumn.html)の型引数が逆です。
7
- そのため`ScheduleDataCell`も逆になってしまい、ヌルポがでます。
8
-
9
- `setCellValueFactory`があったはずですが、丸々抜けています。
10
- 通常は表示するプロパティを指定するわけですが、セルにまとめて表示したいわけですね?
11
- 単に改行したいだけなら各プロパティを`concat`するのが簡単です。
12
-
13
- あくまで`ScheduleDataCell`を利用したいなら、アイテム自身(`ScheduleData`)をセルに渡す必要がありますが、[ObservableValue<T>](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/beans/value/ObservableValue.html)でなければなりません。
14
- [TableColumn#setCellValueFactory](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/scene/control/TableColumn.html#setCellValueFactory-javafx.util.Callback-)
15
-
16
- 単純に[ReadOnlyObjectWrapper<T>](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/beans/property/ReadOnlyObjectWrapper.html)で包んでみたところ、編集が即時反映されませんでした(まあ当然か)
17
-
18
- ちょっと正しいか自信はないのですが、`ScheduleData`を`ObjectBinding`にして内部のプロパティとバインドしたところ想定した動作になりました。
19
-
20
- ```fxml
21
- <?xml version="1.0" encoding="UTF-8"?>
22
-
23
- <?import javafx.scene.control.TableColumn?>
24
- <?import javafx.scene.control.TableView?>
25
- <?import javafx.scene.layout.BorderPane?>
26
- <BorderPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1"
27
- fx:controller="sample.ScheduleTableController">
28
- <center>
29
- <TableView fx:id="table" editable="true">
30
- <columns>
31
- <TableColumn fx:id="scheduleColumn" prefWidth="100" text="schedule"/>
32
- <TableColumn fx:id="concatColumn" prefWidth="100" text="concat"/>
33
- <TableColumn fx:id="titleColumn" prefWidth="100" text="title"/>
34
- <TableColumn fx:id="timeColumn" prefWidth="100" text="time"/>
35
- <TableColumn fx:id="detailColumn" prefWidth="100" text="detail"/>
36
- </columns>
37
- </TableView>
38
- </center>
39
- </BorderPane>
40
- ```
41
-
42
- ```Java
43
- package sample;
44
-
45
- import java.net.URL;
46
- import java.time.LocalTime;
47
- import java.util.ResourceBundle;
48
-
49
- import javafx.beans.property.ReadOnlyObjectWrapper;
50
- import javafx.collections.FXCollections;
51
- import javafx.collections.ObservableList;
52
- import javafx.fxml.FXML;
53
- import javafx.fxml.Initializable;
54
- import javafx.scene.control.TableCell;
55
- import javafx.scene.control.TableColumn;
56
- import javafx.scene.control.TableView;
57
- import javafx.scene.control.cell.PropertyValueFactory;
58
- import javafx.scene.control.cell.TextFieldTableCell;
59
- import javafx.scene.layout.VBox;
60
- import javafx.scene.text.Text;
61
-
62
-
63
- public class ScheduleTableController implements Initializable {
64
- @FXML
65
- private TableView<ScheduleData> table;
66
- @FXML
67
- private TableColumn<ScheduleData, ScheduleData> scheduleColumn; // アイテム自身をセルに出すため<ScheduleData, ScheduleData>になる
68
- @FXML
69
- private TableColumn<ScheduleData, String> concatColumn; // 通常はアイテムの型(TableView<T>のT), プロパティの型(StringPropertyならString)
70
- @FXML
71
- private TableColumn<ScheduleData, String> titleColumn;
72
- @FXML
73
- private TableColumn<ScheduleData, String> timeColumn;
74
- @FXML
75
- private TableColumn<ScheduleData, String> detailColumn;
76
-
77
- @Override public void initialize(URL location, ResourceBundle resources) {
78
- // ScheduleDataがObjectBindingでなくとも、Wrapperでコード上は通る
79
- // が、編集した時の挙動が異なる
80
- // 即時反映されずセルのクリックでupdateItemが走る模様
81
- // scheduleColumn.setCellValueFactory(x -> new ReadOnlyObjectWrapper<>(x.getValue()));
82
-
83
- // これでいいのか自信はないが、とりあえず想定通りに動いている
84
- scheduleColumn.setCellValueFactory(x -> x.getValue());
85
- scheduleColumn.setCellFactory(x -> new ScheduleDataCell());
86
-
87
-
88
- // 単にまとめたいだけならつなげればいい
89
- concatColumn.setCellValueFactory(x ->
90
- x.getValue().titleProperty()
91
- .concat("\n")
92
- .concat(x.getValue().timeProperty())
93
- .concat("\n")
94
- .concat(x.getValue().detailProperty()));
95
-
96
-
97
- // titleColumn.setCellValueFactory(new PropertyValueFactory<>("title")); // ↓同じ意味
98
- titleColumn.setCellValueFactory(x -> x.getValue().titleProperty());
99
- titleColumn.setCellFactory(TextFieldTableCell.forTableColumn());
100
-
101
- timeColumn.setCellValueFactory(x -> x.getValue().timeProperty());
102
- timeColumn.setCellFactory(TextFieldTableCell.forTableColumn());
103
-
104
- detailColumn.setCellValueFactory(x -> x.getValue().detailProperty());
105
- detailColumn.setCellFactory(TextFieldTableCell.forTableColumn());
106
-
107
- ObservableList<ScheduleData> schedules = FXCollections.observableArrayList(
108
- new ScheduleData("部活", LocalTime.of(12, 30), LocalTime.of(16, 0), "試合"),
109
- new ScheduleData("部活", LocalTime.of(12, 30), LocalTime.of(16, 0), "試合"));
110
- table.setItems(schedules);
111
- }
112
- }
113
-
114
- class ScheduleDataCell extends TableCell<ScheduleData, ScheduleData> {
115
- private VBox cellContainer = new VBox();
116
- private Text scheduleTitle = new Text();
117
- private Text time = new Text();
118
- private Text txtDetail = new Text();
119
-
120
- public ScheduleDataCell() {
121
- cellContainer.getChildren().addAll(scheduleTitle, time, txtDetail);
122
- }
123
-
124
- @Override protected void updateItem(ScheduleData item, boolean empty) {
125
- super.updateItem(item, empty);
126
-
127
- if (!empty) {
128
- scheduleTitle.setText(item.titleProperty().get());
129
- time.setText(item.timeProperty().get());
130
- txtDetail.setText(item.detailProperty().get());
131
-
132
- // これでもよさそうな気もするが、いつunbindすればいいかよくわからない
133
- // scheduleTitle.textProperty().bind(item.titleProperty());
134
- // time.textProperty().bind(item.timeProperty());
135
- // txtDetail.textProperty().bind(item.detailProperty());
136
-
137
- setGraphic(cellContainer);
138
- }
139
- }
140
- }
141
- ```
142
-
143
- ```Java
144
- package sample;
145
-
146
- import java.time.LocalTime;
147
-
148
- import javafx.beans.binding.ObjectBinding;
149
- import javafx.beans.property.SimpleStringProperty;
150
- import javafx.beans.property.StringProperty;
151
-
152
-
153
- public class ScheduleData extends ObjectBinding<ScheduleData> {
154
- private StringProperty title = new SimpleStringProperty();
155
- private StringProperty time = new SimpleStringProperty();
156
- private StringProperty detail = new SimpleStringProperty();
157
-
158
- public ScheduleData(String title, LocalTime startTime, LocalTime finishTime, String detail) {
159
- this.title.set(title);
160
- time.set(startTime + "~" + finishTime);
161
- this.detail.set(detail);
162
-
163
- bind(this.title, time, this.detail);
164
- }
165
-
166
- public StringProperty titleProperty() { return title; }
167
-
168
- public StringProperty timeProperty() { return time; }
169
-
170
- public StringProperty detailProperty() { return detail; }
171
-
172
- @Override protected ScheduleData computeValue() { return this; }
173
- }
174
- ```
175
-
176
- ```Java
177
- package sample;
178
-
179
- import javafx.application.Application;
180
- import javafx.fxml.FXMLLoader;
181
- import javafx.scene.Parent;
182
- import javafx.scene.Scene;
183
- import javafx.stage.Stage;
184
-
185
-
186
- public class Main extends Application {
187
- public static void main(String[] args) { launch(args); }
188
-
189
- @Override public void start(Stage primaryStage) throws Exception {
190
- Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
191
- primaryStage.setScene(new Scene(root, 600, 300));
192
- primaryStage.show();
193
- }
194
- }
195
- ```
196
-
197
- ---
198
-
199
- そもそも`TableView`は表形式で編集するのに便利なコンポーネントだと思うのですが、まとめて表示の意味は何なのでしょう?
200
-
201
- ---
202
-
203
- [Java - TableViewにデータを追加したい|teratail](https://teratail.com/questions/332466)
204
- と全く同じ質問ですよね?
205
- [推奨していない質問|teratail(テラテイル)](https://teratail.com/help/avoid-asking)
206
-
1
+ [JavaFX - JavaFXのTableColumnに中身を追加したい|teratail](https://teratail.com/questions/331339)
2
+ 過去質問で解決したはずの部分まで壊れているのはなんででしょうか?
3
+ 試行錯誤は大変結構ですが、手あたり次第に勘で書き換えてもうまくいきません。
4
+ `TableView`はもともと難しいので、1行1行どういう意味かザックリでも理解しながらやってください。
5
+
6
+ まず[TableColumn<S,T>](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/scene/control/TableColumn.html)の型引数が逆です。
7
+ そのため`ScheduleDataCell`も逆になってしまい、ヌルポがでます。
8
+
9
+ `setCellValueFactory`があったはずですが、丸々抜けています。
10
+ 通常は表示するプロパティを指定するわけですが、セルにまとめて表示したいわけですね?
11
+ 単に改行したいだけなら各プロパティを`concat`するのが簡単です。
12
+
13
+ あくまで`ScheduleDataCell`を利用したいなら、アイテム自身(`ScheduleData`)をセルに渡す必要がありますが、[ObservableValue<T>](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/beans/value/ObservableValue.html)でなければなりません。
14
+ [TableColumn#setCellValueFactory](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/scene/control/TableColumn.html#setCellValueFactory-javafx.util.Callback-)
15
+
16
+ 単純に[ReadOnlyObjectWrapper<T>](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/beans/property/ReadOnlyObjectWrapper.html)で包んでみたところ、編集が即時反映されませんでした(まあ当然か)
17
+
18
+ ちょっと正しいか自信はないのですが、`ScheduleData`を`ObjectBinding`にして内部のプロパティとバインドしたところ想定した動作になりました。
19
+
20
+ ```xml
21
+ <?xml version="1.0" encoding="UTF-8"?>
22
+
23
+ <?import javafx.scene.control.TableColumn?>
24
+ <?import javafx.scene.control.TableView?>
25
+ <?import javafx.scene.layout.BorderPane?>
26
+ <BorderPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1"
27
+ fx:controller="sample.ScheduleTableController">
28
+ <center>
29
+ <TableView fx:id="table" editable="true">
30
+ <columns>
31
+ <TableColumn fx:id="scheduleColumn" prefWidth="100" text="schedule"/>
32
+ <TableColumn fx:id="concatColumn" prefWidth="100" text="concat"/>
33
+ <TableColumn fx:id="titleColumn" prefWidth="100" text="title"/>
34
+ <TableColumn fx:id="timeColumn" prefWidth="100" text="time"/>
35
+ <TableColumn fx:id="detailColumn" prefWidth="100" text="detail"/>
36
+ </columns>
37
+ </TableView>
38
+ </center>
39
+ </BorderPane>
40
+ ```
41
+
42
+ ```Java
43
+ package sample;
44
+
45
+ import java.net.URL;
46
+ import java.time.LocalTime;
47
+ import java.util.ResourceBundle;
48
+
49
+ import javafx.beans.property.ReadOnlyObjectWrapper;
50
+ import javafx.collections.FXCollections;
51
+ import javafx.collections.ObservableList;
52
+ import javafx.fxml.FXML;
53
+ import javafx.fxml.Initializable;
54
+ import javafx.scene.control.TableCell;
55
+ import javafx.scene.control.TableColumn;
56
+ import javafx.scene.control.TableView;
57
+ import javafx.scene.control.cell.PropertyValueFactory;
58
+ import javafx.scene.control.cell.TextFieldTableCell;
59
+ import javafx.scene.layout.VBox;
60
+ import javafx.scene.text.Text;
61
+
62
+
63
+ public class ScheduleTableController implements Initializable {
64
+ @FXML
65
+ private TableView<ScheduleData> table;
66
+ @FXML
67
+ private TableColumn<ScheduleData, ScheduleData> scheduleColumn; // アイテム自身をセルに出すため<ScheduleData, ScheduleData>になる
68
+ @FXML
69
+ private TableColumn<ScheduleData, String> concatColumn; // 通常はアイテムの型(TableView<T>のT), プロパティの型(StringPropertyならString)
70
+ @FXML
71
+ private TableColumn<ScheduleData, String> titleColumn;
72
+ @FXML
73
+ private TableColumn<ScheduleData, String> timeColumn;
74
+ @FXML
75
+ private TableColumn<ScheduleData, String> detailColumn;
76
+
77
+ @Override public void initialize(URL location, ResourceBundle resources) {
78
+ // ScheduleDataがObjectBindingでなくとも、Wrapperでコード上は通る
79
+ // が、編集した時の挙動が異なる
80
+ // 即時反映されずセルのクリックでupdateItemが走る模様
81
+ // scheduleColumn.setCellValueFactory(x -> new ReadOnlyObjectWrapper<>(x.getValue()));
82
+
83
+ // これでいいのか自信はないが、とりあえず想定通りに動いている
84
+ scheduleColumn.setCellValueFactory(x -> x.getValue());
85
+ scheduleColumn.setCellFactory(x -> new ScheduleDataCell());
86
+
87
+
88
+ // 単にまとめたいだけならつなげればいい
89
+ concatColumn.setCellValueFactory(x ->
90
+ x.getValue().titleProperty()
91
+ .concat("\n")
92
+ .concat(x.getValue().timeProperty())
93
+ .concat("\n")
94
+ .concat(x.getValue().detailProperty()));
95
+
96
+
97
+ // titleColumn.setCellValueFactory(new PropertyValueFactory<>("title")); // ↓同じ意味
98
+ titleColumn.setCellValueFactory(x -> x.getValue().titleProperty());
99
+ titleColumn.setCellFactory(TextFieldTableCell.forTableColumn());
100
+
101
+ timeColumn.setCellValueFactory(x -> x.getValue().timeProperty());
102
+ timeColumn.setCellFactory(TextFieldTableCell.forTableColumn());
103
+
104
+ detailColumn.setCellValueFactory(x -> x.getValue().detailProperty());
105
+ detailColumn.setCellFactory(TextFieldTableCell.forTableColumn());
106
+
107
+ ObservableList<ScheduleData> schedules = FXCollections.observableArrayList(
108
+ new ScheduleData("部活", LocalTime.of(12, 30), LocalTime.of(16, 0), "試合"),
109
+ new ScheduleData("部活", LocalTime.of(12, 30), LocalTime.of(16, 0), "試合"));
110
+ table.setItems(schedules);
111
+ }
112
+ }
113
+
114
+ class ScheduleDataCell extends TableCell<ScheduleData, ScheduleData> {
115
+ private VBox cellContainer = new VBox();
116
+ private Text scheduleTitle = new Text();
117
+ private Text time = new Text();
118
+ private Text txtDetail = new Text();
119
+
120
+ public ScheduleDataCell() {
121
+ cellContainer.getChildren().addAll(scheduleTitle, time, txtDetail);
122
+ }
123
+
124
+ @Override protected void updateItem(ScheduleData item, boolean empty) {
125
+ super.updateItem(item, empty);
126
+
127
+ if (!empty) {
128
+ scheduleTitle.setText(item.titleProperty().get());
129
+ time.setText(item.timeProperty().get());
130
+ txtDetail.setText(item.detailProperty().get());
131
+
132
+ // これでもよさそうな気もするが、いつunbindすればいいかよくわからない
133
+ // scheduleTitle.textProperty().bind(item.titleProperty());
134
+ // time.textProperty().bind(item.timeProperty());
135
+ // txtDetail.textProperty().bind(item.detailProperty());
136
+
137
+ setGraphic(cellContainer);
138
+ }
139
+ }
140
+ }
141
+ ```
142
+
143
+ ```Java
144
+ package sample;
145
+
146
+ import java.time.LocalTime;
147
+
148
+ import javafx.beans.binding.ObjectBinding;
149
+ import javafx.beans.property.SimpleStringProperty;
150
+ import javafx.beans.property.StringProperty;
151
+
152
+
153
+ public class ScheduleData extends ObjectBinding<ScheduleData> {
154
+ private StringProperty title = new SimpleStringProperty();
155
+ private StringProperty time = new SimpleStringProperty();
156
+ private StringProperty detail = new SimpleStringProperty();
157
+
158
+ public ScheduleData(String title, LocalTime startTime, LocalTime finishTime, String detail) {
159
+ this.title.set(title);
160
+ time.set(startTime + "~" + finishTime);
161
+ this.detail.set(detail);
162
+
163
+ bind(this.title, time, this.detail);
164
+ }
165
+
166
+ public StringProperty titleProperty() { return title; }
167
+
168
+ public StringProperty timeProperty() { return time; }
169
+
170
+ public StringProperty detailProperty() { return detail; }
171
+
172
+ @Override protected ScheduleData computeValue() { return this; }
173
+ }
174
+ ```
175
+
176
+ ```Java
177
+ package sample;
178
+
179
+ import javafx.application.Application;
180
+ import javafx.fxml.FXMLLoader;
181
+ import javafx.scene.Parent;
182
+ import javafx.scene.Scene;
183
+ import javafx.stage.Stage;
184
+
185
+
186
+ public class Main extends Application {
187
+ public static void main(String[] args) { launch(args); }
188
+
189
+ @Override public void start(Stage primaryStage) throws Exception {
190
+ Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
191
+ primaryStage.setScene(new Scene(root, 600, 300));
192
+ primaryStage.show();
193
+ }
194
+ }
195
+ ```
196
+
197
+ ---
198
+
199
+ そもそも`TableView`は表形式で編集するのに便利なコンポーネントだと思うのですが、まとめて表示の意味は何なのでしょう?
200
+
201
+ ---
202
+
203
+ [Java - TableViewにデータを追加したい|teratail](https://teratail.com/questions/332466)
204
+ と全く同じ質問ですよね?
205
+
206
+ [推奨していない質問|teratail(テラテイル)](https://teratail.com/help/avoid-asking)
207
+
207
208
  省略された部分が多く試す気が起きずにスルーしてしまいましたが、省略すべきはなくても動く部分(自動生成されたコメントや`initStyle()`等)であって、`initComponent()`や`fxml`等なくては動かない部分はできる限り記載してください。