JTable單元格放自定義控件(一)-如何在JTable的單元格放JPanel


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

   最近自己嘗試着模仿着實現一款非常有名的進銷庫存管理系統(智慧記)里面的一個功能。功能如下下圖所示。

       JTable tableA的第一列(品名規格)放的是自定義JPanel控件,JPanel上面放的是JTextfield和JButton,點擊每一行第一列的JButton會彈出彈出一個JDialog,選擇JDialog上面表格tableB的多行數據,插入到表格tableA里去。

1、一開始的表格tableA如下

JTable單元格放自定義控件(一)-如何在JTable的單元格放JPanel

 

2、點擊JButton后界面如下

JTable單元格放自定義控件(一)-如何在JTable的單元格放JPanel

 

3、選中tableB的多行數據

JTable單元格放自定義控件(一)-如何在JTable的單元格放JPanel

 

4、點擊確定的時候一次性插入選中的數據到tableA中

JTable單元格放自定義控件(一)-如何在JTable的單元格放JPanel


 

 

       這個問題我首先查了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();
       }
}

此時運行結果如下:

JTable單元格放自定義控件(一)-如何在JTable的單元格放JPanel
我們得到了最簡單的表格。

接下來我們要改寫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();
          }
}

此時運行結果如下,看看第一列似乎已經變成我們需要的樣子了。

JTable單元格放自定義控件(一)-如何在JTable的單元格放JPanel

 

我們先寫出接下來要彈出的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();
 }
}

 

運行結果如下:

JTable單元格放自定義控件(一)-如何在JTable的單元格放JPanel
此時是不是更加接近我們想要的效果了。但是如何實現最后一部功能呢。即,在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

JTable單元格放自定義控件(一)-如何在JTable的單元格放JPanel

選擇兩件商品后,點擊確定,就將值寫入了tableA中了。
JTable單元格放自定義控件(一)-如何在JTable的單元格放JPanel

       現在基本完成了這個功能。在這個的基礎上,要做出效果類似於智慧記那樣的效果或是其他操作,那就是調整表表格本身的事情了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM