JTableのセルを横方向に連結する
Total: 12460
, Today: 11
, Yesterday: 7
Posted by aterai at
Last-modified:
概要
JTable
のセルを横方向に連結するセルレンダラーを作成します。
Screenshot
Advertisement
サンプルコード
class ColumnSpanningCellRenderer extends JPanel implements TableCellRenderer {
private static final int TARGET_COLIDX = 0;
private final JTextArea textArea = new JTextArea(2, 999999);
private final JLabel label = new JLabel();
private final JLabel iconLabel = new JLabel();
private final JScrollPane scroll = new JScrollPane(textArea);
protected ColumnSpanningCellRenderer() {
super(new BorderLayout());
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setBorder(BorderFactory.createEmptyBorder());
scroll.setViewportBorder(BorderFactory.createEmptyBorder());
scroll.setOpaque(false);
scroll.getViewport().setOpaque(false);
textArea.setBorder(BorderFactory.createEmptyBorder());
textArea.setMargin(new Insets(0, 0, 0, 0));
textArea.setForeground(Color.RED);
textArea.setEditable(false);
textArea.setFocusable(false);
textArea.setOpaque(false);
iconLabel.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4));
iconLabel.setOpaque(false);
Border b1 = BorderFactory.createEmptyBorder(2, 2, 2, 2);
Border b2 = BorderFactory.createMatteBorder(0, 0, 1, 1, Color.GRAY);
label.setBorder(BorderFactory.createCompoundBorder(b2, b1));
setBackground(textArea.getBackground());
setOpaque(true);
add(label, BorderLayout.NORTH);
add(scroll);
}
@Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
OptionPaneDescription d;
if (value instanceof OptionPaneDescription) {
d = (OptionPaneDescription) value;
add(iconLabel, BorderLayout.WEST);
} else {
String title = Objects.toString(value, "");
int mrow = table.convertRowIndexToModel(row);
Object o = table.getModel().getValueAt(mrow, 0);
if (o instanceof OptionPaneDescription) {
OptionPaneDescription t = (OptionPaneDescription) o;
d = new OptionPaneDescription(title, t.icon, t.text);
} else {
d = new OptionPaneDescription(title, null, "");
}
remove(iconLabel);
}
label.setText(d.title);
textArea.setText(d.text);
iconLabel.setIcon(d.icon);
Rectangle cr = table.getCellRect(row, column, false);
if (column != TARGET_COLIDX) {
cr.x -= iconLabel.getPreferredSize().width;
}
scroll.getViewport().setViewPosition(cr.getLocation());
if (isSelected) {
setBackground(Color.ORANGE);
} else {
setBackground(Color.WHITE);
}
return this;
}
}
class OptionPaneDescription {
public final String title;
public final Icon icon;
public final String text;
protected OptionPaneDescription(String title, Icon icon, String text) {
this.title = title;
this.icon = icon;
this.text = text;
}
}
View in GitHub: Java, Kotlin解説
文字列を配置したJTextArea
を各カラムごとにJViewport
で表示する領域を切り取ってセルに貼り付けています。さらにJTable
のセルの縦罫線自体をtable.setShowVerticalLines(false)
などで非表示にしてレンダラー内のJTextArea
は連続しているように見せかけ、代わりに上部のJLabel
はBorder
を設定することで区切りを表示しています。
- 列の入れ替えには対応していない
0
行目だけカラムヘッダのサイズを変更すると描画がおかしくなる?0
行目ではなく一番上に表示されている行の表示が乱れているJTextArea#scrollRectToVisible(...)
ではなくJViewport#setViewPosition(Point)
を使用すると正常にリサイズ可能0
行目だけ高さ1
の仮行を追加して回避(ソートなどで問題が残る)
JTable
のクリック(セル選択?)などで表示が乱れる場合があるJTable#repaint(Rectangle)
をオーバーライドして常に全体を描画することで回避
JScrollPane
内にJTextArea
を配置せずに直接JTextArea
から表示領域を切り取っても良さそう?