• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:CellEditorをJSpinnerにして日付を変更
#navi(../)
RIGHT:Posted by [[terai]] at 2008-09-22
*CellEditorをJSpinnerにして日付を変更 [#e0a555cb]
JTableで、日付を表示する列のセルエディタをJSpinnerにします。
---
category: swing
folder: DateCellEditor
title: CellEditorをJSpinnerにして日付を変更
tags: [JTable, TableCellEditor, JSpinner, Focus, ChangeListener]
author: aterai
pubdate: 2008-09-22T13:48:58+09:00
description: JTableで、日付を表示する列のセルエディタをJSpinnerにします。
image: https://lh5.googleusercontent.com/_9Z4BYR88imo/TQTKdTsjXPI/AAAAAAAAAWA/vtjdEFUkZA4/s800/DateCellEditor.png
---
* 概要 [#summary]
`JTable`で、日付を表示する列のセルエディタを`JSpinner`にします。

-&jnlp;
-&jar;
-&zip;
#download(https://lh5.googleusercontent.com/_9Z4BYR88imo/TQTKdTsjXPI/AAAAAAAAAWA/vtjdEFUkZA4/s800/DateCellEditor.png)

#screenshot

**サンプルコード [#t0d2e8ee]
#code{{
class SpinnerCellEditor extends AbstractCellEditor implements TableCellEditor {
  private final JSpinner spinner = new JSpinner(new SpinnerDateModel());
* サンプルコード [#sourcecode]
#code(link){{
class SpinnerCellEditor extends JSpinner implements TableCellEditor {
  private final JSpinner.DateEditor editor;
  public SpinnerCellEditor() {
    editor = new JSpinner.DateEditor(spinner, "yyyy/MM/dd");
    spinner.setEditor(editor);
    super(new SpinnerDateModel());
    setEditor(editor = new JSpinner.DateEditor(this, "yyyy/MM/dd"));
    setArrowButtonEnabled(false);
    spinner.addFocusListener(new FocusAdapter() {
      public void focusGained(FocusEvent e) {
    editor.getTextField().setHorizontalAlignment(JFormattedTextField.LEFT);

    addFocusListener(new FocusAdapter() {
      @Override public void focusGained(FocusEvent e) {
        //System.out.println("spinner");
        editor.getTextField().requestFocusInWindow();
      }
    });
    editor.getTextField().addFocusListener(new FocusAdapter() {
      //@Override
      public void focusLost(FocusEvent e) {
      @Override public void focusLost(FocusEvent e) {
        setArrowButtonEnabled(false);
      }
      //@Override
      public void focusGained(FocusEvent e) {
      @Override public void focusGained(FocusEvent e) {
        //System.out.println("getTextField");
        setArrowButtonEnabled(true);
        EventQueue.invokeLater(new Runnable() {
          public void run() {
          @Override public void run() {
            editor.getTextField().setCaretPosition(8);
            editor.getTextField().setSelectionStart(8);
            editor.getTextField().setSelectionEnd(10);
          }
        });
      }
    });
    spinner.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
    setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
  }
  private void setArrowButtonEnabled(boolean flag) {
    for(Component c: spinner.getComponents()) {
      if(c instanceof JButton) {
        ((JButton)c).setEnabled(flag);
    for (Component c : getComponents()) {
      if (c instanceof JButton) {
        ((JButton) c).setEnabled(flag);
      }
    }
  }
  public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
    spinner.setValue(value);
    editor.getTextField().setHorizontalAlignment(JFormattedTextField.LEFT);
    //JTextField tf = editor.getTextField();
    //tf.setCaretPosition(8);
    //tf.setSelectionStart(8);
    //tf.setSelectionEnd(10);
    return spinner;
  @Override public Component getTableCellEditorComponent(JTable table,
      Object value, boolean isSelected, int row, int column) {
    setValue(value);
    return this;
  }
  public Object getCellEditorValue() {
    return spinner.getValue();
  @Override public Object getCellEditorValue() {
    return getValue();
  }
  public boolean isCellEditable(EventObject e) {
    if(e instanceof MouseEvent) {
      return ((MouseEvent)e).getClickCount() >= 2;

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

  @Override public boolean stopCellEditing() {
    try {
      commitEdit();
    } catch (Exception pe) {
      Toolkit.getDefaultToolkit().beep();
      return false;
    }
    fireEditingStopped();
    return true;
  }
}
// ...
}}

**解説 [#s39b6483]
TableModel#getColumnClass(int)で、第2列目が日付(Date.class)を返すように設定し、JTable#setDefaultEditor(Class,CellEditor)メソッドで上記のセルエディタイを関連付けています。
* 解説 [#explanation]
`TableModel#getColumnClass(int)`で、第`2`列目が日付(`Date.class`)を返すように設定し、`JTable#setDefaultEditor(Class,CellEditor)`メソッドで上記のセルエディタを関連付けています。

#code{{
table.setDefaultEditor(Date.class, new SpinnerCellEditor());
table.setSurrendersFocusOnKeystroke(true);
}}

この日付用のセルエディタ(SpinnerCellEditor)は、以下のような動作になっています。
-スピナエディタで左寄せ
--デフォルトのセルレンダラーと合わせるため
--1.6.0_07, 1.6.0_06, 1.5.0_16などで、JSpinner(WindowsL&F)の文字サイズ、余白が微妙に異なる?
-ダブルクリックで編集開始
-編集開始時のキャレットは先頭の年(yyyy)ではなく日(dd)で、日(dd)が選択状態になる
--スピナボタン、上下キーなどで、日(dd)から値が変更されるようにするため
-編集開始時、スピナエディタにフォーカスが無い場合は、スピナボタンはクリック不可
--編集開始と同時に、スピナボタンが押されて日付が変更されるのを防止するため
この日付用のセルエディタ(`SpinnerCellEditor`)は、以下のような動作になっています。

//**参考リンク
**コメント [#t15c7e2c]
- スピナエディタで左寄せ
-- デフォルトのセルレンダラーと合わせるため
-- %%`1.6.0_07`, `1.6.0_06`, `1.5.0_16`などで、`JSpinner`(`WindowsLookAndFeel`)の文字サイズ、余白が微妙に異なる?%%
- ダブルクリックで編集開始
- 編集開始時のキャレットは先頭の「年(`yyyy`)」ではなく「日(`dd`)」上にあり、この「日(`dd`)」が選択状態になるよう設定
-- 編集開始直後にKBD{Up}、KBD{Down}キーで「日(`dd`)」の値を変更可能にするために必要
- 編集開始時、スピナエディタにフォーカスが無い場合は、スピナボタンはクリック不可に設定
-- 編集開始と同時に、スピナボタンが押されて日付が変更されるのを防止するため

* 参考リンク [#reference]
- [[JTableのセルに複数のJButtonを配置する>Swing/MultipleButtonsInTableCell]]

* コメント [#comment]
#comment
- `JTextField`を直接編集して、KBD{Tab}キーなどで編集終了すると`ArrayIndexOutOfBoundsException`が発生するバグを修正。 -- &user(aterai); &new{2013-05-30 (木) 19:56:25};

#comment