質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
JavaFX

JavaFXとは、Java仮想マシン上で動作するリッチインターネットアプリケーション (RIA) のGUIライブラリです。Swingとは異なり、FXMLと呼ばれる XMLとCSSを併用してデザインを記述します。

Q&A

解決済

1回答

1752閲覧

TableViewで特定のセルのみ編集可能にしたい

otenkikaeru

総合スコア8

JavaFX

JavaFXとは、Java仮想マシン上で動作するリッチインターネットアプリケーション (RIA) のGUIライブラリです。Swingとは異なり、FXMLと呼ばれる XMLとCSSを併用してデザインを記述します。

0グッド

0クリップ

投稿2017/10/09 10:05

Java8 JavaFX でプログラムを作成しています。

TableViewで列の設定は編集不可にしている状態で
特定のセルのみ編集可能にする方法はありますでしょうか?

ご存知の方いらっしゃいましたら、ご教授お願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

Cellクラスにはずばりその目的のためのeditableプロパティーがありますのでそれが使えます。ただし、例えば**TextFieldTableCellなどを用いる場合はTableViewおよびTableColumnは編集可能でなければならない(※)**ため、TableColumnを編集不可にしたままでは実現できません。

さて、例えば「先頭行だけ編集可能にしたい」といった場合なら、バインディングを用いて次のように書けます。

java

1@FXML TableColumn<TableModel, String> nameColumn; 2 3... 4 5public void initialize(URL location, ResourceBundle resources) { 6 ... 7 nameColumn.setCellValueFactory(new PropertyValueFactory("name")); 8 nameColumn.setCellFactory(column -> new TextFieldTableCell() { 9 { // instance initialization block 10 editableProperty().bind(indexProperty().isEqualTo(0)); 11 } 12 }); 13 ... 14}

蛇足ながら・・・
Cellとモデルインスタンス(のプロパティ)の対応関係はTableViewの表示状態によって自動的に(勝手に)変更されてしまう点に注意してください。つまりCell生成時にeditableプロパティーへ固定値を設定してもダメで、依存関係にあるプロパティーの状態を監視し常に適切な値となるようにしなければなりません。それにはaddListenerも使えますが、バインディングの方がより確実に表現できます。


※: セルが編集可能とは?
TextFieldTableCell#startEditのAPIリファレンスには以下のようにしか書かれてません。

この関数を呼び出して、セルが編集可能な場合に、非編集状態から編集状態に遷移します。

実際はstartEditで「TableView、TableColumn、Cellが全て編集可能か」をチェックしているので、そのとおりに記述しておいてくれればいいのに・・・と自分などは思いますが、「3つのeditableプロパティーのAND条件である点ははっきり書いてないけど汲み取ってください」ということなのかも知れません・・・


追記:
あるモデルインスタンスの他のプロパティーの値によってeditableを制御する実装を考えてみました。
元の回答に書いたように、セルはいつどのモデルインスタンスに割り当てられるかが予測できませんので、考えられる状態変化を全て監視せねばなりません。
そのような煩雑な処理をアプリケーションの論理から分離したいので、「セルに対するモデルインスタンスのプロパティーの値を得るユーティリティー」を別途定義し、それを使った「"locked"プロパティーがfalseの場合にeditableがtrueとなるようなTextEditTableCell」の例をあげてみます。

java

1@FXML TableColumn<TableModel, String> nameColumn; 2 3... 4 5public void initialize(URL location, ResourceBundle resources) { 6 ... 7 colName.setCellFactory(col -> new TextFieldTableCell<>() { 8 { // instance initialization block 9 // ユーティリティーにより、lockedプロパティーと同じ値を持つ式を生成する 10 ObjectExpression<Boolean> cloneOfLocked = 11 TableModelPropertyFactory.forTableCell(this, model -> model.lockedProperty(), false); 12 // 上の式を使ってeditableプロパティーがlockedの逆になるようバインドする 13 editableProperty().bind(cloneOfLocked.isNotEqualTo(true)); 14 } 15 }); 16 ... 17}```

以下がユーティリティーです。とりあえず作ってみたというレベルなので、実装方法の考え方のヒントと考えていただければと思います。

java

1import javafx.beans.binding.ObjectExpression; 2import javafx.beans.property.ReadOnlyProperty; 3import javafx.beans.property.SimpleObjectProperty; 4import javafx.beans.value.ChangeListener; 5import javafx.collections.ListChangeListener; 6import javafx.collections.ObservableList; 7import javafx.scene.control.TableCell; 8import javafx.scene.control.TableView; 9import javafx.util.Callback; 10 11import java.util.Objects; 12 13/** 14 * TableViewのモデルのプロパティーの値を参照するためのファクトリー 15 */ 16public class TableModelPropertyFactory { 17 /** 18 * 特定のTableCellに対応するモデルインスタンスの特定のプロパティーの値を表す式を得る 19 * @param cell 結果のObjectExpressionを参照するTableCell 20 * @param mapper バインドするプロパティーを求める関数 21 * @param defaultValue バインドするプロパティーがない場合のデフォルト値 22 * @param <S> TableViewのモデルの型 23 * @param <U> バインド元のモデルプロパティーの値の型 24 * @return cellに対応したモデルインスタンスの指定プロパティーの値 25 */ 26 public static <S, U> ObjectExpression<U> forTableCell(TableCell<S, ?> cell, Callback<S, ReadOnlyProperty<U>> mapper, U defaultValue) { 27 return new PropertyExpression<S, U>(cell, mapper, defaultValue); 28 } 29 30 private static class PropertyExpression<S, U> extends SimpleObjectProperty<U> { 31 private final ChangeListener<?> cl = (observable, oldValue, newValue) -> refreshBinding(); 32 private final ListChangeListener<S> lcl = c -> refreshBinding(); 33 private final TableCell<S, ?> cell; 34 private final Callback<S, ReadOnlyProperty<U>> mapper; 35 private final U defaultValue; 36 37 private PropertyExpression(TableCell<S, ?> cell, Callback<S, ReadOnlyProperty<U>> mapper, U defaultValue) { 38 super(); 39 this.cell = Objects.requireNonNull(cell); 40 this.mapper = Objects.requireNonNull(mapper); 41 this.defaultValue = defaultValue; 42 // 初期バインド 43 refreshBinding(); 44 // TableViewのモデルの順番が入れ替わった際にバインドしなおす 45 TableView<S> tableView = cell.getTableView(); 46 if (tableView != null) 47 initForTableView(tableView); 48 cell.tableViewProperty().addListener((observable, oldTable, newTable) -> { 49 assert oldTable == null && newTable != null; 50 initForTableView(newTable); 51 }); 52 53 // cellのindexが変化したらバインドしなおす 54 cell.indexProperty().addListener((observable, oldIndex, newIndex) -> refreshBinding()); 55 } 56 57 private void initForTableView(TableView<S> tableView) { 58 ObservableList<S> items = tableView.getItems(); 59 if (items != null) 60 items.addListener(lcl); 61 // テーブルのitemsプロパティーの値(=ObservableList)が入れ替わったら 62 // リスナーを再設定する 63 tableView.itemsProperty().addListener((observable, oldList, newList) -> { 64 if (oldList != null) 65 oldList.removeListener(lcl); 66 if (newList != null) 67 newList.addListener(lcl); 68 }); 69 } 70 71 private void refreshBinding() { 72 unbind(); 73 TableView<S> tableView = cell.getTableView(); 74 if (tableView == null) 75 return; 76 ObservableList<S> items = tableView.getItems(); 77 if (items == null) 78 return; 79 int index = cell.getIndex(); 80 if (index < 0 || index >= items.size()) { 81 // 表示中のモデルインスタンスなし 82 set(defaultValue); 83 } else { 84 bind(mapper.call(items.get(index))); 85 } 86 } 87 } 88}

投稿2017/10/09 21:34

編集2017/10/10 13:07
KSwordOfHaste

総合スコア18392

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

otenkikaeru

2017/10/10 07:23

ご回答ありがとうございます。 セルごとのバインドすることで、編集可不可の切り替えができること理解できました。
otenkikaeru

2017/10/10 07:26

重ねてご質問で申し訳ありませんが、バインドするときに 同じ行の別の列の情報を使用してバインドすることは可能でしょうか? 1列目の編集可能不可能の切り替えを2列目のチェックボックスの状態によって切り替えるような動き を考えています。
KSwordOfHaste

2017/10/10 08:08

Cell以外のものとバインドするのは(自分が知る限りは)簡潔にはかけなさそうです。例えばモデルに booleanProperty a; StringProperty b; があったとき、aがtrueの場合のみbが編集できるようにするといった感じですよね?
otenkikaeru

2017/10/10 08:14

おっしゃる通りの動きを考えています。 別の列のプロパティをバインドの引数に指定するようなことができればと思ったのですが。
KSwordOfHaste

2017/10/10 09:12

これはバインドで書くかどうかを置いても、実装自体が(自分には)ややこしそうです。ちょっと落ち着いて考えてみます。
otenkikaeru

2017/10/10 09:24

ありがとうございます。 こちらもテーブルビューの処理について、まだあまり理解できておらず ご協力いただけると非常にありがたいです。 こちらでも、引き続き調査していきます。
KSwordOfHaste

2017/10/10 13:12

実装例を追記してみました。あまりに回答が長すぎるせいか、ユーティリティークラスの字下げがうまくきかないようです。見づらくて申し訳ないですが、あしからず。
otenkikaeru

2017/10/10 14:13

丁寧なご回答ありがとうございます。 提示いただいたソースを参考に、自分のプログラムに組み込んでみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問