原文链接:http://blog.sina.com.cn/s/blog_7f1c8c710101hdpf.html

最近自己尝试着模仿着实现一款非常有名的进销库存管理系统(智慧记)里面的一个功能。功能如下下图所示。

JTable tableA的第一列(品名规格)放的是自定义JPanel控件,JPanel上面放的是JTextfield和JButton,点击每一行第一列的JButton会弹出弹出一个JDialog,选择JDialog上面表格tableB的多行数据,插入到表格tableA里去。

1、一开始的表格tableA如下

2、点击JButton后界面如下

3、选中tableB的多行数据

4、点击确定的时候一次性插入选中的数据到tableA中

这个问题我首先查了jdk文档,发现API里并没有提供一种方法可以直接实现这个操作,于是上网查了很长时间资料,最后终于解决了这个问题,下面我详细的谈谈我实现这个功能的过程,并提供我实现这个功能的可以直接运行的源代码。

要解决这个问题,要先弄清楚TableModel、TableCellRenderer、TableCellEditor接口的作用,

TableModel为JTable提供显示的数据、维数、表格中的数据类型、显示的列标题以及单元格·是否允许被编辑用的。TableCellRenderer(单元格渲染器)接口,就是用来绘制展示当前cell单元数值内容的,你可以用文字、数值或者图片来表示内容,我们现在要绘制的就是一个带有一个JButton和一个JTextField的JPanel。就是上图tableA里的那个第一列的自定义控件。TableCellEditor(单元格编辑器)接口, 主要是用来当用户点击在具体cell时进行编辑的组件,所以TableCellEditor除了具有TableCellRenderer一样的绘制功能外还可以进行交互动作,例如在cell上出现下拉框、勾选框甚至通过按钮弹出更复杂的对话框让用户进行输入编辑。我们现在就是要通过这个接口,实现给单元格里的JButton添加事件,从而使其能够弹出上图那个JDialog。

实现这个功能我用了3个类。大家先运行下下面的代码,然后在代码后面,我尝试着讲解了是如何一步步得到最后这段可以运行的代码的。

JTableTestCellEdit完整的代码如下:

package specialtable;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EventObject;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.EventListenerList;
import javax.swing.table.TableCellEditor;

public class JTableTestCellEdit extends JPanel implements TableCellEditor,ActionListener{
 private static final long serialVersionUID = 5860619160549087886L; 
 //EventListenerList:保存EventListener 列表的类。 
 private EventListenerList listenerList = new EventListenerList(); 
 //ChangeEvent用于通知感兴趣的参与者事件源中的状态已发生更改。 
 private ChangeEvent changeEvent = new ChangeEvent(this);

JButton edit_btn;
 JTextField edit_txf;
 JTableTest jTableTest;
 
 public JTableTestCellEdit(){ 
  super();
  setLayout(new BorderLayout());
  edit_btn = new JButton("...");
  edit_txf = new JTextField();
  add(edit_txf);
  add(edit_btn,BorderLayout.EAST);
  edit_btn.setBackground(Color.white);
  edit_btn.setPreferredSize(new Dimension(20,getHeight()));
  edit_btn.addActionListener(this);
 } 
 
 JTableTestCellEdit(JTableTest jTableTest){
  this();
  this.jTableTest = jTableTest;
 }

public void addCellEditorListener(CellEditorListener l) { 
  listenerList.add(CellEditorListener.class,l); 
 } 
 public void removeCellEditorListener(CellEditorListener l) { 
  listenerList.remove(CellEditorListener.class,l); 
 } 
 private void fireEditingStopped(){ 
  CellEditorListener listener; 
  Object[]listeners = listenerList.getListenerList(); 
  for(int i = 0; i < listeners.length; i++){ 
   if(listeners[i]== CellEditorListener.class){ 
    //之所以是i+1,是因为一个为CellEditorListener.class(Class对象), 
    //接着的是一个CellEditorListener的实例 
    listener= (CellEditorListener)listeners[i+1]; 
    //让changeEvent去通知编辑器已经结束编辑 
    //          //在editingStopped方法中,JTable调用getCellEditorValue()取回单元格的值, 
    //并且把这个值传递给TableValues(TableModel)的setValueAt() 
    listener.editingStopped(changeEvent); 
   } 
  } 
 } 
 public void cancelCellEditing() {          
 } 
  
 public boolean stopCellEditing() { 
  //可以注释掉下面的fireEditingStopped();,然后在GenderEditor的构造函数中把 
  //addActionListener()的注释去掉(这时请求终止编辑操作从JComboBox获得), 
  System.out.println("编辑其中一个单元格,再点击另一个单元格时,调用。"); 
  fireEditingStopped();//请求终止编辑操作从JTable获得 
  return true; 
 } 
  
 public Component getTableCellEditorComponent(JTable table, Object value, 
   boolean isSelected, int row, int column) { 
  if(value != null)
   edit_txf.setText(value.toString());
  return this; 
 } 
  
 public boolean isCellEditable(EventObject anEvent) { 
  return true; 
 } 
  
 public boolean shouldSelectCell(EventObject anEvent) { 
  return true; 
 }

public Object getCellEditorValue() { 
  return edit_txf.getText(); 
 }

public void actionPerformed(ActionEvent e){
  Point p = edit_btn.getLocation();
  new JTableTestDialog(100,180,this,jTableTest).setVisible(true);
 }
}

JTableTest 类的完整代码如下:

package specialtable;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

public class JTableTest extends JFrame{
 String[] columnNames = {"名称及规格","零数","件数","数量","单位","件价","单价","金额","备注"};
 JTable tableA;
 public Object[][] values = { 
            {"","","","","","","","",""}, 
            {"","","","","","","","",""}, 
            {"","","","","","","","",""}, 
            {"","","","","","","","",""}, 
            {"","","","","","","","",""} 
 };
 DefaultTableModel model = new DefaultTableModel(values,columnNames);
 
 public JTableTest(){
  setBounds(100,100,800,400);
  tableA = new JTable(model);
  tableA.setRowHeight(30);
  JScrollPane scrollPane = new JScrollPane(tableA);
  DefaultTableColumnModel dcm = (DefaultTableColumnModel)tableA.getColumnModel();
        dcm.getColumn(0).setPreferredWidth(200);
       
        //调用我们刚才自己改写后的TableCellRenderer接口JTableTestRenderer
        TableColumnModel tcm= tableA.getColumnModel(); 
  TableColumn tc = tcm.getColumn(0); 
  tc.setCellRenderer(new JTableTestRenderer());
  
  //调用我们刚才自己改写后的TableCellEditor接口JTableTestCellEditor
  tc.setCellEditor(new JTableTestCellEdit(this)); 
  
  add(scrollPane);
  setVisible(true);
 }
 
 public static void main(String[] args){
  new JTableTest();
 }
 
 public void getTable(DefaultTableModel model){
  tableA.setModel(model);
  TableColumnModel tcm= tableA.getColumnModel();
  TableColumn tc = tcm.getColumn(0);
  tc.setCellRenderer(new JTableTestRenderer());
  tc.setCellEditor(new JTableTestCellEdit(this)); 
  tableA.setColumnSelectionAllowed(false);
  tableA.setRowSelectionAllowed(false);
  tcm.getColumn(0).setPreferredWidth(200); 
  tableA.repaint();
 }
}

JTableTestDialog类的完整代码如下:

package specialtable;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.Connection;
import java.sql.Statement;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;

public class JTableTestDialog extends JDialog implements ActionListener{
 String[] colunmNames = {"厂商","名称及规格","零数","件数"};
 public Object[][] values = { 
   {"京东","电器","12","13"}, 
   {"淘宝","电脑","15","16"}, 
   {"当当","书籍","13","26"}, 
   {"拍拍","qq","15","96"}, 
   {"亚马逊","书","12","18"} 
 };
 DefaultTableModel model = new DefaultTableModel(values,colunmNames);
 JTable tableB;
 JScrollPane scrollPane;
 JLabel tip_lbl =new JLabel("按ctrl或shift可多选货品");
 JButton ok = new JButton("确定");
 JPanel centerPanel = new JPanel();
 JPanel southPanel = new JPanel();
 JTableTestCellEdit jTableTestCellEdit;
 JTableTest jTableTest;

public JTableTestDialog(int x,int y){
  setBounds(x,y,400,300);
  tableB = new JTable(model);
  scrollPane = new JScrollPane(tableB);
  scrollPane.setSize(400, 280);
  southPanel.add(tip_lbl,BorderLayout.WEST);
  southPanel.add(ok,BorderLayout.EAST);
  ok.addActionListener(this);
  add(scrollPane,BorderLayout.CENTER);
  add(southPanel,BorderLayout.SOUTH);
 }

public JTableTestDialog(int x,int y,JTableTestCellEdit jTableTestCellEdit,JTableTest jTableTest){
  this(x,y);
  this.jTableTestCellEdit = jTableTestCellEdit;
  this.jTableTest = jTableTest;
 }

public void actionPerformed(ActionEvent e){ 
  if(jTableTestCellEdit != null && jTableTest != null){
   String[] columnNames = {"名称及规格","零数","件数","数量","单位","件价","单价","金额","备注"};
   DefaultTableModel model2 = new DefaultTableModel(columnNames,0);
   int[] rows = tableB.getSelectedRows();
   for(int i = 0; i < rows.length; i++){
    Vector v = new Vector();
    v.add(tableB.getValueAt(rows[i], 0));
    v.add(tableB.getValueAt(rows[i], 1));
    v.add(tableB.getValueAt(rows[i], 2));
    v.add(tableB.getValueAt(rows[i], 3));
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    model2.addRow(v);
   }
   for(int i = 0; i < 5;i++){
    Vector v = new Vector();
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    model2.addRow(v);
   }
   jTableTest.getTable(model2);
   dispose();   
  }
 }
}

代码的实现过程。

首先我们在一个窗口里写出tableA代码如下。

package specialtable;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;

public class JTableTest extends JFrame{
      String[] columnNames = {"名称及规格","零数","件数","数量","单位","件价","单价","金额","备注"};
      JTable tableA;
      public Object[][] values = { 
                {"","","","","","","","",""}, 
                {"","","","","","","","",""}, 
                {"","","","","","","","",""}, 
                {"","","","","","","","",""}, 
                {"","","","","","","","",""} 
      };
      DefaultTableModel model = new DefaultTableModel(values,columnNames);

public JTableTest(){
          setBounds(100,100,800,400);
          tableA = new JTable(model);
          tableA.setRowHeight(30);
         JScrollPane scrollPane = new JScrollPane(tableA);
         DefaultTableColumnModel dcm = (DefaultTableColumnModel)tableA.getColumnModel();
         dcm.getColumn(0).setPreferredWidth(200);
         add(scrollPane);
         setVisible(true);
      }
 
       public static void main(String[] args){
            new JTableTest();
       }
}

此时运行结果如下:


我们得到了最简单的表格。

接下来我们要改写TableCellRenderer,代码如下:

package specialtable;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.TableCellRenderer;

public class JTableTestRenderer extends JPanel implements TableCellRenderer {
        JButton edit_btn;
        JTextField edit_txf;
        public JTableTestRenderer(){ 
              super();
             setLayout(new BorderLayout());
             edit_btn = new JButton("...");
             edit_txf = new JTextField();
             add(edit_txf);
             add(edit_btn,BorderLayout.EAST);
             edit_btn.setBackground(Color.white);
             edit_btn.setPreferredSize(new Dimension(20,getHeight()));
       } 
 
     public Component getTableCellRendererComponent(JTable table, Object value, 
          boolean isSelected, boolean hasFocus, int row, int column) { 
          if(isSelected){ 
              setForeground(table.getForeground()); 
               super.setBackground(table.getBackground()); 
          }else{ 
               setForeground(table.getForeground()); 
               setBackground(table.getBackground()); 
         } 
       if(value != null)
             edit_txf.setText(value.toString());
             return this; 
        } 
}

//在类JTableTest调用我们刚才自己改写后的TableCellRenderer接口JTableTestRenderer
 TableColumnModel tcm= tableA.getColumnModel();  
 TableColumn tc = tcm.getColumn(0); 
  tc.setCellRenderer(new JTableTestRenderer());

此时,完整JTableTest类的完整代码如下:

package specialtable;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

public class JTableTest extends JFrame{
          String[] columnNames = {"名称及规格","零数","件数","数量","单位","件价","单价","金额","备注"};
          JTable tableA;
          public Object[][] values = { 
                      {"","","","","","","","",""}, 
                      {"","","","","","","","",""}, 
                     {"","","","","","","","",""}, 
                      {"","","","","","","","",""}, 
                       {"","","","","","","","",""} 
            };
        DefaultTableModel model = new DefaultTableModel(values,columnNames);
 
        public JTableTest(){
                 setBounds(100,100,800,400);
                 tableA = new JTable(model);
                 tableA.setRowHeight(30);
                 JScrollPane scrollPane = new JScrollPane(tableA);
                 DefaultTableColumnModel dcm = (DefaultTableColumnModel)tableA.getColumnModel();
                 dcm.getColumn(0).setPreferredWidth(200);
       
              //调用我们刚才自己改写后的TableCellRenderer接口JTableTestRenderer
                TableColumnModel tcm= tableA.getColumnModel(); 
                TableColumn tc = tcm.getColumn(0); 
                tc.setCellRenderer(new JTableTestRenderer());
                add(scrollPane);
                setVisible(true);
        }
         public static void main(String[] args){
                    new JTableTest();
          }
}

此时运行结果如下,看看第一列似乎已经变成我们需要的样子了。

我们先写出接下来要弹出的JDialog类JTableTestDialog ,代码如下:

package specialtable;
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;

public class JTableTestDialog extends JDialog{
 String[] colunmNames = {"厂商","名称及规格","零数","件数"};
 public Object[][] values = { 
            {"京东","电器","12","13"}, 
            {"淘宝","电脑","15","16"}, 
            {"当当","书籍","13","26"}, 
            {"拍拍","qq","15","96"}, 
            {"亚马逊","书","12","18"} 
 };
 DefaultTableModel model = new DefaultTableModel(values,colunmNames);
 JTable tableB;
 JScrollPane scrollPane;
 JLabel tip_lbl =new JLabel("按ctrl或shift可多选货品");
 JButton ok = new JButton("确定");;
 JPanel centerPanel = new JPanel();
 JPanel southPanel = new JPanel();

public JTableTestDialog(int x,int y){
  setBounds(x,y,400,300);
  tableB = new JTable(model);
  scrollPane = new JScrollPane(tableB);
  scrollPane.setSize(400, 280);
  southPanel.add(tip_lbl,BorderLayout.WEST);
  southPanel.add(ok,BorderLayout.EAST);
  add(scrollPane,BorderLayout.CENTER);
  add(southPanel,BorderLayout.SOUTH);
 }
}

接下来我们改写TableCellEditor接口:

代码如下:

package specialtable;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EventObject;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.EventListenerList;
import javax.swing.table.TableCellEditor;

public class JTableTestCellEdit extends JPanel implements TableCellEditor,ActionListener{
 private static final long serialVersionUID = 5860619160549087886L; 
 //EventListenerList:保存EventListener 列表的类。 
 private EventListenerList listenerList = new EventListenerList(); 
 //ChangeEvent用于通知感兴趣的参与者事件源中的状态已发生更改。 
 private ChangeEvent changeEvent = new ChangeEvent(this);

JButton edit_btn;
 JTextField edit_txf;

public JTableTestCellEdit(){ 
  super();
  setLayout(new BorderLayout());
  edit_btn = new JButton("...");
  edit_txf = new JTextField();
  add(edit_txf);
  add(edit_btn,BorderLayout.EAST);
  edit_btn.setBackground(Color.white);
  edit_btn.setPreferredSize(new Dimension(20,getHeight()));
  edit_btn.addActionListener(this);    //给单元格的JButton添加ActionListener,以便于弹出JDialog
 }

public void addCellEditorListener(CellEditorListener l) { 
  listenerList.add(CellEditorListener.class,l); 
 } 
 public void removeCellEditorListener(CellEditorListener l) { 
  listenerList.remove(CellEditorListener.class,l); 
 } 
 private void fireEditingStopped(){ 
  CellEditorListener listener; 
  Object[]listeners = listenerList.getListenerList(); 
  for(int i = 0; i < listeners.length; i++){ 
   if(listeners[i]== CellEditorListener.class){ 
    //之所以是i+1,是因为一个为CellEditorListener.class(Class对象), 
    //接着的是一个CellEditorListener的实例 
    listener= (CellEditorListener)listeners[i+1]; 
    //让changeEvent去通知编辑器已经结束编辑 
    //          //在editingStopped方法中,JTable调用getCellEditorValue()取回单元格的值, 
    //并且把这个值传递给TableValues(TableModel)的setValueAt() 
    listener.editingStopped(changeEvent); 
   } 
  } 
 } 
 public void cancelCellEditing() {          
 } 
  
 public boolean stopCellEditing() { 
  //可以注释掉下面的fireEditingStopped();,然后在GenderEditor的构造函数中把 
  //addActionListener()的注释去掉(这时请求终止编辑操作从JComboBox获得), 
  System.out.println("编辑其中一个单元格,再点击另一个单元格时,调用。"); 
  fireEditingStopped();//请求终止编辑操作从JTable获得 
  return true; 
 } 
  
 public Component getTableCellEditorComponent(JTable table, Object value, 
   boolean isSelected, int row, int column) { 
  if(value != null)
   edit_txf.setText(value.toString());
  return this; 
 } 
  
 public boolean isCellEditable(EventObject anEvent) { 
  return true; 
 } 
  
 public boolean shouldSelectCell(EventObject anEvent) { 
  return true; 
 }

public Object getCellEditorValue() { 
  return edit_txf.getText(); 
 }

public void actionPerformed(ActionEvent e){
  new JTableTestDialog(100,180).setVisible(true);
 }
}

此时在JTableTest中调用我们刚才自己改写后的TableCellEditor接口JTableTestCellEditor

调用后的的完整代码如下:

package specialtable;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

public class JTableTest extends JFrame{
 String[] columnNames = {"名称及规格","零数","件数","数量","单位","件价","单价","金额","备注"};
 JTable tableA;
 public Object[][] values = { 
            {"","","","","","","","",""}, 
            {"","","","","","","","",""}, 
            {"","","","","","","","",""}, 
            {"","","","","","","","",""}, 
            {"","","","","","","","",""} 
 };
 DefaultTableModel model = new DefaultTableModel(values,columnNames);
 
 public JTableTest(){
  setBounds(100,100,800,400);
  tableA = new JTable(model);
  tableA.setRowHeight(30);
  JScrollPane scrollPane = new JScrollPane(tableA);
  DefaultTableColumnModel dcm = (DefaultTableColumnModel)tableA.getColumnModel();
        dcm.getColumn(0).setPreferredWidth(200);
       
        //调用我们刚才自己改写后的TableCellRenderer接口JTableTestRenderer
        TableColumnModel tcm= tableA.getColumnModel(); 
  TableColumn tc = tcm.getColumn(0); 
  tc.setCellRenderer(new JTableTestRenderer());
  
  //调用我们刚才自己改写后的TableCellEditor接口JTableTestCellEditor
  tc.setCellEditor(new JTableTestCellEdit()); 
  
  add(scrollPane);
  setVisible(true);
 }
 
 public static void main(String[] args){
  new JTableTest();
 }
}

运行结果如下:


此时是不是更加接近我们想要的效果了。但是如何实现最后一部功能呢。即,在JTableTestDialog中选择多行数据插入表格tableA。

我们可以将数据先写入TableModel中,由于这个过程是弹出JTableTestDialog后完成的,而最终的结果写在了tableA里,因此,需要把JTableTest当前对象传给给JTableTestDialog,而JTableTestDialog是在点击了JTableTestCellEdit的JButton之后弹出来的,而JTableTestCellEdit是在JTableTest里调用的,因此可以JTableTest通过把当前对象先传给JTableTestCellEdit对象,然后在弹出JDialog后通过JTableTestCellEdit对象和JTableTestCellEdit同时传给JDialog对象。然后在JDialog对象里将数据传到JTableTest对象的tableModel里去。因此我们分别要在这些类里添加一些构造方法,以便实现对象的传递。这个过程似乎有点复杂,我不确定自己讲清楚了没有,不多说了,直接看代码吧,这样最直接。
在JTableTestCellEdit里添加一个如下构造方法,以便于将JTableTest对象传进来。

JTableTestCellEdit(JTableTest jTableTest){
  this();
  this.jTableTest = jTableTest;
 }

此时JTableTest里的调用变成如下所示,将自己传给TableCellEditor对象。

//调用我们刚才自己改写后的TableCellEditor接口JTableTestCellEditor
  tc.setCellEditor(new JTableTestCellEdit(this));

JTableTestDialog里需要添加如下构造方法,以便于接收传进来的JTableTestCellEdit对象,和传给JTableTestCellEdit的JTableTest对象。

public JTableTestDialog(int x,int y,JTableTestCellEdit jTableTestCellEdit,JTableTest jTableTest){
  this(x,y);
  this.JTableTestCellEdit = JTableTestCellEdit;
  this.jTableTest = jTableTest;
 }

此时JTableTestCellEdit对象的监视器里应该通过如下语句完成传值,

new JTableTestDialog(100,180,this,jTableTest).setVisible(true);

JTableTestCellEdit完整的代码如下:

package specialtable;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EventObject;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.EventListenerList;
import javax.swing.table.TableCellEditor;

public class JTableTestCellEdit extends JPanel implements TableCellEditor,ActionListener{
 private static final long serialVersionUID = 5860619160549087886L; 
 //EventListenerList:保存EventListener 列表的类。 
 private EventListenerList listenerList = new EventListenerList(); 
 //ChangeEvent用于通知感兴趣的参与者事件源中的状态已发生更改。 
 private ChangeEvent changeEvent = new ChangeEvent(this);

JButton edit_btn;
 JTextField edit_txf;
 JTableTest jTableTest;
 
 public JTableTestCellEdit(){ 
  super();
  setLayout(new BorderLayout());
  edit_btn = new JButton("...");
  edit_txf = new JTextField();
  add(edit_txf);
  add(edit_btn,BorderLayout.EAST);
  edit_btn.setBackground(Color.white);
  edit_btn.setPreferredSize(new Dimension(20,getHeight()));
  edit_btn.addActionListener(this);
 } 
 
 JTableTestCellEdit(JTableTest jTableTest){
  this();
  this.jTableTest = jTableTest;
 }

public void addCellEditorListener(CellEditorListener l) { 
  listenerList.add(CellEditorListener.class,l); 
 } 
 public void removeCellEditorListener(CellEditorListener l) { 
  listenerList.remove(CellEditorListener.class,l); 
 } 
 private void fireEditingStopped(){ 
  CellEditorListener listener; 
  Object[]listeners = listenerList.getListenerList(); 
  for(int i = 0; i < listeners.length; i++){ 
   if(listeners[i]== CellEditorListener.class){ 
    //之所以是i+1,是因为一个为CellEditorListener.class(Class对象), 
    //接着的是一个CellEditorListener的实例 
    listener= (CellEditorListener)listeners[i+1]; 
    //让changeEvent去通知编辑器已经结束编辑 
    //          //在editingStopped方法中,JTable调用getCellEditorValue()取回单元格的值, 
    //并且把这个值传递给TableValues(TableModel)的setValueAt() 
    listener.editingStopped(changeEvent); 
   } 
  } 
 } 
 public void cancelCellEditing() {          
 } 
  
 public boolean stopCellEditing() { 
  //可以注释掉下面的fireEditingStopped();,然后在GenderEditor的构造函数中把 
  //addActionListener()的注释去掉(这时请求终止编辑操作从JComboBox获得), 
  System.out.println("编辑其中一个单元格,再点击另一个单元格时,调用。"); 
  fireEditingStopped();//请求终止编辑操作从JTable获得 
  return true; 
 } 
  
 public Component getTableCellEditorComponent(JTable table, Object value, 
   boolean isSelected, int row, int column) { 
  if(value != null)
   edit_txf.setText(value.toString());
  return this; 
 } 
  
 public boolean isCellEditable(EventObject anEvent) { 
  return true; 
 } 
  
 public boolean shouldSelectCell(EventObject anEvent) { 
  return true; 
 }

public Object getCellEditorValue() { 
  return edit_txf.getText(); 
 }

public void actionPerformed(ActionEvent e){
  Point p = edit_btn.getLocation();
  new JTableTestDialog(100,180,this,jTableTest).setVisible(true);
 }
}

JTableTestCellEdit差不多已经完成了他的任务,接下来就是JTableTestDialog对象如何将值传给JTableTest的问题了。

我们在JTableTest添加一个如下所示的方法,以便于接收JTableTestDialog里的存有JTableTestDialog上数据的TableModel对象。

public void getTable(DefaultTableModel model){
  tableA.setModel(model);
  TableColumnModel tcm= tableA.getColumnModel();
  TableColumn tc = tcm.getColumn(0);
  tc.setCellRenderer(new JTableTestRenderer());
  tc.setCellEditor(new JTableTestCellEdit(this)); 
  tableA.setColumnSelectionAllowed(false);
  tableA.setRowSelectionAllowed(false);
  tcm.getColumn(0).setPreferredWidth(200); 
  tableA.repaint();
 }

接着在JTableTestDialog类里将数据写入TableModel对象里传给JTableTest对象:

这时JTableTest 类的完整代码如下:

package specialtable;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

public class JTableTest extends JFrame{
 String[] columnNames = {"名称及规格","零数","件数","数量","单位","件价","单价","金额","备注"};
 JTable tableA;
 public Object[][] values = { 
            {"","","","","","","","",""}, 
            {"","","","","","","","",""}, 
            {"","","","","","","","",""}, 
            {"","","","","","","","",""}, 
            {"","","","","","","","",""} 
 };
 DefaultTableModel model = new DefaultTableModel(values,columnNames);
 
 public JTableTest(){
  setBounds(100,100,800,400);
  tableA = new JTable(model);
  tableA.setRowHeight(30);
  JScrollPane scrollPane = new JScrollPane(tableA);
  DefaultTableColumnModel dcm = (DefaultTableColumnModel)tableA.getColumnModel();
        dcm.getColumn(0).setPreferredWidth(200);
       
        //调用我们刚才自己改写后的TableCellRenderer接口JTableTestRenderer
        TableColumnModel tcm= tableA.getColumnModel(); 
  TableColumn tc = tcm.getColumn(0); 
  tc.setCellRenderer(new JTableTestRenderer());
  
  //调用我们刚才自己改写后的TableCellEditor接口JTableTestCellEditor
  tc.setCellEditor(new JTableTestCellEdit(this)); 
  
  add(scrollPane);
  setVisible(true);
 }
 
 public static void main(String[] args){
  new JTableTest();
 }
 
 public void getTable(DefaultTableModel model){
  tableA.setModel(model);
  TableColumnModel tcm= tableA.getColumnModel();
  TableColumn tc = tcm.getColumn(0);
  tc.setCellRenderer(new JTableTestRenderer());
  tc.setCellEditor(new JTableTestCellEdit(this)); 
  tableA.setColumnSelectionAllowed(false);
  tableA.setRowSelectionAllowed(false);
  tcm.getColumn(0).setPreferredWidth(200); 
  tableA.repaint();
 }
}

JTableTestDialog类的完整代码如下:

package specialtable;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.Connection;
import java.sql.Statement;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;

public class JTableTestDialog extends JDialog implements ActionListener{
 String[] colunmNames = {"厂商","名称及规格","零数","件数"};
 public Object[][] values = { 
   {"京东","电器","12","13"}, 
   {"淘宝","电脑","15","16"}, 
   {"当当","书籍","13","26"}, 
   {"拍拍","qq","15","96"}, 
   {"亚马逊","书","12","18"} 
 };
 DefaultTableModel model = new DefaultTableModel(values,colunmNames);
 JTable tableB;
 JScrollPane scrollPane;
 JLabel tip_lbl =new JLabel("按ctrl或shift可多选货品");
 JButton ok = new JButton("确定");
 JPanel centerPanel = new JPanel();
 JPanel southPanel = new JPanel();
 JTableTestCellEdit jTableTestCellEdit;
 JTableTest jTableTest;

public JTableTestDialog(int x,int y){
  setBounds(x,y,400,300);
  tableB = new JTable(model);
  scrollPane = new JScrollPane(tableB);
  scrollPane.setSize(400, 280);
  southPanel.add(tip_lbl,BorderLayout.WEST);
  southPanel.add(ok,BorderLayout.EAST);
  ok.addActionListener(this);
  add(scrollPane,BorderLayout.CENTER);
  add(southPanel,BorderLayout.SOUTH);
 }

public JTableTestDialog(int x,int y,JTableTestCellEdit jTableTestCellEdit,JTableTest jTableTest){
  this(x,y);
  this.jTableTestCellEdit = jTableTestCellEdit;
  this.jTableTest = jTableTest;
 }

public void actionPerformed(ActionEvent e){ 
  if(jTableTestCellEdit != null && jTableTest != null){
   String[] columnNames = {"名称及规格","零数","件数","数量","单位","件价","单价","金额","备注"};
   DefaultTableModel model2 = new DefaultTableModel(columnNames,0);
   int[] rows = tableB.getSelectedRows();
   for(int i = 0; i < rows.length; i++){
    Vector v = new Vector();
    v.add(tableB.getValueAt(rows[i], 0));
    v.add(tableB.getValueAt(rows[i], 1));
    v.add(tableB.getValueAt(rows[i], 2));
    v.add(tableB.getValueAt(rows[i], 3));
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    model2.addRow(v);
   }
   for(int i = 0; i < 5;i++){
    Vector v = new Vector();
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    v.add("");
    model2.addRow(v);
   }
   jTableTest.getTable(model2);
   dispose();   
  }
 }
}

最后的运行结果如下:

点击JButton弹出JDialog

选择两件商品后,点击确定,就将值写入了tableA中了。

现在基本完成了这个功能。在这个的基础上,要做出效果类似于智慧记那样的效果或是其他操作,那就是调整表表格本身的事情了。

JTable单元格放自定义控件(一)-如何在JTable的单元格放JPanel的更多相关文章

  1. delphi 需要应用一个单元是,需要在工程里面先添加单元

    delphi 需要应用一个单元是,需要在工程里面先添加单元

  2. Excel 2010 如何在Excel的单元格中加入下拉选项

    http://jingyan.baidu.com/article/03b2f78c4ba8a05ea237ae95.html 第一步:打开excel文档,选中需加入下拉选项的单元格.   第二步:点击 ...

  3. EXCEL中,如何引用一个单元格中的数据,作为另一个单元格内容中的一部分?

    https://zhidao.baidu.com/question/230715654.html 假设单元格A1值是8(该值由函数计算得出),我要在单元格B1中引用A1的值,但只是作为B1单元格内容中 ...

  4. NPOI 生成Excel (单元格合并、设置单元格样式:字段,颜色、设置单元格为下拉框并限制输入值、设置单元格只能输入数字等)

    NPIO源码地址:https://github.com/tonyqus/npoi NPIO使用参考:源码中的 NPOITest项目 下面代码包括: 1.包含多个Sheet的Excel 2.单元格合并 ...

  5. 如何在JTable中动态添加一行

    JTable tbImage = new JTable(5,5);//create a dummy tableDefaultTableModel dtm=(DefaultTableModel)tbIm ...

  6. POI使用cell.getCellStyle()设置指定单元格颜色,但是其它没有指定的单元格也会变色

    HSSFCell cell = row.createCell((short)i); cell.getCellStyle().setAlignment(HSSFCellStyle.ALIGN_RIGHT ...

  7. 网易云课堂_C++程序设计入门(上)_第4单元:物以类聚 – 对象和类_第4单元作业【3】- 在线编程(难度:难)

    1 在本单元作业[1]和作业[2]的基础上,创建一个MyRectangle类,并在main函数中创建类的实例.(10分) 题目难度: 难 题目内容: Screen类: 与作业[2]要求完全相同. 如果 ...

  8. 网易云课堂_C++程序设计入门(下)_第11单元:工欲善其事必先利其器 - STL简介_第11单元 - 单元作业2:OJ编程 - list 与 deque

    第11单元 - 单元作业2:OJ编程 - list 与 deque 查看帮助 返回   温馨提示: 1.本次作业属于Online Judge题目,提交后由系统即时判分. 2.学生可以在作业截止时间之 ...

  9. 在JTable单元格上 加入组件,并赋予可编辑能力 [转]

    表格(单元格放置组件) 对于JTable单元格的渲染主要是通过两个接口来实现的,一个是TableCellRenderer另一个是TableCellEditor,JTable默认是用的是DefaultC ...

随机推荐

  1. 使用MongoDB的开源项目

    根据谷歌的搜索结果筛选出来的. 统计应用 counlty https://count.ly/ mongopress 开源CMS系统 http://www.mongopress.org/ Rubedo ...

  2. 加载页面遮挡耗时操作任务页面--第三方开源--AndroidProgressLayout

    在Android的开发中,往往有这种需求,比如一个耗时的操作,联网获取网络图片.内容,数据库耗时读写等等,在此耗时操作过程中,开发者也许不希望用户再进行其他操作(其他操作可能会引起逻辑混乱),而此时需 ...

  3. Global::pickClassMethod_DNT

    /*************************************************** Created Date: 19 Jul 2013 Created By: Jimmy Xie ...

  4. 记一次Surface Pro 2还原操作

    因为要做Azure的一个case,对自己的域环境下直接进行了捕获.结果导致机器直接crash. 重启后使用本地账号登陆后发现所有Win 8 的App都无法使用,包括进入设置中还原方式也无法使用. 可以 ...

  5. Java 包(package)详解

    为了更好地组织类,Java提供了包机制,用于区别类名的命名空间. 包的作用 1 把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用. 2 如同文件夹一样,包也采用了树形目录的存储方式.同一 ...

  6. Win8.1 IIS6 SQL SERVER 2012 执行 SqlServices.InstallSessionState 出错

    新装了WIN8.1,感觉很不错. 新建了第一个站点是,在执行 SqlServices.InstallSessionState("localhost", null, SessionS ...

  7. c++类中的静态成员

    静态成员和非静态成员的区别: 类静态成员用static修饰,类的静态成员属于类本身,而不属于类的某个具体对象,静态成员被类的所有对象共享,因此某个对象对静态成员(数据成员)的修改对其对象是可见的.而类 ...

  8. iTween基础之Scale(缩放大小)

    一.基础介绍:二.基础属性 原文地址:http://blog.csdn.net/dingkun520wy/article/details/50684392 一.基础介绍 ScaleTo:改变游戏对象的 ...

  9. 8种方法提升windows 8使用方便-----Win+x 编辑菜单

    在windows 8上,你可以同时按下windows键和x键或者右键点击屏幕左下角打开一个菜单名为电源菜单或者快速访问菜单,这个菜单包含快速访问系统的工具,如控制面板,命令提示符,任务管理器,资源管理 ...

  10. Careercup - Google面试题 - 5735304249999360

    2014-05-03 23:18 题目链接 原题: Insert a element in a sorted circular linked list 题目:题意简单明了,向一个有序的循环单向链表中插 ...