• category: swing folder: MultipleButtonsInTableCell title: JTableのセルに複数のJButtonを配置する tags: [JTable, TableCellEditor, TableCellRenderer, JButton, JPanel, ActionListener] author: aterai pubdate: 2009-10-05T12:57:02+09:00 description: JTableのセル内にクリック可能な複数のJButtonを配置します。 image: https://lh4.googleusercontent.com/_9Z4BYR88imo/TQTQRygoYeI/AAAAAAAAAfU/-Sr9o7PsQkM/s800/MultipleButtonsInTableCell.png hreflang:
       href: https://java-swing-tips.blogspot.com/2009/10/multiple-jbuttons-in-jtable-cell.html
       lang: en

概要

概要

JTableのセル内にクリック可能な複数のJButtonを配置します。

サンプルコード

サンプルコード

class ButtonsPanel extends JPanel {
  public final List<JButton> buttons =
    Arrays.asList(new JButton("view"), new JButton("edit"));
  public ButtonsPanel() {
    super();
    setOpaque(true);
    for(JButton b: buttons) {
    for (JButton b: buttons) {
      b.setFocusable(false);
      b.setRolloverEnabled(false);
      add(b);
    }
  }
}
#spandel
View in GitHub: Java, Kotlin
#spanend
#spanadd

#spanend
class ButtonsRenderer extends ButtonsPanel
                      implements TableCellRenderer {
  public ButtonsRenderer() {
    super();
    setName("Table.cellRenderer");
  }
  @Override public Component getTableCellRendererComponent(JTable table,
        Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    setBackground(isSelected?table.getSelectionBackground():table.getBackground());
#spanadd

#spanend
  @Override public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected,
      boolean hasFocus, int row, int column) {
    Color bgc = isSelected ? table.getSelectionBackground()
                           : table.getBackground()
    setBackground(bgc);
    return this;
  }
}
#spandel
#spanend
#spanadd

#spanend
class ButtonsEditor extends ButtonsPanel
                    implements TableCellEditor {
  public ButtonsEditor(final JTable table) {
    super();
    //---->
    //DEBUG: view button click -> control key down + edit button(same cell) press
    //       -> remain selection color
    // ---->
    // DEBUG: view button click
    //   -> control key down + edit button(same cell) press
    //   -> remain selection color
    MouseListener ml = new MouseAdapter() {
      @Override public void mousePressed(MouseEvent e) {
        ButtonModel m = ((JButton)e.getSource()).getModel();
        if(m.isPressed() && table.isRowSelected(table.getEditingRow())
                         && e.isControlDown()) {
        ButtonModel m = ((JButton) e.getSource()).getModel();
        if (m.isPressed() && table.isRowSelected(table.getEditingRow())
                          && e.isControlDown()) {
          setBackground(table.getBackground());
        }
      }
    };
    buttons.get(0).addMouseListener(ml);
    buttons.get(1).addMouseListener(ml);
    //<----
    // <----

    buttons.get(0).addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        fireEditingStopped();
        JOptionPane.showMessageDialog(table, "Viewing");
      }
    });

    buttons.get(1).addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        int row = table.convertRowIndexToModel(table.getEditingRow());
        Object o = table.getModel().getValueAt(row, 0);
        fireEditingStopped();
        JOptionPane.showMessageDialog(table, "Editing: "+o);
        JOptionPane.showMessageDialog(table, "Editing: " + o);
      }
    });

    addMouseListener(new MouseAdapter() {
      @Override public void mousePressed(MouseEvent e) {
        fireEditingStopped();
      }
    });
  }
#spanadd

#spanend
  @Override public Component getTableCellEditorComponent(JTable table,
        Object value, boolean isSelected, int row, int column) {
    this.setBackground(table.getSelectionBackground());
    return this;
  }
#spanadd

#spanend
  @Override public Object getCellEditorValue() {
    return "";
  }
  //Copied from AbstractCellEditor
  //protected EventListenerList listenerList = new EventListenerList();
#spanadd

#spanend
  // Copied from AbstractCellEditor
  // protected EventListenerList listenerList = new EventListenerList();
  transient protected ChangeEvent changeEvent = null;
  @Override public boolean isCellEditable(java.util.EventObject e) {
    return true;
  }
#spandel
//......
#spanend
#spanadd
// ...
#spanend

解説

上記のサンプルでは、CellRenderer用とCellEditor用に、JButton2つ配置したJPanelをそれぞれ作成しています。アクションイベントを設定するのは、CellEditor用のJButtonで、CellRenderer用のJButtonは表示のためのダミーです。

解説

上記のサンプルでは、CellRenderer用とCellEditor用にJButton2つ配置したJPanelをそれぞれ作成しています。CellRenderer用のJButtonは表示のみに使用するため、クリック後に実行するActionCellEditor用のJButtonだけに設定しています。
  • LookAndFeelなどが更新されたら、JTable#updateUI()内でSwingUtilities#updateRendererOrEditorUI()を呼び出すなどして、各セルレンダラーやセルエディタ(これらはJTableの子コンポーネントではないので)を更新
    • AbstractCellEditorを継承するセルエディタは、ComponentDefaultCellEditorも継承していないので、LookAndFeelを変更しても追従しない
    • そのため、JTable#updateUI()をオーバーライドして、セルエディタ自体を作成し直すなどの対応が必要
  • このサンプルでは、Componentを継承(TableCellEditorを実装)するセルエディタを作成し、AbstractCellEditorから必要なメソッドをコピーして回避する方法を使用している
  • LookAndFeelなどが更新されたらJTable#updateUI()メソッド内でSwingUtilities#updateRendererOrEditorUI()メソッドを呼び出すなどして各セルレンダラーやセルエディタ(これらはJTableの子コンポーネントではないので)を更新
    • AbstractCellEditorを継承するセルエディタはComponentDefaultCellEditorも継承していないのでLookAndFeelを変更しても追従しない
    • そのためJTable#updateUI()をオーバーライドしてセルエディタ自体を再作成するなどの対応が必要
  • このサンプルでは、Componentを継承(TableCellEditorを実装)するセルエディタを作成し、AbstractCellEditorから必要なメソッドをコピーして回避している
#spandel
//SwingUtilities#updateRendererOrEditorUI()
#spanend
#spanadd
// SwingUtilities#updateRendererOrEditorUI()
#spanend
static void updateRendererOrEditorUI(Object rendererOrEditor) {
  if (rendererOrEditor == null) {
    return;
  }
  Component component = null;
  if (rendererOrEditor instanceof Component) {
    component = (Component)rendererOrEditor;
    component = (Component) rendererOrEditor;
  }
  if (rendererOrEditor instanceof DefaultCellEditor) {
    component = ((DefaultCellEditor)rendererOrEditor).getComponent();
    component = ((DefaultCellEditor) rendererOrEditor).getComponent();
  }
  if (component != null) {
    SwingUtilities.updateComponentTreeUI(component);
  }
}

#spanend
#spandel
import java.awt.*;
#spanend
#spandel
import java.awt.event.*;
#spanend
#spandel
import java.util.*;
#spanend
#spandel
import java.util.List;
#spanend
#spandel
import javax.swing.*;
#spanend
#spandel
import javax.swing.event.*;
#spanend
#spandel
import javax.swing.table.*;
#spanend
#spandel
import javax.swing.text.*;
#spanend
#spandel

#spanend
#spandel
public class ButtonsInsideCellTest {
#spanend
  private JComponent makeUI() {
    String[] columnNames = {"Buttons", "Spinner"};
    Object[][] data = {
      {50, 100}, {100, 50}, {30, 20}, {0, 100}
    };
    DefaultTableModel model = new DefaultTableModel(data, columnNames) {
      @Override public Class<?> getColumnClass(int column) {
        return getValueAt(0, column).getClass();
      }
    };
    JTable table = new JTable(model);
    table.setRowHeight(36);
    table.setAutoCreateRowSorter(true);
#spandel

#spanend
    TableColumn column = table.getColumnModel().getColumn(0);
    column.setCellRenderer(new ButtonsRenderer());
    column.setCellEditor(new ButtonsEditor());
#spandel

#spanend
    column = table.getColumnModel().getColumn(1);
    column.setCellRenderer(new SpinnerRenderer());
    column.setCellEditor(new SpinnerEditor());
#spandel

#spanend
    return new JScrollPane(table);
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });
  }
  public static void createAndShowGUI() {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.getContentPane().add(new ButtonsInsideCellTest().makeUI());
    f.setSize(320, 240);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }
#spandel
}
#spanend
#spandel

#spanend
#spandel
class SpinnerPanel extends JPanel {
#spanend
  public final JSpinner spinner = new JSpinner(new SpinnerNumberModel(100, 0, 200, 1));
  public SpinnerPanel() {
    super(new GridBagLayout());
    GridBagConstraints c = new GridBagConstraints();
#spandel

#spanend
    c.weightx = 1.0;
    c.insets = new Insets(0, 10, 0, 10);
    c.fill = GridBagConstraints.HORIZONTAL;
#spandel

#spanend
    setOpaque(true);
    add(spinner, c);
  }
#spandel
}
#spanend
#spandel
class SpinnerRenderer extends SpinnerPanel implements TableCellRenderer {
#spanend
  public SpinnerRenderer() {
    super();
    setName("Table.cellRenderer");
  }
  @Override public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    setBackground(isSelected?table.getSelectionBackground():table.getBackground());
    spinner.setValue((Integer)value);
    return this;
  }
#spandel
}
#spanend
#spandel

#spanend
#spandel
class SpinnerEditor extends SpinnerPanel implements TableCellEditor {
#spanend
#spandel
//     public SpinnerEditor(final JTable table, final int column) {
#spanend
#spandel
//         super();
#spanend
#spandel
//     }
#spanend
  @Override public Component getTableCellEditorComponent(
      JTable table, Object value, boolean isSelected, int row, int column) {
    this.setBackground(table.getSelectionBackground());
    spinner.setValue((Integer)value);
    return this;
  }
  @Override public Object getCellEditorValue() {
    //System.out.println("getCellEditorValue: " + spinner.getValue());
    //try {
    //  spinner.commitEdit();
    //} catch(Exception pe) {
    //  // Edited value is invalid, spinner.getValue() will return
    //  // the last valid value, you could revert the spinner to show that:
    //  JComponent editor = spinner.getEditor();
    //  if (editor instanceof JSpinner.DefaultEditor) {
    //    ((JSpinner.DefaultEditor)editor).getTextField().setValue(spinner.getValue());
    //  }
    //}
    return spinner.getValue();
  }
#spandel

#spanend
  //Copied from AbstractCellEditor
  //protected EventListenerList listenerList = new EventListenerList();
  transient protected ChangeEvent changeEvent = null;
#spandel

#spanend
  @Override public boolean isCellEditable(EventObject e) {
    return true;
  }
  @Override public boolean shouldSelectCell(EventObject anEvent) {
    return true;
  }
  @Override public boolean stopCellEditing() {
    try {
      spinner.commitEdit();
    } catch(Exception pe) {
      Toolkit.getDefaultToolkit().beep();
      return false;
      // 直前の値に戻して、編集を終了する場合
      // Edited value is invalid, spinner.getValue() will return
      // the last valid value, you could revert the spinner to show that:
      //JComponent editor = spinner.getEditor();
      //if (editor instanceof JSpinner.DefaultEditor) {
      //  ((JSpinner.DefaultEditor)editor).getTextField().setValue(spinner.getValue());
      //}
    }
    fireEditingStopped();
    return true;
  }
  @Override public void  cancelCellEditing() {
    fireEditingCanceled();
  }
  @Override public void addCellEditorListener(CellEditorListener l) {
    listenerList.add(CellEditorListener.class, l);
  }
  @Override public void removeCellEditorListener(CellEditorListener l) {
    listenerList.remove(CellEditorListener.class, l);
  }
  public CellEditorListener[] getCellEditorListeners() {
    return listenerList.getListeners(CellEditorListener.class);
  }
  protected void fireEditingStopped() {
    // Guaranteed to return a non-null array
    Object[] listeners = listenerList.getListenerList();
    // Process the listeners last to first, notifying
    // those that are interested in this event
    for(int i = listeners.length-2; i>=0; i-=2) {
      if(listeners[i]==CellEditorListener.class) {
        // Lazily create the event:
        if(changeEvent == null) changeEvent = new ChangeEvent(this);
        ((CellEditorListener)listeners[i+1]).editingStopped(changeEvent);
      }
    }
  }
  protected void fireEditingCanceled() {
    // Guaranteed to return a non-null array
    Object[] listeners = listenerList.getListenerList();
    // Process the listeners last to first, notifying
    // those that are interested in this event
    for(int i = listeners.length-2; i>=0; i-=2) {
      if(listeners[i]==CellEditorListener.class) {
        // Lazily create the event:
        if(changeEvent == null) changeEvent = new ChangeEvent(this);
        ((CellEditorListener)listeners[i+1]).editingCanceled(changeEvent);
      }
    }
  }
#spandel
}
#spanend
#spandel

#spanend
#spandel
class ButtonsPanel extends JPanel {
#spanend
  public final List<JButton> buttons = Arrays.asList(new JButton("+"), new JButton("-"));
  public final JLabel label = new JLabel() {
    @Override public Dimension getPreferredSize() {
      Dimension d = super.getPreferredSize();
      d.width = 50;
      return d;
    }
  };
  public int i = -1;
  public ButtonsPanel() {
    super();
    label.setHorizontalAlignment(SwingConstants.RIGHT);
    setOpaque(true);
    add(label);
    for(JButton b: buttons) {
      b.setFocusable(false);
      b.setRolloverEnabled(false);
      add(b);
    }
  }
#spandel
}
#spanend
#spandel
class ButtonsRenderer extends ButtonsPanel implements TableCellRenderer {
#spanend
  public ButtonsRenderer() {
    super();
    setName("Table.cellRenderer");
  }
  @Override public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    this.setBackground(isSelected?table.getSelectionBackground():table.getBackground());
    label.setText(value!=null?value.toString():"");
    return this;
  }
#spandel
}
#spanend
#spandel
class ButtonsEditor extends ButtonsPanel implements TableCellEditor {
#spanend
  public ButtonsEditor() {
    super();
    buttons.get(0).addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        i++;
        label.setText(""+i);
        fireEditingStopped();
      }
    });
#spandel

#spanend
    buttons.get(1).addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        i--;
        label.setText(""+i);
        fireEditingStopped();
      }
    });
#spandel

#spanend
    addMouseListener(new MouseAdapter() {
      @Override public void mousePressed(MouseEvent e) {
        fireEditingStopped();
      }
    });
  }
  @Override public Component getTableCellEditorComponent(
      JTable table, Object value, boolean isSelected, int row, int column) {
    this.setBackground(table.getSelectionBackground());
    i = (Integer)value;
    label.setText(""+i);
    return this;
  }
  @Override public Object getCellEditorValue() {
    return i;
  }
#spandel

#spanend
  //Copied from AbstractCellEditor
  //protected EventListenerList listenerList = new EventListenerList();
  transient protected ChangeEvent changeEvent = null;
#spandel

#spanend
  @Override public boolean isCellEditable(EventObject e) {
    return true;
  }
  @Override public boolean shouldSelectCell(EventObject anEvent) {
    return true;
  }
  @Override public boolean stopCellEditing() {
    fireEditingStopped();
    return true;
  }
  @Override public void  cancelCellEditing() {
    fireEditingCanceled();
  }
  @Override public void addCellEditorListener(CellEditorListener l) {
    listenerList.add(CellEditorListener.class, l);
  }
  @Override public void removeCellEditorListener(CellEditorListener l) {
    listenerList.remove(CellEditorListener.class, l);
  }
  public CellEditorListener[] getCellEditorListeners() {
    return listenerList.getListeners(CellEditorListener.class);
  }
  protected void fireEditingStopped() {
    // Guaranteed to return a non-null array
    Object[] listeners = listenerList.getListenerList();
    // Process the listeners last to first, notifying
    // those that are interested in this event
    for(int i = listeners.length-2; i>=0; i-=2) {
      if(listeners[i]==CellEditorListener.class) {
        // Lazily create the event:
        if(changeEvent == null) changeEvent = new ChangeEvent(this);
        ((CellEditorListener)listeners[i+1]).editingStopped(changeEvent);
      }
    }
  }
  protected void fireEditingCanceled() {
    // Guaranteed to return a non-null array
    Object[] listeners = listenerList.getListenerList();
    // Process the listeners last to first, notifying
    // those that are interested in this event
    for(int i = listeners.length-2; i>=0; i-=2) {
      if(listeners[i]==CellEditorListener.class) {
        // Lazily create the event:
        if(changeEvent == null) changeEvent = new ChangeEvent(this);
        ((CellEditorListener)listeners[i+1]).editingCanceled(changeEvent);
      }
    }
  }
#spandel
}
#spanend
#spandel

参考リンク

参考リンク

コメント

コメント