回答編集履歴

1

追記

2017/10/10 13:07

投稿

KSwordOfHaste
KSwordOfHaste

スコア18394

test CHANGED
@@ -59,3 +59,241 @@
59
59
 
60
60
 
61
61
  実際はstartEditで「TableView、TableColumn、Cellが全て編集可能か」をチェックしているので、そのとおりに記述しておいてくれればいいのに・・・と自分などは思いますが、「3つのeditableプロパティーのAND条件である点ははっきり書いてないけど汲み取ってください」ということなのかも知れません・・・
62
+
63
+
64
+
65
+ ---
66
+
67
+ 追記:
68
+
69
+ あるモデルインスタンスの他のプロパティーの値によってeditableを制御する実装を考えてみました。
70
+
71
+ 元の回答に書いたように、セルはいつどのモデルインスタンスに割り当てられるかが予測できませんので、考えられる状態変化を全て監視せねばなりません。
72
+
73
+ そのような煩雑な処理をアプリケーションの論理から分離したいので、「セルに対するモデルインスタンスのプロパティーの値を得るユーティリティー」を別途定義し、それを使った「"locked"プロパティーがfalseの場合にeditableがtrueとなるようなTextEditTableCell」の例をあげてみます。
74
+
75
+
76
+
77
+ ```java
78
+
79
+ @FXML TableColumn<TableModel, String> nameColumn;
80
+
81
+
82
+
83
+ ...
84
+
85
+
86
+
87
+ public void initialize(URL location, ResourceBundle resources) {
88
+
89
+ ...
90
+
91
+ colName.setCellFactory(col -> new TextFieldTableCell<>() {
92
+
93
+ { // instance initialization block
94
+
95
+ // ユーティリティーにより、lockedプロパティーと同じ値を持つ式を生成する
96
+
97
+ ObjectExpression<Boolean> cloneOfLocked =
98
+
99
+ TableModelPropertyFactory.forTableCell(this, model -> model.lockedProperty(), false);
100
+
101
+ // 上の式を使ってeditableプロパティーがlockedの逆になるようバインドする
102
+
103
+ editableProperty().bind(cloneOfLocked.isNotEqualTo(true));
104
+
105
+ }
106
+
107
+ });
108
+
109
+ ...
110
+
111
+ }```
112
+
113
+ ```
114
+
115
+
116
+
117
+ 以下がユーティリティーです。とりあえず作ってみたというレベルなので、実装方法の考え方のヒントと考えていただければと思います。
118
+
119
+
120
+
121
+ ```java
122
+
123
+ import javafx.beans.binding.ObjectExpression;
124
+
125
+ import javafx.beans.property.ReadOnlyProperty;
126
+
127
+ import javafx.beans.property.SimpleObjectProperty;
128
+
129
+ import javafx.beans.value.ChangeListener;
130
+
131
+ import javafx.collections.ListChangeListener;
132
+
133
+ import javafx.collections.ObservableList;
134
+
135
+ import javafx.scene.control.TableCell;
136
+
137
+ import javafx.scene.control.TableView;
138
+
139
+ import javafx.util.Callback;
140
+
141
+
142
+
143
+ import java.util.Objects;
144
+
145
+
146
+
147
+ /**
148
+
149
+ * TableViewのモデルのプロパティーの値を参照するためのファクトリー
150
+
151
+ */
152
+
153
+ public class TableModelPropertyFactory {
154
+
155
+ /**
156
+
157
+ * 特定のTableCellに対応するモデルインスタンスの特定のプロパティーの値を表す式を得る
158
+
159
+ * @param cell 結果のObjectExpressionを参照するTableCell
160
+
161
+ * @param mapper バインドするプロパティーを求める関数
162
+
163
+ * @param defaultValue バインドするプロパティーがない場合のデフォルト値
164
+
165
+ * @param <S> TableViewのモデルの型
166
+
167
+ * @param <U> バインド元のモデルプロパティーの値の型
168
+
169
+ * @return cellに対応したモデルインスタンスの指定プロパティーの値
170
+
171
+ */
172
+
173
+ public static <S, U> ObjectExpression<U> forTableCell(TableCell<S, ?> cell, Callback<S, ReadOnlyProperty<U>> mapper, U defaultValue) {
174
+
175
+ return new PropertyExpression<S, U>(cell, mapper, defaultValue);
176
+
177
+ }
178
+
179
+
180
+
181
+ private static class PropertyExpression<S, U> extends SimpleObjectProperty<U> {
182
+
183
+ private final ChangeListener<?> cl = (observable, oldValue, newValue) -> refreshBinding();
184
+
185
+ private final ListChangeListener<S> lcl = c -> refreshBinding();
186
+
187
+ private final TableCell<S, ?> cell;
188
+
189
+ private final Callback<S, ReadOnlyProperty<U>> mapper;
190
+
191
+ private final U defaultValue;
192
+
193
+
194
+
195
+ private PropertyExpression(TableCell<S, ?> cell, Callback<S, ReadOnlyProperty<U>> mapper, U defaultValue) {
196
+
197
+ super();
198
+
199
+ this.cell = Objects.requireNonNull(cell);
200
+
201
+ this.mapper = Objects.requireNonNull(mapper);
202
+
203
+ this.defaultValue = defaultValue;
204
+
205
+ // 初期バインド
206
+
207
+ refreshBinding();
208
+
209
+ // TableViewのモデルの順番が入れ替わった際にバインドしなおす
210
+
211
+ TableView<S> tableView = cell.getTableView();
212
+
213
+ if (tableView != null)
214
+
215
+ initForTableView(tableView);
216
+
217
+ cell.tableViewProperty().addListener((observable, oldTable, newTable) -> {
218
+
219
+ assert oldTable == null && newTable != null;
220
+
221
+ initForTableView(newTable);
222
+
223
+ });
224
+
225
+
226
+
227
+ // cellのindexが変化したらバインドしなおす
228
+
229
+ cell.indexProperty().addListener((observable, oldIndex, newIndex) -> refreshBinding());
230
+
231
+ }
232
+
233
+
234
+
235
+ private void initForTableView(TableView<S> tableView) {
236
+
237
+ ObservableList<S> items = tableView.getItems();
238
+
239
+ if (items != null)
240
+
241
+ items.addListener(lcl);
242
+
243
+ // テーブルのitemsプロパティーの値(=ObservableList)が入れ替わったら
244
+
245
+ // リスナーを再設定する
246
+
247
+ tableView.itemsProperty().addListener((observable, oldList, newList) -> {
248
+
249
+ if (oldList != null)
250
+
251
+ oldList.removeListener(lcl);
252
+
253
+ if (newList != null)
254
+
255
+ newList.addListener(lcl);
256
+
257
+ });
258
+
259
+ }
260
+
261
+
262
+
263
+ private void refreshBinding() {
264
+
265
+ unbind();
266
+
267
+ TableView<S> tableView = cell.getTableView();
268
+
269
+ if (tableView == null)
270
+
271
+ return;
272
+
273
+ ObservableList<S> items = tableView.getItems();
274
+
275
+ if (items == null)
276
+
277
+ return;
278
+
279
+ int index = cell.getIndex();
280
+
281
+ if (index < 0 || index >= items.size()) {
282
+
283
+ // 表示中のモデルインスタンスなし
284
+
285
+ set(defaultValue);
286
+
287
+ } else {
288
+
289
+ bind(mapper.call(items.get(index)));
290
+
291
+ }
292
+
293
+ }
294
+
295
+ }
296
+
297
+ }
298
+
299
+ ```