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

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

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

JTableとは二次元的なセルの表を表示し編集するJava Swing用のコンポーネントです。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

解決済

ソースコード上の値ではなくて自分で編集した値を使って空白セルを用いたJTableのグループ化を実現したいんですが

rokkunroru
rokkunroru

総合スコア10

JTable

JTableとは二次元的なセルの表を表示し編集するJava Swing用のコンポーネントです。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

2回答

1グッド

1クリップ

674閲覧

投稿2022/04/08 14:19

編集2022/04/08 14:23

前提

https://ateraimemo.com/Swing/RowGroupInTable.html
このサイトの参考リンクのstackoverflowにある回答欄のソースコードについて

やりたいこと

コピペして何回か実行してみました。すると問題点が出てきました
すでに登録?保存?した値(例addMyData(model, new MyData("Tom", 17, "Book1"));
についてはレンダラーの機能で表現できていると思うのですが
新しいデータを追加してソートしたい場合には
新しい行を追加して空のセルの中に編集した値を入れるという風に考えると思います
しかしうまい方法が思い浮かびません

自分の頭の中

まず新しい行を追加するにはaddRowメソッドで追加するしかないので
model.addRow();
でも空の行を追加したいので
model.addRow(new Object[]);
と書くけどその行に新規のデータを手打ちしてソートしようにもモデルと紐づけられてないのでこれは無理
じゃあ、MyDataクラスのようにコンストラクタとゲッターを作ってそれを利用するか?いや待てよ
自分が編集したらこのコンストラクタは生成されないから意味ないだろ?

諦めきれない

一か月ほど前、似たような質問でベストアンサーの方に
「> 空っぽの行を追加して値を入力して保存する

コード的には逆のほうが分かり易いと思います。
つまり、追加ボタンを押したら ( 例えば ) 入力用のダイアログを出して日付や氏名等を入力し、 OK が押されたらそのデータで TableModel に addRow するということです。」
という回答をしてもらったんですが
その回答は確かにコードを作る側として簡単そうなんですけど
いざプログラムが完成したときに使う側としては一々ダイアログを出して入力する作業がめんどくさそうなんですよ
今ここで躓いています何か打開する方法を教えてください

追記
つまり何が言いたいのかというと
手打ちした値を何かに保存してそれをモデルと関連付けてソートしてさらにグループ化を表現したい(ダイアログの方法以外で
ということです

該当のソースコード

java

1import java.awt.*; 2import java.util.*; 3import java.util.List; 4import javax.swing.*; 5import javax.swing.table.*; 6 7public class RowGroupInTableTest { 8 private JComponent makeUI() { 9 String[] titles = new String[] {"Name", "Last job duration", "Book #"}; 10 DefaultTableModel model = new DefaultTableModel(null, titles) { 11 @Override public Class<?> getColumnClass(int column) { 12 return MyData.class; 13 } 14 }; 15 addMyData(model, new MyData("Tom", 17, "Book1")); 16 addMyData(model, new MyData("Tom", 23, "Book2")); 17 addMyData(model, new MyData("Tom", 25, "Book3")); 18 addMyData(model, new MyData("Polazzo", 41, "Book1")); 19 addMyData(model, new MyData("Polazzo", 45, "Book2")); 20 addMyData(model, new MyData("Polazzo", 12, "Book3")); 21 addMyData(model, new MyData("Anna", 1, "Book3")); 22 addMyData(model, new MyData("Anna", 33, "Book5")); 23 24 JTable table = new JTable(model); 25 table.setFillsViewportHeight(true); 26 table.setDefaultRenderer(MyData.class, new MyRenderer()); 27 28 TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(table.getModel()); 29 Comparator<MyData> c = Comparator.comparing(MyData::getName); 30 sorter.setComparator(0, c); 31 sorter.setComparator(1, c.thenComparing(Comparator.comparingInt(MyData::getDuration))); 32 sorter.setComparator(2, c.thenComparing(Comparator.comparing(MyData::getBook))); 33 table.setRowSorter(sorter); 34 35 return new JScrollPane(table); 36 } 37 private static void addMyData(DefaultTableModel model, MyData data) { 38 //Omission work... 39 model.addRow(Collections.nCopies(3, data).toArray()); 40 } 41 public static void main(String[] args) { 42 EventQueue.invokeLater(() -> { 43 JFrame f = new JFrame(); 44 f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 45 f.getContentPane().add(new RowGroupInTableTest().makeUI()); 46 f.setSize(320, 240); 47 f.setLocationRelativeTo(null); 48 f.setVisible(true); 49 }); 50 } 51} 52 53class MyData { 54 private final String name; 55 private final int duration; 56 private final String book; 57 protected MyData(String name, int duration, String book) { 58 this.name = name; 59 this.duration = duration; 60 this.book = book; 61 } 62 public String getName() { 63 return name; 64 } 65 public int getDuration() { 66 return duration; 67 } 68 public String getBook() { 69 return book; 70 } 71} 72 73class MyRenderer implements TableCellRenderer { 74 TableCellRenderer def = new DefaultTableCellRenderer(); 75 @Override public Component getTableCellRendererComponent( 76 JTable table, Object value, boolean isSelected, boolean hasFocus, 77 int row, int column) { 78 JLabel orig = (JLabel) def.getTableCellRendererComponent( 79 table, value, isSelected, hasFocus, row, column); 80 orig.setHorizontalAlignment(SwingConstants.LEFT); 81 MyData data = (MyData) value; 82 switch (table.convertColumnIndexToModel(column)) { 83 case 0: 84 String str = data.getName(); 85 if (row > 0) { 86 //if (table.getModel().getValueAt(row-1, column).equals(value)) { 87 //Since it compares with the value of the previous line on the display, 88 //table.getModel() is not needed 89 MyData prev = (MyData) table.getValueAt(row - 1, column); 90 if (Objects.equals(prev.getName(), str)) { 91 str = " "; 92 } 93 } 94 orig.setText(str); 95 break; 96 case 1: 97 orig.setHorizontalAlignment(SwingConstants.RIGHT); 98 orig.setText("" + data.getDuration()); 99 break; 100 case 2: 101 orig.setText(data.getBook()); 102 break; 103 default: 104 break; 105 } 106 return orig; 107 } 108}

補足情報(FW/ツールのバージョンなど)

All in One パッケージ Eclipse 2021

TN8001👍を押しています

以下のような質問にはグッドを送りましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

グッドが多くついた質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

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

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

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

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

TN8001

2022/04/17 10:09

回答で解決したなら「解決済」にしてください。 [ヘルプ | 質問を解決済みにしたい](https://teratail.com/help#resolve-question) 解決していないのであれば、問題点・わからない点等をコメントするか質問を編集してください。

回答2

2

ベストアンサー

諦めきれない

っておっしゃってるので、いろいろ試しては頭にきてやめてを繰り返していました^^;
これめちゃくちゃ悩ましいですね...(実際やってみないと難しさがわからないですね)

JTableで同一内容のセルを空表示にしてグループ化を表現する - Java Swing Tips
まず参考リンクでなぜ変則的なモデルになっているかを、把握しておく必要があります。

Java

1// 普通はこう持つ 2Object[][] data = { { "Tom", 17, "Book1", }, { "Tom", 23, "Book2", }, }; 3 4// しかしこうなっている 5var d1 = new MyData("Tom", 17, "Book1"); 6var d2 = new MyData("Tom", 23, "Book2"); 7Object[][] data = { { d1, d1, d1, }, { d2, d2, d2, }, };

なぜこんなことになっているかというと、普通の持ち方だとTableRowSorterに文字列等しか渡ってこず、ほかのカラムの情報が取れないからです。
そのためすべてのセルにMyDataを入れることによって、TableRowSorterで複数カラムによるソートを実現しています。

この状態で編集するにはCellRendererと、対になるCellEditorを作る必要があります。
やってやれないことはないですが、実現したいことに比べあまりに面倒です。

発想を変えてまともなモデルを作ったうえで、ソートのほうをどうにかすることにしました^^
空表示にするには常に先頭のカラムが、(プライマリで)ソートされていなければなりません。
これはSortKeysの0番目は、常にColumnが0ということになります。
DefaultRowSorter#setSortKeys (Java Platform SE 8 )
そのためどのカラムをクリックしても左端にしか▲がでませんが、意味からすると全く正しいです。

新しい行を追加して空のセルの中に編集した値を入れるという風に考えると思います

これC#メインの私からすると当たり前なんですけど(DataGridViewとか標準でそういう作り)、Javaは(Swing・JavaFXどちらも)そういう思想ではないようですね...

手打ちした値を何かに保存してそれをモデルと関連付けてソートしてさらにグループ化を表現したい(ダイアログの方法以外で

13 表ビュー(リリース8)
これみたいに同じ画面内に、テキストフィールドがあるのでもダメですか?

空行を追加するボタンを置くなら、テキストフィールドがあっても大差ない気がします。
かといって「入力を感知して常に空行を出し続ける」のは、面倒そうなのは明らかなのでやる気はないです^^;

Java

1import javax.swing.*; 2import javax.swing.table.*; 3import java.awt.*; 4import java.util.*; 5import java.util.List; 6 7 8public class RowGroupInTableTest extends JFrame { 9 public static void main(String[] args) { 10 new RowGroupInTableTest().setVisible(true); 11 } 12 13 private final MyDataModel model = new MyDataModel(new String[]{ "Name", "Last job duration", "Book #" }); 14 15 RowGroupInTableTest() { 16 setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 17 setSize(320, 240); 18 setLocationRelativeTo(null); 19 add(makeTable()); 20 add(makeInput(), BorderLayout.SOUTH); 21 } 22 23 private JComponent makeTable() { 24 var d = new MyData[]{ 25 new MyData("Tom", 17, "Book1"), 26 new MyData("Tom", 3, "Book2"), 27 new MyData("Tom", 25, "Book3"), 28 new MyData("Polazzo", 41, "Book1"), 29 new MyData("Polazzo", 45, "Book2"), 30 new MyData("Polazzo", 12, "Book3"), 31 new MyData("Anna", 1, "Book3"), 32 new MyData("Anna", 33, "Book5"), 33 }; 34 model.addMyData(d); 35 36 var table = new JTable(model); 37 38 var renderer = new DefaultTableCellRenderer() { 39 @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 40 if (0 < row && Objects.equals(value, table.getValueAt(row - 1, column))) { 41 value = ""; 42 } 43 return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 44 } 45 }; 46 table.getColumnModel().getColumn(0).setCellRenderer(renderer); 47 48 var sorter = new TableRowSorter<>(table.getModel()) { 49 // いまいちだがうまく制御が利かなかったため自前管理 50 private final SortKey[] keys = new SortKey[]{ 51 new SortKey(0, SortOrder.ASCENDING), 52 new SortKey(1, SortOrder.ASCENDING), 53 new SortKey(2, SortOrder.ASCENDING), 54 }; 55 56 { 57 // なくてもいいのだがデフォはSortOrder.UNSORTEDなのでわかりにくいかなと 58 super.setSortKeys(Arrays.asList(keys)); 59 setSortsOnUpdates(true); 60 } 61 62 @Override public void setSortKeys(List<? extends SortKey> sortKeys) { 63 // オリジナルとソートが同じではないがこっちのほうが好み 64 var c = sortKeys.get(0).getColumn(); 65 var o = keys[c].getSortOrder() == SortOrder.ASCENDING ? SortOrder.DESCENDING : SortOrder.ASCENDING; 66 keys[c] = new SortKey(keys[c].getColumn(), o); 67 68 var newKeys = new ArrayList<SortKey>(); 69 newKeys.add(keys[0]); 70 newKeys.add(keys[c]); 71 super.setSortKeys(newKeys); 72 } 73 }; 74 table.setRowSorter(sorter); 75 76 return new JScrollPane(table); 77 } 78 79 private JComponent makeInput() { 80 var inner = new JPanel(new GridLayout(1, 3)); 81 var nameTextField = new JTextField(); 82 inner.add(nameTextField); 83 var durationTextField = new JTextField(); 84 durationTextField.setHorizontalAlignment(JTextField.RIGHT); 85 durationTextField.setInputVerifier(new InputVerifier() { 86 @Override public boolean verify(JComponent input) { 87 try { 88 Integer.parseInt(((JTextField) input).getText()); 89 input.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); 90 return true; 91 } catch (NumberFormatException e) { 92 input.setBorder(BorderFactory.createLineBorder(Color.RED)); 93 return false; 94 } 95 } 96 }); 97 inner.add(durationTextField); 98 var bookTextField = new JTextField(); 99 inner.add(bookTextField); 100 101 var outer = new JPanel(new BorderLayout()); 102 outer.add(inner, BorderLayout.CENTER); 103 var button = new JButton("add"); 104 button.addActionListener(x -> { 105 var name = nameTextField.getText(); 106 var duration = Integer.parseInt(durationTextField.getText()); 107 var book = bookTextField.getText(); 108 model.addMyData(new MyData(name, duration, book)); 109 }); 110 getRootPane().setDefaultButton(button); 111 outer.add(button, BorderLayout.EAST); 112 113 return outer; 114 } 115 116 // 別にclassでいいんですけどなんとなくw 117 record MyData(String name, int duration, String book) { } 118 119 static class MyDataModel extends AbstractTableModel { 120 private final List<MyData> data = new ArrayList<>(); 121 private final String[] columns; 122 123 public MyDataModel(String[] titles) { columns = titles; } 124 125 public void addMyData(MyData myData) { 126 var rowIndex = data.size(); 127 data.add(myData); 128 fireTableRowsInserted(rowIndex, rowIndex); 129 fireTableRowsUpdated(0, data.size() - 1); // live sorting 130 } 131 132 public void addMyData(MyData[] myDataArray) { 133 var firstRow = data.size(); 134 Collections.addAll(data, myDataArray); 135 var lastRow = data.size() - 1; 136 fireTableRowsInserted(firstRow, lastRow); 137 } 138 139 @Override public int getRowCount() { return data.size(); } 140 141 @Override public int getColumnCount() { return columns.length; } 142 143 @Override public String getColumnName(int column) { return columns[column]; } 144 145 @Override public Class<?> getColumnClass(int columnIndex) { 146 // Switch式いいですね^^ 147 return switch (columnIndex) { 148 case 0, 2 -> String.class; 149 case 1 -> Integer.class; 150 default -> throw new IndexOutOfBoundsException(columnIndex); 151 }; 152 } 153 154 @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return true; } 155 156 @Override public Object getValueAt(int rowIndex, int columnIndex) { 157 var d = data.get(rowIndex); 158 return switch (columnIndex) { 159 case 0 -> d.name(); 160 case 1 -> d.duration(); 161 case 2 -> d.book(); 162 default -> throw new IndexOutOfBoundsException(columnIndex); 163 }; 164 } 165 166 @Override public void setValueAt(Object val, int rowIndex, int columnIndex) { 167 var d = data.get(rowIndex); 168 switch (columnIndex) { 169 case 0 -> data.set(rowIndex, new MyData((String) val, d.duration(), d.book())); 170 case 1 -> data.set(rowIndex, new MyData(d.name(), (Integer) val, d.book())); 171 case 2 -> data.set(rowIndex, new MyData(d.name(), d.duration(), (String) val)); 172 default -> throw new IndexOutOfBoundsException(columnIndex); 173 } 174 fireTableCellUpdated(rowIndex, columnIndex); 175 } 176 177 public MyData getMyData(int rowIndex) { return data.get(rowIndex); } 178 179 public List<MyData> getMyDataList() { return Collections.unmodifiableList(data); } 180 } 181}

ソート法がオリジナル(どのカラムをクリックしても左端も変わる)と違いますが、私はこっち(2個目・3個目のクリックでは左端は動かない)が好みです^^

アプリ画像

投稿2022/04/12 15:21

TN8001

総合スコア8037

jimbe, rokkunroru👍を押しています

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

jimbe

2022/04/12 17:05

私も RowSorter を弄っていたのですが、落とし所がなかなか決まらず。 後は JTable 自体を弄るくらいでしょうか。
rokkunroru

2022/04/13 08:11

自分の理想はこれに近いかもです。お二人ともありがとうございます

2

仰る通り、追加修正等の度にダイアログが出てくるのは結構面倒です。ですので EXCEL シートのように直接書き込めた方が「使う側にとっては簡単」です。
そしてダイアログを出すほうが「コードを作る側として簡単そう」というのもその通りです。
そこから逆に言いますと、使う側にとっては簡単になる「直接書き込み」はコードを作る側にとっては(コード的にも、データの整合性的にも)「簡単では無く」なります。

例えば JTable のセルの値を更新するテーブルモデルの実装方法 のような記事をお読みになってみては如何でしょうか。


テーブルモデルを AbstractTableModel から作り、編集可能としてみました。(編集可能状態になるだけで、実際には編集・追加の処理が入っていませんので、編集・追加は出来ません。)
"Name" の列は OmittableString.class とすることでレンダラを使うようにし、編集可能とする為にエディタを設定しています。(name の実体は String なので特別なことはしていません。)
グループ化・ソートをオリジナル(?)に合わせる為に、 Duration・Book クラスを作って name を参照できるようにしました。
その為、全列にレンダラ・エディタが必要になりました・・・。

大きくなってきたので Row クラスを別ファイルとしました。
Insert キー / Delete キーで 行の追加・削除をしてみました。

RowGroupInTableTest2.java

java

1public class RowGroupInTableTest2 extends JFrame { 2 public static void main(String[] args) { 3 EventQueue.invokeLater(() -> new RowGroupInTableTest2().setVisible(true)); 4 } 5 6 RowGroupInTableTest2() { 7 super("RowGroupInTableTest2"); 8 setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 9 setSize(320, 240); 10 setLocationRelativeTo(null); 11 12 MyTableModel model = new MyTableModel(); 13 model.add(new MyData("Tom", 17, "Book1")); 14 model.add(new MyData("Tom", 23, "Book2")); 15 model.add(new MyData("Tom", 25, "Book3")); 16 model.add(new MyData("Polazzo", 41, "Book1")); 17 model.add(new MyData("Polazzo", 45, "Book2")); 18 model.add(new MyData("Polazzo", 12, "Book3")); 19 model.add(new MyData("Anna", 1, "Book3")); 20 model.add(new MyData("Anna", 33, "Book5")); 21 22 JTable table = new JTable(model); 23 table.setFillsViewportHeight(true); 24 table.setDefaultRenderer(Row.Name.class, new Row.NameRenderer()); 25 table.setDefaultEditor(Row.Name.class, new DefaultCellEditor(new JTextField())); 26 table.setDefaultRenderer(Row.Duration.class, new Row.DurationRenderer()); 27 table.setDefaultEditor(Row.Duration.class, new Row.DurationEditor()); 28 table.setDefaultRenderer(Row.Book.class, new Row.BookRenderer()); 29 table.setDefaultEditor(Row.Book.class, new Row.BookEditor()); 30 31 table.setRowSorter(new TableRowSorter<TableModel>(model)); 32 33 setAction(table, KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0), new AddAction()); 34 setAction(table, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), new DeleteAction()); 35 36 getContentPane().add(new JScrollPane(table)); 37 } 38 39 private static void setAction(JTable table, KeyStroke key, Action a) { 40 table.getActionMap().put(a, a); 41 42 InputMap im; 43 im = table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 44 im.put(key, a); 45 im = table.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); 46 im.put(key, a); 47 } 48 49 static class AddAction extends AbstractAction { 50 public AddAction() { 51 super("table-row-add"); 52 } 53 54 @Override 55 public void actionPerformed(ActionEvent e) { 56 JTable table = (JTable)e.getSource(); 57 MyTableModel model = (MyTableModel)table.getModel(); 58 model.insert(table.getRowCount(), new MyData("", 0, "")); 59 } 60 } 61 62 public class DeleteAction extends AbstractAction { 63 public DeleteAction() { 64 super("table-row-del"); 65 } 66 67 @Override 68 public void actionPerformed(ActionEvent e) { 69 JTable table = (JTable)e.getSource(); 70 int[] rows = table.getSelectedRows(); 71 for(int i=0; i<rows.length; i++) { 72 rows[i] = table.convertRowIndexToModel(rows[i]); 73 } 74 75 MyTableModel model = (MyTableModel)table.getModel(); 76 for(int i=rows.length-1; i>=0; i--) { 77 model.remove(rows[i]); 78 } 79 } 80 } 81} 82 83class MyData { 84 final String name; 85 final int duration; 86 final String book; 87 88 MyData(String name, int duration, String book) { 89 this.name = name; 90 this.duration = duration; 91 this.book = book; 92 } 93} 94 95class MyTableModel extends AbstractTableModel { 96 abstract private class Column { 97 final String title; 98 Column(String title) { 99 this.title = title; 100 } 101 abstract Object getValueAt(Row row); 102 abstract Class<?> getColumnClass(); 103 abstract Row setRowAt(Row row, Object aValue); 104 } 105 private Column[] columns = { 106 new Column("Name") { 107 Object getValueAt(Row row) { return row.name; } 108 Class<?> getColumnClass() { return Row.Name.class; } 109 Row setRowAt(Row row, Object aValue) { return row.setName((String)aValue); } 110 }, 111 new Column("Last job duration") { 112 Object getValueAt(Row row) { return row.duration; } 113 Class<?> getColumnClass() { return Row.Duration.class; } 114 Row setRowAt(Row row, Object aValue) { return row.setDuration((Integer)aValue); } 115 }, 116 new Column("Book #") { 117 Object getValueAt(Row row) { return row.book; } 118 Class<?> getColumnClass() { return Row.Book.class; } 119 Row setRowAt(Row row, Object aValue) { return row.setBook((String)aValue); } 120 } 121 }; 122 123 private List<Row> rowList = new ArrayList<>(); 124 125 void add(MyData data) { 126 insert(rowList.size(), data); 127 } 128 129 void insert(int rowIndex, MyData data) { 130 rowList.add(rowIndex, new Row(data.name, data.duration, data.book)); 131 fireTableRowsInserted(rowIndex, rowIndex); 132 } 133 134 void remove(int rowIndex) { 135 rowList.remove(rowIndex); 136 fireTableRowsDeleted(rowIndex, rowIndex); 137 } 138 139 List<MyData> getList() { 140 List<MyData> list = new ArrayList<>(); 141 for(Row row : rowList) list.add(row.getMyData()); 142 return list; 143 } 144 145 @Override 146 public String getColumnName(int column) { 147 return columns[column].title; 148 } 149 150 @Override 151 public int getColumnCount() { 152 return columns.length; 153 } 154 155 @Override 156 public int getRowCount() { 157 return rowList.size(); 158 } 159 160 @Override 161 public Object getValueAt(int rowIndex, int columnIndex) { 162 return columns[columnIndex].getValueAt(rowList.get(rowIndex)); 163 } 164 165 @Override 166 public Class<?> getColumnClass(int columnIndex) { 167 return columns[columnIndex].getColumnClass(); 168 } 169 170 @Override 171 public boolean isCellEditable(int rowIndex, int columnIndex) { 172 return true; 173 } 174 175 @Override 176 public void setValueAt(Object aValue, int rowIndex, int columnIndex) { 177 rowList.set(rowIndex, columns[columnIndex].setRowAt(rowList.get(rowIndex), aValue)); 178 } 179}

Row.java

java

1class Row { 2 final String name; 3 final Duration duration; 4 final Book book; 5 6 Row(String name, int duration, String book) { 7 this.name = name; 8 this.duration = new Duration(duration); 9 this.book = new Book(book); 10 } 11 12 Row setName(String name) { 13 return new Row(name, duration.value, book.value); 14 } 15 Row setDuration(int duration) { 16 return new Row(name, duration, book.value); 17 } 18 Row setBook(String book) { 19 return new Row(name, duration.value, book); 20 } 21 MyData getMyData() { 22 return new MyData(name, duration.value, book.value); 23 } 24 25 class Name { 26 } 27 static class NameRenderer extends DefaultTableCellRenderer { 28 @Override 29 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 30 //直前の行と同じ内容なら省略する 31 value = row>0 && Objects.equals(table.getValueAt(row-1, column), value) ? "" : value; 32 33 return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 34 } 35 } 36 37 class Duration implements Comparable<Duration> { 38 final Integer value; 39 private Duration(int value) { 40 this.value = value; 41 } 42 private String getName() { return Row.this.name; } 43 @Override 44 public int compareTo(Duration o) { 45 int result = getName().compareTo(o.getName()); 46 return result == 0 ? value.compareTo(o.value) : result; 47 } 48 } 49 50 static class DurationRenderer extends DefaultTableCellRenderer { 51 DurationRenderer() { 52 super(); 53 setHorizontalAlignment(SwingConstants.RIGHT); 54 } 55 @Override 56 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 57 return super.getTableCellRendererComponent(table, ((Duration)value).value, isSelected, hasFocus, row, column); 58 } 59 } 60 static class DurationEditor extends AbstractCellEditor implements TableCellEditor { 61 private JTextField component = new JTextField(); 62 DurationEditor() { 63 component.setHorizontalAlignment(JTextField.RIGHT); 64 } 65 @Override 66 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { 67 Duration duration = (Duration)value; 68 component.setText(""+duration.value); 69 return component; 70 } 71 @Override 72 public Object getCellEditorValue() { 73 try { 74 return Integer.parseInt(component.getText()); 75 } catch(NumberFormatException e) { 76 return 0; 77 } 78 } 79 } 80 81 class Book implements Comparable<Book> { 82 final String value; 83 private Book(String value) { 84 this.value = value; 85 } 86 private String getName() { return Row.this.name; } 87 @Override 88 public int compareTo(Book o) { 89 int result = getName().compareTo(o.getName()); 90 return result == 0 ? value.compareTo(o.value) : result; 91 } 92 } 93 94 static class BookRenderer extends DefaultTableCellRenderer { 95 @Override 96 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 97 return super.getTableCellRendererComponent(table, ((Book)value).value, isSelected, hasFocus, row, column); 98 } 99 } 100 101 static class BookEditor extends AbstractCellEditor implements TableCellEditor { 102 private JTextField component = new JTextField(); 103 @Override 104 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { 105 Book book = (Book)value; 106 component.setText(book.value); 107 return component; 108 } 109 @Override 110 public Object getCellEditorValue() { 111 return component.getText(); 112 } 113 } 114}

投稿2022/04/08 14:37

編集2022/04/13 08:38
jimbe

総合スコア10741

rokkunroru, TN8001👍を押しています

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

rokkunroru

2022/04/11 13:04 編集

すみません、編集可能か不可能かの件で質問したいのですが https://java.keicode.com/lib/swing-jtable-2.php の下の 「見栄えは前回と同様です。しかし、前回と違うのは今度は全てのセルが編集不可になっていることです。 これはなぜかというと、あるセルを編集可とするか編集不可とすべきかもテーブルモデルクラスで決めることです。しかし、 今回は明示的に編集可としていないので、編集不可となります。 前回 JTable のコンストラクタにデータとカラム名を渡したときは、実は内部的に自動的にデフォルトのテーブルモデルオブジェクトが作成されます。 (その名もズバリ DefaultTableModel というクラスのオブジェクトです。)その DefaultTableModel では全てのセルを編集可としているので、JTable は全てのセルを編集可としているということです。」 という文があるのですが ならなぜ該当のソースコードはDefaultTableModel を使っているにもかかわらず編集不可能なのでしょうか
jimbe

2022/04/11 14:29 編集

「該当のソースコード」と仰っているのは TableTest13 のことでしょうか。 TableTest13 のモデル MyTableModel1 は TableTest13 の上で紹介されているコードで、 DefaultTableModel では無く AbstractTableModel です。 AbstractTableModel の isCellEditable メソッドは常に false を返します。 AbstractTableModel.isCellEditable https://docs.oracle.com/javase/jp/8/docs/api/javax/swing/table/AbstractTableModel.html#isCellEditable-int-int- DefaultTableModel.isCellEditable https://docs.oracle.com/javase/jp/8/docs/api/javax/swing/table/DefaultTableModel.html#isCellEditable-int-int-
rokkunroru

2022/04/12 01:33

そちらではなくて addMyData(model, new MyData("Tom", 17, "Book1")); addMyData(model, new MyData("Tom", 23, "Book2")); がある方のソースコードです分かりづらくて申し訳ないです
jimbe

2022/04/12 03:45 編集

失礼しました、ご質問のコードのほうですね。 それは恐らく getColumnClass で MyData.class を返しているからだと思います。 JTable セルの編集には CellEditor が使われ、それは基本的なクラス毎 (String や Integer 、Boolean 等) には予め用意されており、 getColumnClass の返すクラスによって選択・使用されます。 が、 MyData 用のエディタは当然ありませんので、編集出来ません。 CellRenderer のように CellEditor も作成・設定する必要があると思います。
jimbe

2022/04/13 08:40

コード変更したら 10000万文字をぎりぎり超えたようで、import 省略しました。
TN8001

2022/04/13 15:26

> Insert キー / Delete キーで 行の追加・削除をしてみました。 なるほどその手がありましたね。 マウス人間なんで全く考えもしませんでした^^; > コード変更したら 10000万文字をぎりぎり超えたようで、import 省略しました。 1万字ですねw 調子に乗ってコードを書いていると、意外とあっさり行ってしまいますよね。 私は今回はJTableへのヘイトがたまって、そういう状態にはなっていませんが^^;
jimbe

2022/04/13 19:34

Insert/Delete は本来(?)ならメニューのショートカットとしてですが、メニューの実装は提示出来なさそうです。 10000万…それだけあれば…ご指摘有難う御座います orz コード400行位でいっぱいですから、冗長気味の java では少し大きくなるだけでも厳しいです。 Swing も、更新があった当時でもどこか中途半端で、結局そのままとなってしまいましたね。

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

ただいまの回答率
86.12%

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

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

質問する

関連した質問

同じタグがついた質問を見る

JTable

JTableとは二次元的なセルの表を表示し編集するJava Swing用のコンポーネントです。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。