• category: swing folder: RowGroupInTable title: JTableで同一内容のセルを空表示にしてグループ化を表現する tags: [JTable, TableRowSorter, TableCellRenderer] author: aterai pubdate: 2017-06-05T16:09:45+09:00 description: JTableで直上のセルと同一内容のセルを空表示にして、行のグループ化を表現します。 image: https://drive.google.com/uc?id=1BtPc00VYQd5w5UwghwQc6KYrIoZCZ0W_EQ

概要

JTableで直上のセルと同一内容のセルを空表示にして、行のグループ化を表現します。

サンプルコード

String colors = "colors";
String sports = "sports";
String food   = "food";
addRowData(model, new RowData(colors, "blue",     1));
addRowData(model, new RowData(colors, "violet",   2));
addRowData(model, new RowData(colors, "red",      3));
addRowData(model, new RowData(colors, "yellow",   4));
addRowData(model, new RowData(sports, "baseball", 23));
addRowData(model, new RowData(sports, "soccer",   22));
addRowData(model, new RowData(sports, "football", 21));
addRowData(model, new RowData(sports, "hockey",   20));
addRowData(model, new RowData(food,   "hot dogs", 10));
addRowData(model, new RowData(food,   "pizza",    11));
addRowData(model, new RowData(food,   "ravioli",  12));
addRowData(model, new RowData(food,   "bananas",  13));
//...
class RowDataRenderer implements TableCellRenderer {
  private final TableCellRenderer renderer = new DefaultTableCellRenderer();
  @Override public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected, boolean hasFocus,
      int row, int column) {
    JLabel label = (JLabel) renderer.getTableCellRendererComponent(
        table, value, isSelected, hasFocus, row, column);
    label.setHorizontalAlignment(SwingConstants.LEFT);
    RowData data = (RowData) value;
    switch (table.convertColumnIndexToModel(column)) {
    case 0:
      String str = data.getGroup();
      if (row > 0) {
        RowData prev = (RowData) table.getValueAt(row - 1, column);
        if (Objects.equals(prev.getGroup(), str)) {
          label.setText(" ");
          break;
        }
      }
      label.setText("+ " + str);
      break;
    case 1:
      label.setText(data.getName());
      break;
    case 2:
      label.setHorizontalAlignment(SwingConstants.RIGHT);
      label.setText(Integer.toString(data.getCount()));
      break;
    default:
      break;
    }
    return label;
  }
}
View in GitHub: Java, Kotlin

解説

  • TableModel
    • データとしてすべての列にRowDataのインスタンスのコピーを設定
    • セルレンダラーで、第0列目にRowData.getGroup()の値、第1列目にRowData.getName()の値、第2列目にRowData.getCount()の値を表示するよう設定
  • RowDataRenderer
    • JTableの第0列目で、表示上の直上のセルとRowData.getGroup()で取得した値が同一の場合、その値ではなく空白文字を表示するよう設定
      • 表示上の0行目には直上セルは存在しないので、常にそのままRowData.getGroup()の値を表示する
  • TableRowSorter
    • ソートでグループ化が変化しないように、グループ化しない第1列目と第2列目は、まず第0列目をソートしてからその列の比較を行う以下のようなComparatorを使用する
      TableRowSorter<TableModel> sorter = new TableRowSorter<>(table.getModel());
      Comparator<RowData> c = Comparator.comparing(RowData::getGroup);
      sorter.setComparator(0, c);
      sorter.setComparator(1, c.thenComparing(RowData::getName));
      sorter.setComparator(2, c.thenComparing(RowData::getCount));
      table.setRowSorter(sorter);
      

参考リンク

コメント