12.11.6 编辑单元格內容
如果用户双击JTable
表格的指定单元格,系统将会开始编辑该单元格的内容。在默认情况下,系统会使用文本框来编辑该单元格的内容,包括如图12.54所示表格的图标单元格。
与此类似的是,如果用户双击JTree
的节点,默认也会采用文本框来编辑节点的内容。
但如果单元格内容不是文字内容,而是如图12.54所示的图形类型时,用户当然不希望使用文本编辑器来编辑该单元格的内容,因为这种编辑方式非常不直观,用户体验相当差。为了避免这种情况,可以实现自己的单元格编辑器,从而可以给用户提供更好的操作界面。
实现JTable
的单元格编辑器应该实现TableCellEditor
接口,实现JTree
的节点编辑器需要实现TreeCelleditor
接口,TableCellEditor
和TreeCelleditor
这两个接口有非常紧密的联系。它们有一个共同的父接口:CellEditor
;而且它们有一个共同的实现类:DefaultCellEditor
。
关于TableCellEditor
和TreeCellEditor
两个接口及其实现类之间的关系如图12.55所示。
从图12.55中可以看出,Swing
为TableCellEditor
提供了DefaultCellEditor
实现类(也可作为TreeCellEditor
的实现类),DefaultCellEditor
类有三个构造器,它们分别使用文本框、复选框和ComboBox
作为单元格编辑器,其中
- 使用文本框编辑器是最常见的情形,
- 如果单元格的值是
Boolean
类型,则系统默认使用复选框编辑器(如图12.54中最右边一列所示),这两种情形都是前面见过的情形。
- 如果想指定某列使用
JComboBox
作为单元格编
实现TableCellEditor
接口可以开发自己的单元格编辑器,但这种做法比较烦琐:通常会使用扩展DefaultCellEditor
类的方式,这种方式比较简单。
TableCellEditor
接口定义了一个getTableCellEditorComponent
方法,该方法返回一个Component
对象,该对象就是该单元格的编辑器。
安装单元格编辑器的两种方式
一旦实现了自己的单元格编辑器,就可以为JTable
对象安装该单元格编辑器,与安装单元格绘制器类似,安装单元格编辑器也有两种方式
- 局部方式(列级):为特定列指定单元格编辑器,通过调用
TableColumn
的setCellEditor()
方法为该列安装单元格编辑器
- 全局方式(表级):调用
JTable
的setDefaultEditor()
方法为该表格安装默认的单元格编辑器。该方法需要两个参数,即列类型和单元格编辑器,这两个参数表明对于指定类型的数据列使用该单元格编辑器。
局部覆盖全局
与单元格绘制器相似的是,如果有一列同时满足列级单元格编辑器和表级单元格编辑器的要求,系统将采用列级单元格编辑器。
程序
下面程序实现了一个ImageCellEditor
编辑器,该编辑器由一个不可直接编辑的文本框和一个按钮组成,当用户单击该按钮时,该编辑器弹出一个文件选择器,方便用户选择图标文件。除此之外,下面程序还创建了一个基于JComboBox
的DefaultcellEditor
类,该编辑器允许用户通过下拉列表来选择图标。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
| import java.io.File; import java.awt.*; import javax.swing.*; import javax.swing.table.*; import javax.swing.filechooser.*;
public class TableCellEditorTest { JFrame jf = new JFrame("使用单元格编辑器"); JTable table; Object[][] tableData = { new Object[]{"李清照" , 29 , "女" , new ImageIcon("icon/3.gif") , new ImageIcon("icon/3.gif") , true}, new Object[]{"苏格拉底", 56 , "男" , new ImageIcon("icon/1.gif") , new ImageIcon("icon/1.gif") , false}, new Object[]{"李白", 35 , "男" , new ImageIcon("icon/4.gif") , new ImageIcon("icon/4.gif") , true}, new Object[]{"弄玉", 18 , "女" , new ImageIcon("icon/2.gif") , new ImageIcon("icon/2.gif") , true}, new Object[]{"虎头" , 2 , "男" , new ImageIcon("icon/5.gif") , new ImageIcon("icon/5.gif") , false} }; String[] columnTitle = {"姓名" , "年龄" , "性别" , "主头像" , "次头像" , "是否中国人"}; public void init() { ExtendedTableModel model = new ExtendedTableModel( columnTitle , tableData); table = new JTable(model); table.setRowSelectionAllowed(false); table.setRowHeight(40); table.setDefaultEditor(ImageIcon.class, new ImageCellEditor()); TableColumn lastColumn = table.getColumnModel().getColumn(4); JComboBox<ImageIcon> editCombo = new JComboBox<>(); for (int i = 1; i <= 10; i++) { editCombo.addItem(new ImageIcon("icon/" + i + ".gif")); } lastColumn.setCellEditor(new DefaultCellEditor(editCombo)); jf.add(new JScrollPane(table)); jf.pack(); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jf.setVisible(true); } public static void main(String[] args) { new TableCellEditorTest().init(); } } class ExtendedTableModel extends DefaultTableModel { public ExtendedTableModel(String[] columnNames , Object[][] cells) { super(cells , columnNames); } public Class getColumnClass(int c) { return getValueAt(0 , c).getClass(); } }
class ImageCellEditor extends DefaultCellEditor { private JFileChooser fDialog = new JFileChooser(); ; private JTextField field = new JTextField(15); private JButton button = new JButton("..."); public ImageCellEditor() { super(new JTextField()); initEditor(); } private void initEditor() { field.setEditable(false); button.addActionListener(e -> browse()); fDialog.addChoosableFileFilter(new FileFilter() { public boolean accept(File f) { if (f.isDirectory()) { return true; } String extension = Utils.getExtension(f); if (extension != null) { if (extension.equals(Utils.tiff) || extension.equals(Utils.tif) || extension.equals(Utils.gif) || extension.equals(Utils.jpeg) || extension.equals(Utils.jpg) || extension.equals(Utils.png)) { return true; } else { return false; } } return false; } public String getDescription() { return "有效的图片文件"; } }); fDialog.setAcceptAllFileFilterUsed(false); } public Component getTableCellEditorComponent(JTable table , Object value , boolean isSelected , int row , int column) { this.button.setPreferredSize(new Dimension(20, 20)); JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); field.setText(value.toString()); panel.add(this.field, BorderLayout.CENTER); panel.add(this.button, BorderLayout.EAST); return panel; } public Object getCellEditorValue() { return new ImageIcon(field.getText()); } private void browse() { fDialog.setCurrentDirectory(new File("icon")); int result = fDialog.showOpenDialog(null); if (result == JFileChooser.CANCEL_OPTION) { super.cancelCellEditing(); return; } else { field.setText("icon/" + fDialog.getSelectedFile().getName()); } } } class Utils { public final static String jpeg = "jpeg"; public final static String jpg = "jpg"; public final static String gif = "gif"; public final static String tiff = "tiff"; public final static String tif = "tif"; public final static String png = "png"; public static String getExtension(File f) { String ext = null; String s = f.getName(); int i = s.lastIndexOf('.'); if (i > 0 && i < s.length() - 1) { ext = s.substring(i + 1).toLowerCase(); } return ext; } }
|
上面程序中实现了一个ImageCellEditor
编辑器,程序中的粗体字代码将该单元格编辑器注册成Imagelcon
类型的单元格编辑器,如果某一列的数据类型是ImageIcon
,则默认使用该单元格编辑器。ImageCellEditor
扩展了DefaultCellEditor
基类,重写getTableCellEditorComponent
方法返回一个JPanel
,该JPanel
里包含一个文本框和一个按钮。
除此之外,程序中的粗体字代码还为最后一列安装了一个基于JComboBox
的DefaultCellEditor
。
运行上面程序,双击倒数第3列的任意单元格,开始编辑该单元格,将看到如图12.56所示的窗口。
双击第5列的任意单元格,开始编辑该单元格,将看到如图12.57所示的窗口。
通过图12.56和图12.57可以看岀,如果单元格的值需要从多个枚举值之中选择,则使用DefaultCellEditor
即可。使用自定义的单元格编辑器则非常灵活,可以取得单元格编辑器的全部控制权。