• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:JTableの編集にセルより大きなセルエディタを使用
#navi(../)
*JTableの編集にセルより大きなセルエディタを使用 [#u7405edd]
>編集者:[[Terai Atsuhiro>terai]]~
作成日:2006-08-28~
更新日:&lastmod;
---
category: swing
folder: LargeCellEditor
title: JTableの編集にセルより大きなセルエディタを使用
tags: [JTable, JList, GlassPane]
author: aterai
pubdate: 2006-08-28T00:57:26+09:00
description: 通常のセルエディタではなく、セルより大きなアイコンを選択する為のセルエディタを使用してJTableを編集します。
image: https://lh5.googleusercontent.com/_9Z4BYR88imo/TQTO6b-zTJI/AAAAAAAAAdI/UAROdzzun4k/s800/LargeCellEditor.png
---
* 概要 [#summary]
通常のセルエディタではなく、セルより大きなアイコンを選択する為のセルエディタを使用して`JTable`を編集します。

#contents
#download(https://lh5.googleusercontent.com/_9Z4BYR88imo/TQTO6b-zTJI/AAAAAAAAAdI/UAROdzzun4k/s800/LargeCellEditor.png)

**概要 [#o93f3deb]
通常のセルエディタではなく、セルより大きなアイコンを選択する為のセルエディタを使用してJTableを編集します。
* サンプルコード [#sourcecode]
#code(link){{
class IconTable extends JTable {
  private static final int XOFF = 4;
  private final JList<IconItem> editor;
  private final JComponent glassPane = new JComponent() {
    @Override public void setVisible(boolean flag) {
      super.setVisible(flag);
      setFocusTraversalPolicyProvider(flag);
      setFocusCycleRoot(flag);
    }

#screenshot
    @Override protected void paintComponent(Graphics g) {
      g.setColor(new Color(0x64_FF_FF_FF, true));
      g.fillRect(0, 0, getWidth(), getHeight());
      BufferedImage bufimg = new BufferedImage(
          getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
      Graphics2D g2 = bufimg.createGraphics();
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                          RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .15f));
      g2.setPaint(Color.BLACK);
      Rectangle r = editor.getBounds();
      for (int i = 0; i < XOFF; i++) {
        g2.fillRoundRect(
            r.x - i, r.y + XOFF, r.width + i + i, r.height - XOFF + i, 5, 5);
      }
      g2.dispose();
      g.drawImage(bufimg, 0, 0, null);
    }
  };

**サンプルコード [#p6d316e9]
 class IconTable extends JTable {
   private final MyGlassPane panel = new MyGlassPane();
   private FocusTraversalPolicy policy = new MyFocusTraversalPolicy();
   private FocusTraversalPolicy ftp;
   private final EditorFromList editor;
   private final JFrame frame;
   private Rectangle rect;
 
   public IconTable(JFrame _frame, TableModel model, Vector list) {
     super(model);
     setDefaultRenderer(Object.class, new TestRenderer());
     setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
     initCellSize(50);
     frame  = _frame;
     addMouseListener(new MouseAdapter() {
       public void mouseClicked(MouseEvent me) {
         startEditing();
       }
     });
     editor = new EditorFromList(list);
     editor.addKeyListener(new KeyAdapter() {
       public void keyPressed(KeyEvent e) {
         if(e.getKeyCode()==KeyEvent.VK_ESCAPE) {
           cancelEditing();
         }
       }
     });
     editor.addMouseListener(new MouseAdapter() {
       public void mouseClicked(MouseEvent me) {
         changeValue(me.getPoint());
       }
     });
     panel.add(editor);
     frame.setGlassPane(panel);
     panel.setVisible(false);
   }
   private void initCellSize(int size) {
     setRowHeight(size);
     JTableHeader tableHeader = getTableHeader();
     tableHeader.setResizingAllowed(false);
     tableHeader.setReorderingAllowed(false);
     TableColumnModel m = getColumnModel();
     for(int i=0;i<m.getColumnCount();i++) {
       TableColumn col = m.getColumn(i);
       col.setMinWidth(size);
       col.setMaxWidth(size);
     }
     setBorder(BorderFactory.createLineBorder(Color.black));
   }
   private void initEditor() {
     Dimension dim = editor.getPreferredSize();
     rect = getCellRect(getSelectedRow(), getSelectedColumn(), true);
     int iv = (dim.width-rect.width)/2;
     Point p = SwingUtilities.convertPoint(this,
                 rect.getLocation(), panel);
     rect.setRect(p.x-iv, p.y-iv, dim.width, dim.height);
     editor.setBounds(rect);
     editor.setSelectedValue(getValueAt(
       getSelectedRow(), getSelectedColumn()), true);
   }
   public void startEditing() {
     initEditor();
     ftp = frame.getFocusTraversalPolicy();
     panel.setVisible(true);
     editor.requestFocusInWindow();
     frame.setFocusTraversalPolicy(policy);
   }
   private void cancelEditing() {
     frame.setFocusTraversalPolicy(ftp);
     panel.setVisible(false);
   }
   private void changeValue(Point p) {
     frame.setFocusTraversalPolicy(ftp);
     Object o = editor.getModel().getElementAt(editor.locationToIndex(p));
     if(o != null) {
       setValueAt(o, getSelectedRow(), getSelectedColumn());
     }
     panel.setVisible(false);
   }
 }
  protected IconTable(TableModel model, ListModel<IconItem> list) {
    super(model);
    setDefaultRenderer(Object.class, new IconTableCellRenderer());
    setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    initCellSize(50);
    addMouseListener(new MouseAdapter() {
      @Override public void mouseClicked(MouseEvent e) {
        startEditing();
      }
    });

-&jnlp;
-&jar;
-&zip;
    editor = new EditorFromList<>(list);
    editor.getInputMap(JComponent.WHEN_FOCUSED).put(
        KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "cancel-editing");
    editor.getActionMap().put("cancel-editing", new AbstractAction() {
      @Override public void actionPerformed(ActionEvent e) {
        cancelEditing();
      }
    });
    editor.addMouseListener(new MouseAdapter() {
      @Override public void mouseClicked(MouseEvent e) {
        Point p = e.getPoint();
        IconItem item = editor.getModel().getElementAt(editor.locationToIndex(p));
        setValueAt(item, getSelectedRow(), getSelectedColumn());
        cancelEditing();
      }
    });

**解説 [#t6dfc826]
上記のサンプルでは、JTableのセルをクリックするとそのセル上にセルエディタが表示されて編集できるようになっています。
    glassPane.addMouseListener(new MouseAdapter() {
      @Override public void mouseClicked(MouseEvent e) {
        cancelEditing();
      }
    });
    glassPane.setFocusTraversalPolicy(new DefaultFocusTraversalPolicy() {
      @Override public boolean accept(Component c) {
        return Objects.equals(c, editor);
      }
    });
    glassPane.add(editor);
    glassPane.setVisible(false);
  }

通常のセルエディタは使用しないため、すべてのセルを編集不可にしています。かわりにセルをクリックしたときに、フレームのGlassPaneを有効にして、そこにJListで作成したアイコン選択エディタや、その影などを描画しています。
  private void initCellSize(int size) {
    setRowHeight(size);
    JTableHeader tableHeader = getTableHeader();
    tableHeader.setResizingAllowed(false);
    tableHeader.setReorderingAllowed(false);
    TableColumnModel m = getColumnModel();
    for (int i = 0; i < m.getColumnCount(); i++) {
      TableColumn col = m.getColumn(i);
      col.setMinWidth(size);
      col.setMaxWidth(size);
    }
    setBorder(BorderFactory.createLineBorder(Color.BLACK));
  }

フレームの外にセルエディタを描画することができないため、JTableの周りに余白をかなり多めにとっています。JTableからはみ出す場合は、セルエディタの表示位置を変更するようにしたほうがいいかもしれません。
  public void startEditing() {
    getRootPane().setGlassPane(glassPane);

**参考リンク [#yc6dd5bb]
-[[XP Style Icons - Windows Application Icon, Software XP Icons>http://www.icongalore.com/]]
--アイコンを利用しています。
    Dimension d = editor.getPreferredSize();
    editor.setSize(d);

**コメント [#n6dadda3]
    int sr = getSelectedRow();
    int sc = getSelectedColumn();
    Rectangle r = getCellRect(sr, sc, true);
    Point p = SwingUtilities.convertPoint(this, r.getLocation(), glassPane);
    p.translate((r.width - d.width) / 2, (r.height - d.height) / 2);
    editor.setLocation(p);

    glassPane.setVisible(true);
    editor.setSelectedValue(getValueAt(sr, sc), true);
    editor.requestFocusInWindow();
  }

  private void cancelEditing() {
    glassPane.setVisible(false);
  }
}
}}

* 解説 [#explanation]
上記のサンプルでは、`JTable`のセルをクリックするとそのセル上にセルエディタ(`JList`)を表示してアイコンの選択、変更が可能になります。

- デフォルトの`TableCellEditor`は使用しないためすべてのセルを編集不可に設定
- セルをクリックしたときに`JRootPane`の`GlassPane`を可視化
-- この`GlassPane`にアイコンを選択するためのセルエディタとして`JList`を追加
-- `JList`の半透明の影なども`GlassPane`上に描画
-- `GlassPane`を使用しているため`JFrame`の外にセルエディタを描画できない
--- `JTable`の周りに余白を設定することで回避

* 参考リンク [#reference]
- [https://xp-style-icons.en.softonic.com/ XP Style Icons - Download]

* コメント [#comment]
#comment
- 表示をアニメーションさせたり、音を出したりしても面白そうです。 -- &user(aterai); &new{2006-10-29 (日) 02:03:44};

#comment