浅析JTable与TableModelTableCellRendererTableCellEditor接口——使用JComboBox显示单元格的值

Posted 坚哥威武

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅析JTable与TableModelTableCellRendererTableCellEditor接口——使用JComboBox显示单元格的值相关的知识,希望对你有一定的参考价值。

如同其它的Swing组件,JTable使用MVC(模型、试图、控制器)设计方式,将可视化组件(JTable实例)从其数据(TableModel实现)中分离出来。

·TableModel接口

1、TableModel为JTable提供

·显示的数据

·表格的维数

·表格中每一列所包含的数据类型

·应该显示的列标题

·是否允许编辑指定单元格的值

2、实现TableModel:

TableValues类

 

import javax.swing.table.AbstractTableModel;
/**  
 *     注意:一般使用AbstractTableModel创建TableModel的实现,只有少量数据时使用DefaultTableModel,
 */
public class TableValues extends AbstractTableModel{
         private static final long serialVersionUID = -8430352919270533604L;
         public final static int NAME = 0;
         public final static int GENDER = 1;
         public final static String[] columnNames = {"姓名", "性别"};
         public Object[][] values = {
                            {"Cannel_2020",true},
                            {"Lucy",false},
                            {"韩梅",false},
                            {"李雷",true},
                            {"Jim",true}
         };
         publicint getColumnCount() {
                   return  values[0].length;
         }
         publicint getRowCount() {
                   return values.length;
         }
         public Object getValueAt(int rowIndex, int columnIndex) {
                   return values[rowIndex][columnIndex];
         }


         /**
          * 获取列名
          */
         publicString getColumnName(int column){
                   return columnNames[column];
         }

SimpleTableTest类

 

import java.awt.BorderLayout;
import java.awt.Container;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
public class SimpleTableTest extends JFrame{
        
         private static final long serialVersionUID = -4172876583187222326L;
         protected JTable table;
         public SimpleTableTest(){
                   Container pane = getContentPane();
                   pane.setLayout(newBorderLayout());


                   TableValues tv =  new TableValues();
                   table= new JTable(tv);


                   //设置行高
                   table.setRowHeight(30);
                   //必须把table放入JScrollPane才会有列名出现
                   JScrollPane jsp = new JScrollPane(table);
                   pane.add(jsp,BorderLayout.CENTER);
         }
         publicstatic void main(String[] args) {
                   SimpleTableTeststt = new SimpleTableTest();
                   stt.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                   stt.setSize(400,200);
                   stt.setVisible(true);
         }

运行结果:

技术分享

·实现TableCellRenderer(单元格渲染器)接口

1、使表格“性别”一列的单元格出现JComboBox组件

GenderRenderer类

 

import java.awt.Component;
import javax.swing.JComboBox;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
 
public class GenderRenderer extendsJComboBox implements TableCellRenderer{
         privatestatic final long serialVersionUID = -8624401777277852691L;
         publicGenderRenderer(){
                   super();
                   addItem("男");
                   addItem("女");
         }
         publicComponent getTableCellRendererComponent(JTable table, Object value,
                            booleanisSelected, boolean hasFocus, int row, int column) {
                   if(isSelected){
                            setForeground(table.getForeground());
                            super.setBackground(table.getBackground());
                   }else{
                            setForeground(table.getForeground());
                            setBackground(table.getBackground());
                   }
                   booleanisMale = ((Boolean)value).booleanValue();
                   setSelectedIndex(isMale? 0 : 1);
                   returnthis;
         }
 

2、把SimpleTableTest类的构造函数改变如下:

 

public SimpleTableTest(){
                   setTitle("FromCannel_2020‘s blog(CSDN)");
                   setLayout(newBorderLayout());
                   TableValuestv =  new TableValues();
                   table= new JTable(tv);
                   //设置行宽
                   table.setRowHeight(30);
                  
                   TableColumnModeltcm= table.getColumnModel();
                   TableColumntc = tcm.getColumn(TableValues.GENDER);
                   //设置“性别”列的单元格渲染器(renderer)
                   tc.setCellRenderer(newGenderRenderer());
                  
                   //必须把table放入JScrollPane才会有列名出现
                   JScrollPanejsp = new JScrollPane(table);
                   add(jsp,BorderLayout.CENTER);
         } 

运行结果:

技术分享

3、注意:渲染器实际上并没有像可视化组件添加到Container中那样添加到JTable实例中,即表格中不含有JComboBox实例。此时,是将唯一的JComboBox实例绘制(通过向paint()方法传递Graphics对象)到“性别”一列的每一个单元格所占用的区域中。

4、在TableValues添加如下代码(覆盖AbstractTableModel中的方法),使得JTable实例中单元格可以编辑:

 

    /**
     * 设置单元格可以编辑
     */
    public booleanisCellEditable(int row, int column){
       returntrue;
    } 

然而此时对“性别”一列的单元格进行编辑,会出现如下情况:

技术分享

这就得使用到单元格编辑器了。

·实现TableCellEditor(单元格编辑器)接口

1、

 

import java.awt.Component;
import java.util.EventObject;
import javax.swing.JComboBox;
import javax.swing.JTable;
importjavax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.EventListenerList;
import javax.swing.table.TableCellEditor;
 
public class GenderEditor extends JComboBoximplements TableCellEditor{
        
         privatestatic final long serialVersionUID = 5860619160549087886L;
         //EventListenerList:保存EventListener 列表的类。
         privateEventListenerList listenerList = new EventListenerList();
         //ChangeEvent用于通知感兴趣的参与者事件源中的状态已发生更改。
         privateChangeEvent changeEvent = new ChangeEvent(this);
         publicGenderEditor(){
                   super();
                   addItem("男");
                   addItem("女");
                   //请求终止编辑操作可以包含单元格的JTable收到,也可以从编辑器组件本身(如这里的JComboBox)获得
                   /*addActionListener(newActionListener(){
                            publicvoid actionPerformed(ActionEvent e) {
                                     System.out.println("ActionListener");
                                     //如同stopCellEditing,都是调用fireEditingStopped()方法
                                     fireEditingStopped();
                            }
                           
                   });*/
         }
         publicvoid addCellEditorListener(CellEditorListener l) {
                   listenerList.add(CellEditorListener.class,l);
         }
         publicvoid removeCellEditorListener(CellEditorListener l) {
                   listenerList.remove(CellEditorListener.class,l);
         }
         privatevoid fireEditingStopped(){
                   CellEditorListenerlistener;
                   Object[]listeners = listenerList.getListenerList();
                   for(inti = 0; i < listeners.length; i++){
                            if(listeners[i]== CellEditorListener.class){
                                     //之所以是i+1,是因为一个为CellEditorListener.class(Class对象),
                                     //接着的是一个CellEditorListener的实例
                                     listener= (CellEditorListener)listeners[i+1];
                                     //让changeEvent去通知编辑器已经结束编辑
                           <span style="white-space:pre"> </span>     //在editingStopped方法中,JTable调用getCellEditorValue()取回单元格的值,
                                     //并且把这个值传递给TableValues(TableModel)的setValueAt()
                                     listener.editingStopped(changeEvent);
                            }
                   }
         }
         publicvoid cancelCellEditing() {         
         }
         /**
          * 编辑其中一个单元格,再点击另一个单元格时,调用。-------------!!!!!
          */
         publicboolean stopCellEditing() {
                   //可以注释掉下面的fireEditingStopped();,然后在GenderEditor的构造函数中把
                   //addActionListener()的注释去掉(这时请求终止编辑操作从JComboBox获得),
                   System.out.println("编辑其中一个单元格,再点击另一个单元格时,调用。");
                   fireEditingStopped();//请求终止编辑操作从JTable获得
                   returntrue;
         }
         /**
          * 为一个单元格初始化编辑时,getTableCellEditorComponent被调用
          */
         publicComponent getTableCellEditorComponent(JTable table, Object value,
                            booleanisSelected, int row, int column) {
                   booleanisMale = ((Boolean)value).booleanValue();
                   setSelectedIndex(isMale? 0 : 1);
                   returnthis;
         }
         /**
          * 询问编辑器它是否可以使用 anEvent 开始进行编辑。
          */
         publicboolean isCellEditable(EventObject anEvent) {
                   returntrue;
         }
         /**
          * 如果应该选择正编辑的单元格,则返回true,否则返回 false。
          */
         publicboolean shouldSelectCell(EventObject anEvent) {
                   returntrue;
         }
 
         /**
          * 返回值传递给TableValue(TableModel)中的setValueAt()方法
          */
         publicObject getCellEditorValue() {
                   returnnew Boolean(getSelectedIndex() == 0 ? true : false);
         }

2、把SimpleTableTest类的构造函数中

 

tc.setCellRenderer(new GenderRenderer()); 

后面加入:

 

//设置“性别”列的单元格编辑器(editor)
tc.setCellEditor(new GenderEditor()); 

运行结果:

技术分享

3、还有一点别忘了再在TableValues加入如下代码(原因:看第4的最后一点)

       /**
          * 单元格被编辑完后,调用此方法更新值
          */
         publicvoid setValueAt(Object value, int row, int column){
                   values[row][column]= value;
         }

4、GenderEditor类的工作流程:

1)、调用TableCellEditor接口中的getTableCellEditorComponent()方法初始化编辑

2)、编辑当前的单元格,再点击另一个单元格时,调用CellEditor中的stopCellEditing(),通过fireEditingStopped()调用到editingStopped()。

3)、在editingStopped方法中,JTable调用getCellEditorValue()取回单元格的值,并且把这个值传递给TableValues(TableModel)的setValueAt()

 

以上是关于浅析JTable与TableModelTableCellRendererTableCellEditor接口——使用JComboBox显示单元格的值的主要内容,如果未能解决你的问题,请参考以下文章

如何刷新与 jtable 绑定的 Jscrollpane 中的数据?

JTable 中的动态搜索过滤器

执行 jtable 显示异常

JTable用法-实例

Swing-JTable的渲染器与编辑器使用demo

JTable-在单元格中绘制内容(文本)