Java Swing JTable API

利用 JTable 類,能夠以表格的形式展現數據,可設置容許用戶編輯數據。JTable 自己不擁有或者緩存數據;它只是數據的視圖。這裏有一個放在滾動面板上的典型表格:html



     本文展現如何完成一些常見的表格相關的任務:包括如下部分:java

(1)建立一個簡單的表格正則表達式

(2)向容器添加表格數據庫

(3)改變往往一欄的寬度express

(4)用戶選擇api

(5)建立表格模型數組

(6)監聽數據改變緩存

(7)點燃數據改變事件數據結構

(8)概念:編輯器和渲染器oracle

(9)使用自定義渲染器

(10)爲單元格指定提示工具

(11)爲列頭指定提示工具

(12)排序和過濾

(13)使用組合框做爲編輯器

(14)使用其餘編輯器

(15)使用編輯器驗證用戶的文本輸入

(16)打印

(17)例子列表

 

(1)建立一個簡單的表格

    SimpleTableDemo.java 中的表格在一個字符串數組中聲明各個列名

Java代碼   收藏代碼
  1. String[] columnNames = {"First Name",  
  2.                         "Last Name",  
  3.                         "Sport",  
  4.                         "# of Years",  
  5.                         "Vegetarian"};  

    數據初始化並存儲在二維數組:

Java代碼   收藏代碼
  1. Object[][] data = {  
  2.     {"Kathy", "Smith",  
  3.      "Snowboarding", new Integer(5), new Boolean(false)},  
  4.     {"John", "Doe",  
  5.      "Rowing", new Integer(3), new Boolean(true)},  
  6.     {"Sue", "Black",  
  7.      "Knitting", new Integer(2), new Boolean(false)},  
  8.     {"Jane", "White",  
  9.      "Speed reading", new Integer(20), new Boolean(true)},  
  10.     {"Joe", "Brown",  
  11.   
  12.      "Pool", new Integer(10), new Boolean(false)}  
  13. };  

    接着表格使用這些數據和列名構造一個表格:

Java代碼   收藏代碼
  1. JTable table = new JTable(data, columnNames);  

    有兩個接收數據的 JTable 構造器:

  • JTable(Object[][] rowData, Object[] columnNames)
  • JTable(Vector rowData, Vector columNames)

    這些構造函數的好處是容易實現,而缺點是:

  • 他們自動設置每一個單元格爲可編輯
  • 數據類型都視爲同樣的(字符串類型)。例如,若是表格的一列有 Boolean 數據,表格用單選框來展現這個數據。但是,若是你用上面兩種構造,你的 Boolean 數據將顯示爲字符串,就像前面表格截圖中的 Vegetarian 一列同樣。
  • 它們要求你把全部表格數據放入一個數組或者vector,這些數據結構可能不適合某些數據類型。例如,若是你實體化一組數據庫對象,比起拷貝全部值放入數組和vector,你可能僅僅想要直接查詢這些對象的值。

    若是你想避免這些限制,你須要實現你本身的表格模型,見「(5)建立表格模型」。

 

(2)向容器添加表格

    這裏有一段建立滾動面板做爲表格容器的常規代碼:

Java代碼   收藏代碼
  1. JScrollPane scrollPane = new JScrollPane(table);  
  2. table.setFillsViewportHeight(true);  

    這兩行代碼實現了:表格對象的引用做爲 JScrollPane 構造函數的參數,建立一個容納table的容器,table添加到容器中;JTable.setFillsViewportHeight 方法設置了 fillsViewportHeight 屬性。當這個屬性爲 true 時,表格會佔據容器整個高度,即使表格沒有足夠的行去使用這些垂直空間。這使得表格更容易實現拖拉操做。

 

    滾動面板自動把表格頭放置在視窗的頂端。當表格數據垂直滾動時,列名保持在視窗頂端可視。

 

    若是你想要使用一個沒有滾動面板的表格,你必須得到表格頭組件,而後本身放置它,例如:

Java代碼   收藏代碼
  1. container.setLayout(new BorderLayout());  
  2. container.add(table.getTableHeader(), BorderLayout.PAGE_START);  
  3. container.add(table, BorderLayout.CENTER);   

 

(3)改變往往一列的寬度

    默認狀況下,表格全部列等寬,切這些列自動填滿整個表格的寬度。當表格變寬或者變窄時(一般是用戶調整包含表格的窗口大小),全部的列寬自動調整到適當寬度。

 

    當用戶經過拖動列頭的右邊界來調整某一列的寬度時,要麼別的列的寬度會受到影響而改變,要麼整個表格的寬度會改變。默認狀況下,表格總體寬度保持不變,「拖動點「的右側各列利用增長或減小的空間自我調整,拖動的那一列的左側各列保持不變。

 

    要定義各列初始化寬度,你能夠對錶格各列調用 setPreferredWidth 方法。能夠設置各列首選寬度,和他們的相對寬度。例如,向demo增長下面代碼,是的第三列比其餘列更寬:

Java代碼   收藏代碼
  1. TableColumn column = null;  
  2. for (int i = 0; i < 5; i++) {  
  3.     column = table.getColumnModel().getColumn(i);  
  4.     if (i == 2) {  
  5.         column.setPreferredWidth(100); //third column is bigger  
  6.     } else {  
  7.         column.setPreferredWidth(50);  
  8.     }  
  9. }  

 

   如上面代碼所示,每一列表明一個 TableColumn 對象, TableColumn 提供 getter 和 setter 方法設置和獲取列的最小、首選、最大寬度 和 目前寬度。 基於估計單元格內容須要的空間調整單元格寬度,查看TableRenderDemo.java. 中的 initColumnSizes 方法。

 

    當用戶明確的調整列寬度,列的」首選寬度「就被設置爲用戶指定的」新的當前寬度「。不過,當表格因視窗調整而改變大小是,列的」首選寬度「不會改變。」首選寬度「的存在是用於計算新的列的寬度,來填充可用空間。

 

    你能夠經過調用 setAutoResizeMode 改變一個表格的調整模式。

 

(4)用戶選擇

    默認配置狀況下,一個表格支持選擇一行或多行。用戶能夠選擇一組連續的或不連續的行。用戶最後指示的那個單元格,在 Metal 樣式中,會被outlined(輪廓虛線)。這個單元格被稱爲 」lead selection「(導聯選擇(器, 鈕));有時候也稱爲 」聚焦單元格「 或 」當前單元格「。

 

   用戶使用鼠標鍵盤實現選擇,選擇的方式描述以下

 

操做
鼠標動做
鍵盤動做

選擇單行 點擊 向上或向下
選中連續多行 Shift—點擊/拖拉 Shitf-向上 或 Shift-向下

 

向選中的行集增長行/切換選擇

Control-點擊 Control+向上或向下, 使用空格鍵增長當前行或切換選擇.

 

    下面的例子程序TableSelectionDemo.java展現了相似的表格,容許用戶操縱某些 JTable 選項。還有一個文本面板記錄」選擇事件「。(這個demo裏面的有關複選框事件處理代碼寫的好好)

 

    在下面的截圖中,這事默認的Metal樣式,選中的行highlight,選擇的單元格outline


    在下面的」Selection Mode「下,有一些複選框,選擇」Single Selection「。如今你只能在某一時刻選中一行,若是你選中」Single Interval Selection「,你能夠選擇連續的多行。

 

    全部的」Selection Mode「下面的複選框按鈕,調用 JTable.setSelectionMode. 這個方法帶一個參數,爲javax.swing.ListSelectionModelMULTIPLE_INTERVAL_SELECTIONSINGLE_INTERVAL_SELECTION, andSINGLE_SELECTION.中的一個(依次爲,多行可間隔,多行無間隔,單行)

 

    回到咱們的 demo,注意,在」Selection Options「下三個複選框,每一個複選框控制一個由 JTable 定義的綁定屬性的boolean類型狀態值:

  • 」Row Selection「控制 控制 rowSelectionAllowed 屬性,經過setRowSelectionAllowed 和getRowSelectionAllowed 設置和讀取。當這個綁定屬性爲 true (同時 columnSelectionAllowed屬性爲 false)時,用戶能夠選擇行。
  • 」Coolumn Selection「控制 columnSelectionAllowed 屬性,經過setColumnSelectionAllowedgetColumnSelectionAllowed 設置和讀取。當這個綁定屬性爲 true 時,用戶能夠選擇單個單元格,或者呈矩陣塊地選擇多個單元格
  • 」Cell Selection「控制 cellSelectionEnabled,經過 setCellSelectionEnabled andgetCellSelectionEnabled 設置和獲取。當這個綁定屬性爲 true 是,用戶能夠選擇單個單元格,或是以矩陣塊的形式選擇多個單元格。

    提醒:JTable使用很簡單的選擇原則來管理 行 和 列 的交集,它並不是設計成全面處理獨立的單元格選擇。(就是說,有些多單元格的選擇是不被handle的,你也選不到)

 

    若是你清空三個複選框,就沒有selection了,只有lead selection表現而已。(我以爲lead selection只是形式上的選擇,是一種導航觀察的形式,而selection是確切選中表格中某些單元格的事實。我沒法確切地解釋出lead selection 和 selection的區別,我只能意會呀)

 

    你可能注意到」Cell Selection「複選框在」multiple interval selection「選擇模式中是不可用的。只是在這個demo的模式中是不被支持的。你能夠在」multiple interval selection「模式中指定單元格選擇,可是表格也不會產生有效的selection。

 

    你或許還注意到,改變這」selection option「中某個選項可能影響其餘選項。這是由於容許行選擇和列選擇,就意味着容許單元格原則。JTable自動更新三個綁定屬性,以保持它們的一致性。

 

    提醒:設置 cellSelectionEnabled 的值會附帶同時設置 rowSelectionEnabled 和 columnSelectionEnabled 的效果。一樣,設置後二者的值一樣會影響 cellSelectionEnabled 的值。設置 row……和 cloumn……爲不一樣值,同時設置 cell……爲 false,能夠測試一下。

 

    要得到當前的selection,使用 JTable.getSelectedRows,返回一個帶索引行數的數組,使用 JTable.getSelectedColumns 返回 列索引。 要得到 lead selection 的座標,須要引用table自己的 selection model 和 table 的 column model。下面代碼格式化一個包含一個lead selection的行和列的字符串:

Java代碼   收藏代碼
  1. String.format("Lead Selection: %d, %d. ",  
  2.     table.getSelectionModel().getLeadSelectionIndex(),  
  3.     table.getColumnModel().getSelectionModel().getLeadSelectionIndex());  

    使用selections產生一些時間。參考 How to Write a List Selection Listener in the Writing Event Listeners

 

(5)建立表格模型

    每一個 table 對象 使用一個 table model 對象來管理表格中真實的數據。一個 table model 對象必定要實現 TableModel 接口,若是程序沒有提供一個 table model 對象,JTable自動建立一個 DefaultTableModel實例。這種關係可用下面的圖來解釋


    SimpleTableDemo 中 JTable 的構造器以下面代碼同樣,建立它的 table model:

Java代碼   收藏代碼
  1. new AbstractTableModel() {  
  2.     public String getColumnName(int col) {  
  3.         return columnNames[col].toString();  
  4.     }  
  5.     public int getRowCount() { return rowData.length; }  
  6.     public int getColumnCount() { return columnNames.length; }  
  7.     public Object getValueAt(int row, int col) {  
  8.         return rowData[row][col];  
  9.     }  
  10.     public boolean isCellEditable(int row, int col)  
  11.         { return true; }  
  12.     public void setValueAt(Object value, int row, int col) {  
  13.         rowData[row][col] = value;  
  14.         fireTableCellUpdated(row, col);  
  15.     }  
  16. }  

    上面代碼,簡單的實現了一個 table model。一般在 AbstractTableModel 的子類中實現 table model。

 

    你的模型能夠支持 數組、vector 或 hash map類型的數據。甚至是從外資資源,如數據庫中得到數據。他甚至能夠在運行期間產生數據。

 

    這個TableDemo.java例子中的表格與前面 SimpleTableDemo 中的表格有幾點區別:

  •   TableDemo的自定義 table model,即使它很簡單,不過他能夠輕鬆地肯定數據的類型,幫助 JTable 用最好的格式展現數據。 SimpleTableDemo 自動建立的 table model, 並不知道 # of Years 一欄包括數字(須要右對齊且特殊格式),也不知道 Vegetarian 一欄包含用單選框表示的布爾值。
  • 在 TableDemo 中實現的 table model 並非讓你編輯那些表示姓名的欄目,而是修改其餘欄。在 SimpleTableDemo中,全部單元格都是可編輯的。

觀察 TableDemo.java 的代碼,粗體部分是區別於 SimpleTableDemo自動建立的 table model:

Java代碼   收藏代碼
  1. public TableDemo() {  
  2.     ...  
  3.     JTable table = new JTable(new MyTableModel());  
  4.     ...  
  5. }  
  6.   
  7. class MyTableModel extends AbstractTableModel {  
  8.     private String[] columnNames = ...//same as before...  
  9.     private Object[][] data = ...//same as before...  
  10.   
  11.     public int getColumnCount() {  
  12.         return columnNames.length;  
  13.     }  
  14.   
  15.     public int getRowCount() {  
  16.         return data.length;  
  17.     }  
  18.   
  19.     public String getColumnName(int col) {  
  20.         return columnNames[col];  
  21.     }  
  22.   
  23.     public Object getValueAt(int row, int col) {  
  24.         return data[row][col];  
  25.     }  
  26.   
  27.     <strong>public Class getColumnClass(int c) {  
  28.         return getValueAt(0, c).getClass();  
  29.     }</strong>  
  30.   
  31.   
  32.   
  33.   
  34.     /* 
  35.      * Don't need to implement this method unless your table's 
  36.      * editable. 
  37.      */  
  38.     public boolean isCellEditable(int row, int col) {  
  39.         //Note that the data/cell address is constant,  
  40.         //no matter where the cell appears onscreen.  
  41.         <strong>if (col < 2) {  
  42.             return false;  
  43.         } else {  
  44.             return true;  
  45.         }</strong>  
  46.   
  47.   
  48.   
  49.     }  
  50.   
  51.     /* 
  52.      * Don't need to implement this method unless your table's 
  53.      * data can change. 
  54.      */  
  55.     public void setValueAt(Object value, int row, int col) {  
  56.         data[row][col] = value;  
  57.         fireTableCellUpdated(row, col);  
  58.     }  
  59.     ...  
  60. }  

 

(6)監聽數據改變

    一個 table model 能夠有多個監聽器,不管什麼時候,只要表格數據被改變,都會通知這些監聽器。監聽器是TableModelListener 類的實例。在下面的例子代碼中, SimpleTableDemo 增長了一個監聽器,粗體部分是新的代碼:

 

Java代碼   收藏代碼
  1. <strong>import javax.swing.event.*;  
  2. import javax.swing.table.TableModel;</strong>  
  3.   
  4.   
  5.   
  6.   
  7. public class SimpleTableDemo ...<strong> implements TableModelListener </strong>  
  8.   
  9.   
  10. {  
  11.     ...  
  12.     public SimpleTableDemo() {  
  13.         ...  
  14.     <strong>    table.getModel().addTableModelListener(this);</strong>  
  15.   
  16.   
  17.         ...  
  18.     }  
  19.   
  20.     <strong>public void tableChanged(TableModelEvent e) {  
  21.         int row = e.getFirstRow();  
  22.         int column = e.getColumn();  
  23.         TableModel model = (TableModel)e.getSource();  
  24.         String columnName = model.getColumnName(column);  
  25.         Object data = model.getValueAt(row, column);  
  26.   
  27.         ...// Do something with the data...  
  28.     }</strong>  
  29.   
  30.   
  31.     ...  
  32. }  

 

(7)點燃數據改變事件

    爲了喚醒數據改變事件,table model必定要知道若是構造 TableModelEvent 對象。這是個複雜的過程,可是已經在 DefaultTableModel 中實現了。你可讓 JTable 使用他本身默認的 DefaultTableModel 實例,或者建立自定義的 DefaultTableModel 子類。

 

    若是 DefaultTableModel 不適合做爲自定義 table model 類的基類,考慮使用 AbstractTableModel 做爲基類。這個類實現了構造 TableModelEvent 對象的簡單框架。(DefaultTableModel 是該抽象類的子類)當外界改變了表格數據的時候,你的自定義類僅僅須要調用 AbstractTableModel 方法中的一個,以下:

 

Method Change

fireTableCellUpdated Update of specified cell. 單元格更新
fireTableRowsUpdated Update of specified rows 行更新
fireTableDataChanged Update of entire table (data only). 表格範圍內的數據更新
fireTableRowsInserted New rows inserted. 插入新行
fireTableRowsDeleted Existing rows Deleted 刪除存在的行
fireTableStructureChanged   Invalidate entire table, both data and structure. 使表格無效,包括數據和結構

 

 

(8)概念:編輯器和渲染器(Editors and Renderers)

    在進行後面的學習前,你須要理解表格是如何繪製它的單元格的。你可能會認爲表格中每一個單元格都是一個組件,可是,考慮性能的緣由,Swing的表格並不這麼作。

 

    取而代之的是,一個 single cell renderer(單一單元格繪製器)通常用來繪製全部包含同類型數據的單元格。你能夠想象這個 renderer 是一個可配置的墨水打印,表格使用它將格式化的數據合適地印在每一個單元格上。當用於開始編輯一個單元格的數據時, cell editor 接管這個單元格,控制單元格的編輯行爲。

 

    例如,TableDemo 的 # of Years 列中的每一個單元格包含數字數據——具體是一個Integer對象。默認狀況下,對於數字列,渲染器使用單個 JLabel 實例在列上的單元格繪製恰當的居右的數字。若是用戶開始編輯一個單元格,則默認的單元格編輯器使用一個 居右的 JTextField 來控制單元格的編輯動做。

 

    如何選擇 render 處理某一列的單元格,表格首先會肯定,對於該列,你是否已經指定了一個 renderer。若是你未指定,那麼 table 會調用 table model 的 getColumnClass 方法,得到該列的單元格的數據的類型。接着,table 會將該列的數據類型與一個數據類型列表對比,該列表註冊了多種 cell renderers。該表由 table 初始化,你能夠向該表增長renderer。一般,table 會把下列類型放到列表中:

  • Boolean——複選框
  • Number——居右的label
  • Double, Float——相似Number,不過 從 對象 到 文本的轉化經過 NumberFormat 的實例來執行。
  • Date——label,對象 到 文本 的轉換經過 DateFormat 的實例來執行。
  • ImageIcon,Icon——居中的label
  • Object——展現了對象的字符串值的label

    單元格編輯器使用相似的法則。

 

    注意,若是讓 table 本身建立它的 model,它會把 Object 做爲各列的類型。爲了指定更明確列類型,table model必定要定義適當的 getColumnClass 方法,像 TableDemo.java. 中的定義那樣。

 

    記住,儘管 render 決定有多少單元格和列頭被指定了它的 tool tip text(鼠標指在上面顯示的提示文本),可是 render 自己不處理事件。若是你須要得到 table 內發生的事件,你使用的技術就是在下面分類的事件中作變化:

Situation How to Get Events

To detect events from a cell that is being edited... Use the cell editor (or register a listener on the cell editor).
To detect row/column/cell selections and deselections... Use a selection listener as described in Detecting User Selections.
To detect mouse events on a column header... Register the appropriate type of mouse listener on the table'sJTableHeader object. (See TableSorter.java for an example.)
To detect other events... Register the appropriate listener on the JTable object.

 

 

(9)使用自定義渲染器

    這節的內容將告訴你如何建立和指定一個 cell renderer。你可使用 JTable 的 setDefaultRenderer 方法設置一個類型明確的 cell renderer。使用 TableColumn 的 setCellRenderer 方法,能夠指定某列中的單元格使用的 renderer。你甚至能夠經過建立 JTable 的子類來指定 cell-specific renderer(針對某個單元格的renderer)。

 

    經過默認的renderer, DefaultTableCellRenderer,很容易自定義 text 和 image renderer。你只須要建立一個子類,實現 setValue 方法,這樣它就會調用 setText(合適的字符串參數) 或 setIcon(合適的圖像)。例如,這裏給出默認的 date renderer 的實現:

Java代碼   收藏代碼
  1. static class DateRenderer extends DefaultTableCellRenderer {  
  2.     DateFormat formatter;  
  3.     public DateRenderer() { super(); }  
  4.   
  5.     public void setValue(Object value) {  
  6.         if (formatter==null) {  
  7.             formatter = DateFormat.getDateInstance();  
  8.         }  
  9.         setText((value == null) ? "" : formatter.format(value));  
  10.     }  
  11. }  

    若是隻是繼承 DefaultTableCellRenderer 是不夠的,你可使用另一個超類來構建 renderer。最簡單的方法就是建立一個存在的空間的子類,讓該子類實現 TableCellRenderer 接口。 TableCellRenderer 只要求一個方法: getTableCellRendererComponent。這個方法的實現了 創建 渲染組件 繪製具體的狀態,而後返回這個組件。

 

   在下面的 TableDialogEditDemo.java 的截圖中, 用於處理列 Favorite Color一欄的單元格的 renderer,是 JLabel 的子類,名爲ColorRenderer。


    這裏引用 ColorRenderer.java 中的代碼:

Java代碼   收藏代碼
  1. public class ColorRenderer extends JLabel  
  2.                            implements TableCellRenderer {  
  3.     ...  
  4.     public ColorRenderer(boolean isBordered) {  
  5.         this.isBordered = isBordered;  
  6.         setOpaque(true); //MUST do this for background to show up.  
  7.     }  
  8.   
  9.     public Component getTableCellRendererComponent(  
  10.                             JTable table, Object color,  
  11.                             boolean isSelected, boolean hasFocus,  
  12.                             int row, int column) {  
  13.         Color newColor = (Color)color;  
  14.         setBackground(newColor);  
  15.         if (isBordered) {  
  16.             if (isSelected) {  
  17.                 ...  
  18.                 //selectedBorder is a solid border in the color  
  19.                 //table.getSelectionBackground().  
  20.                 setBorder(selectedBorder);  
  21.             } else {  
  22.                 ...  
  23.                 //unselectedBorder is a solid border in the color  
  24.                 //table.getBackground().  
  25.                 setBorder(unselectedBorder);  
  26.             }  
  27.         }  
  28.           
  29.         setToolTipText(...); //Discussed in the following section  
  30.         return this;  
  31.     }  
  32. }  

 

    下面這句代碼是TableDialogEditDemo.java 中註冊 ColorRender實例爲 全部 Color 類數據的 默認 renderer。    

Java代碼   收藏代碼
  1. table.setDefaultRenderer(Color.class, new ColorRenderer(true));  

 

    要指定一個 cell-specific renderer,你須要定義一個 JTable 子類,覆蓋 getCellRenderer 方法。例如,下面代碼指定第一列第一個單元格使用一個自定義的 renderer:

Java代碼   收藏代碼
  1. TableCellRenderer weirdRenderer = new WeirdRenderer();  
  2. table = new JTable(...) {  
  3.     public TableCellRenderer getCellRenderer(int row, int column) {  
  4.         if ((row == 0) && (column == 0)) {  
  5.             return weirdRenderer;  
  6.         }  
  7.         // else...  
  8.         return super.getCellRenderer(row, column);  
  9.     }  
  10. };  

 

(10)爲單元格指定提示工具

    默認狀況下,tool tip text(提示文本) 是否展現取決於單元格的 renderer。不過,有時候能夠經過覆蓋 JTable 的 getToolTipText(MouseEvent) 方法來指定 tool tip text。這節將告訴你這兩種技術:

 

    使用單元格的 renderer 增長文本提示,首先你要得到或建立一個 cell renderer。而後,在確保 這個 rendering component 是一個 JComponent後,調用 setToolTipText。(以前的ColorRender 繼承了 JLabel,因此它是個JComponent,同時它也實現了TableCellRenderer,因此它是一個 rendering component)

 

    TableRenderDemo.java.的源代碼。它對 Sport 列 增長了文本提示:

Java代碼   收藏代碼
  1. //Set up tool tips for the sport cells.  
  2. DefaultTableCellRenderer renderer =  
  3.         new DefaultTableCellRenderer();  
  4. renderer.setToolTipText("Click for combo box");  
  5. sportColumn.setCellRenderer(renderer);  

    雖然這個文本提示設置是靜態的,可是你能夠實現 依賴於單元格或者程序的 動態文本提示(前面的ColorRender中有關tool tip 的設置也是一種方法):

  • 在renderer 實現的 getTableCellRendererComponent 方法中增長一點代碼
  • 覆蓋 JTable 的 getToolTipText(MouseEvent)方法。

 

    TableDialogEditDemo 對Color類型欄使用一個renderer,見 ColorRenderer.java, 粗體部分爲設置tool tip text 部分的代碼:

Java代碼   收藏代碼
  1. public class ColorRenderer extends JLabel   
  2.                            implements TableCellRenderer {  
  3.     ...  
  4.     public Component getTableCellRendererComponent(  
  5.                             JTable table, Object color,  
  6.                             boolean isSelected, boolean hasFocus,  
  7.                             int row, int column) {  
  8.         Color newColor = (Color)color;  
  9.         ...  
  10.         setToolTipText("RGB value: " + newColor.getRed() + ", "  
  11.                                      + newColor.getGreen() + ", "  
  12.                                      + newColor.getBlue());  
  13.         return this;  
  14.     }  
  15. }  

 

   tool tip的效果以下:


    你能夠經過覆蓋 JTable 的 getToolTipText(MouseEvent)方法指定 tool tip text。

 

    這個demo設置了 Sport 和 Vegetarian欄中的單元格給出文本提示:



 
TableToolTipsDemo.java 中實現了Sport 和 VegeTarian 欄中單元格給出文本提示的代碼以下:

Java代碼   收藏代碼
  1. JTable table = new JTable(new MyTableModel()) {      
  2.     //Implement table cell tool tips.  
  3.     public String getToolTipText(MouseEvent e) {  
  4.         String tip = null;  
  5.         java.awt.Point p = e.getPoint();  
  6.         int rowIndex = rowAtPoint(p);  
  7.         int colIndex = columnAtPoint(p);  
  8.         int realColumnIndex = convertColumnIndexToModel(colIndex);  
  9.   
  10.         if (realColumnIndex == 2) { //Sport column  
  11.             tip = "This person's favorite sport to "  
  12.                    + "participate in is: "  
  13.                    + getValueAt(rowIndex, colIndex);  
  14.   
  15.         } else if (realColumnIndex == 4) { //Veggie column  
  16.             TableModel model = getModel();  
  17.             String firstName = (String)model.getValueAt(rowIndex,0);  
  18.             String lastName = (String)model.getValueAt(rowIndex,1);  
  19.             Boolean veggie = (Boolean)model.getValueAt(rowIndex,4);  
  20.             if (Boolean.TRUE.equals(veggie)) {  
  21.                 tip = firstName + " " + lastName  
  22.                       + " is a vegetarian";  
  23.             } else {  
  24.                 tip = firstName + " " + lastName  
  25.                       + " is not a vegetarian";  
  26.             }  
  27.   
  28.         } else { //another column  
  29.             //You can omit this part if you know you don't   
  30.             //have any renderers that supply their own tool   
  31.             //tips.  
  32.             tip = super.getToolTipText(e);  
  33.         }  
  34.         return tip;  
  35.     }  
  36.     ...  
  37. }  

 

   除了 converColumnIndexToModel 的調用意外,這段代碼很容易明白。這個方法是必需的,由於用戶可能在界面上移動了某些列,視圖上的某列索引並不匹配 table model 中該列的索引,而數據處理是在 table model 上操做的,因此要得到對應於 table model 中該列的索引。

 

(11)爲列頭指定工具集

    你能夠經過設置 table 的 JTableHeader 對象,增長列頭的文本提示。不一樣的列頭經常須要不一樣的文本提示,你能夠覆蓋 table header 的 getToolTipText 方法來改變提示文本。你也能夠 調用 TableColumn.setHeaderRenderer, 對 header 指定自定義的 renderer.

 

    TableToolTipsDemo.java 中也有根據不一樣列顯示不一樣列頭文本提示的例子,以下圖,當你將鼠標移動到後三列的列頭上時,將顯示提示文本。而前兩列的列頭未提供文本提示(名字已經充分說明這列數據,無需別的提示說明),如下是功能截圖:


    下面代碼實現了上面的文本提示功能。建立一個 JTableHeader 子類,覆蓋 getToolTipText(MouseEvent)方法,這樣就能對當前列返回文本。要與 table 關聯這個修訂過的 header,使用 JTable 的 createDefaultTableHeader 方法,返回一個 JTableHeader 子類實例。

Java代碼   收藏代碼
  1. protected String[] columnToolTips = {  
  2.     null, // "First Name" assumed obvious  
  3.     null, // "Last Name" assumed obvious  
  4.     "The person's favorite sport to participate in",  
  5.     "The number of years the person has played the sport",  
  6.     "If checked, the person eats no meat"};  
  7. ...  
  8.   
  9. JTable table = new JTable(new MyTableModel()) {  
  10.     ...  
  11.   
  12.     //Implement table header tool tips.  
  13.     protected JTableHeader createDefaultTableHeader() {  
  14.         return new JTableHeader(columnModel) {  
  15.             public String getToolTipText(MouseEvent e) {  
  16.                 String tip = null;  
  17.                 java.awt.Point p = e.getPoint();  
  18.                 int index = columnModel.getColumnIndexAtX(p.x);  
  19.                 int realIndex =   
  20.                         columnModel.getColumn(index).getModelIndex();  
  21.                 return columnToolTips[realIndex];  
  22.             }  
  23.         };  
  24.     }  
  25. };  

 

    提醒:(有關單元格或列頭文本提示)上面代碼,getToolTipText(MouseEvent e) 和 createDefaultTableHeader 方法都是 JTable 的方法。用了不少匿名類的寫法,要注意看仔細。

 

(12)排序和過濾

    表格 sorting 和 filtering 是由 一個 sorter 對象管理的。得到一個 sorter 對象的最簡單方法是設置 autoCreateRowSorter 綁定屬性 爲true:

Java代碼   收藏代碼
  1. JTable table = new JTable();  
  2. table.setAutoCreateRowSorter(true);  

 

    這段代碼定義了一個 row sorter,他是 javax.swing.table.TableRowSorter 的實例。當用戶點擊某列列頭時,表格會作一個 locale-specific sort。 TableSortDemo.java,例子的截圖:


    你能夠構造一個 TableRowSorter 實例,而後指定它爲你的 table 的sorter,這樣你就能得到更多的分類控制。

Java代碼   收藏代碼
  1. TableRowSorter<TableModel> sorter   
  2.     = new TableRowSorter<TableModel>(table.getModel());  
  3. table.setRowSorter(sorter);  

 

    TableRowSorter 使用 java.util.Comparator (實現了該接口的)對象來排序。實現該接口,必須提供一個名爲 compare 的方法,該方法定義兩個了兩個對象的比較值,用於排序。例如,下面代碼建立了一個 Comparator,根據字符串最後一個單詞來排序。(String實現了Comparable接口)

Java代碼   收藏代碼
  1. Comparator<String> comparator = new Comparator<String>() {  
  2.     public int compare(String s1, String s2) {  
  3.         String[] strings1 = s1.split("\\s");  
  4.         String[] strings2 = s2.split("\\s");  
  5.         return strings1[strings1.length - 1]  
  6.             .compareTo(strings2[strings2.length - 1]);  
  7.     }  
  8. };  

 

    這個例子太簡單了,更具典型意義的是,實現了Comparator接口的類,同時也是  java.text.Collator.的子類,你能夠定義本身的子類,或者使用 Collator 的工廠方法,得到一個支持本地語言的 Comparator,又或是使用 java.text.RuleBasedCollator. ,該類是 Collator 的具體子類。

 

    爲了肯定某一列使用哪一個 Comparator, TableRowSorter 嘗試輪流使用一些規則規則。這些規則按順序的列在下面;第一條規則爲 sorter 提供了一個 Comparator,……:

  1. 若是經過調用 setComparator指定了一個比較器,則使用該比較器
  2. 若是這個 table model 反饋,某一列是由字符串組成的(TableModel.getColumnClass 返回 String.class),則使用一個基於當前本地配置的比較器用於字符串排序。
  3. 若是 TableModel.getColumnClass 返回的類型實現了 Comparable,則基於 Comparable.compareTo.返的值對字符串排序。
  4. 若是經過調用 setStringConverter 爲 table 指定一個字符串轉換器,則對象轉換所得的字符串值表明對象,參加基於本地語言的排序。
  5. 若是以前的幾條規則都沒被採用,則使用一個比較器,該比較器會對列上的數據對象調用 toString,而後根據返回的字符串進行基於本地語言的排序。

    對於更復雜的排序,查閱TableRowSorter 和他的父類 javax.swing.DefaultRowSorter.

 

    調用 setSortKeys ,指定排序規則和優先排序。(有關」鍵「的概念,對於某一列,使用比較器排序時,沒法得出某幾行的順序時,則按照鍵列表中的鍵順序,根據這些鍵,其實就是列,在原來基礎上再次此對這幾行排序…………你懂的)這裏有一個有一個根據前兩列排序的例子。哪一列優先排序是取決於」排序鍵列表「中的「排序鍵」順序。在這個例子中,第二列是第一排序鍵,因此根據第二列優先排序,而後再根據第一列排序:

Java代碼   收藏代碼
  1. List <RowSorter.SortKey> sortKeys   
  2.     = new ArrayList<RowSorter.SortKey>();  
  3. sortKeys.add(new RowSorter.SortKey(1, SortOrder.ASCENDING));  
  4. sortKeys.add(new RowSorter.SortKey(0, SortOrder.ASCENDING));  
  5. sorter.setSortKeys(sortKeys);   

 

    除了對結果集二次排序外,一個 table sorter 能夠指定過濾器,讓哪些行不顯示。TableRowSorter 使用javax.swing.RowFilter 對象實現過濾功能。 RowFilter 實現了幾個工廠方法,能夠建立集中經常使用的 filter。例如regexFilter 方法返回一個基於 regular expression.(正則表達式)的 RowFilter。

 

    在下面的例子代碼中,明確的建立了一個 sorter 對象,接着能夠給它指定一個 filter:

Java代碼   收藏代碼
  1. MyTableModel model = new MyTableModel();  
  2. sorter = new TableRowSorter<MyTableModel>(model);  
  3. table = new JTable(model);  
  4. table.setRowSorter(sorter);  

 

    接着你基於當前文本值進行過濾:

Java代碼   收藏代碼
  1. private void newFilter() {  
  2.     RowFilter<MyTableModel, Object> rf = null;  
  3.     //If current expression doesn't parse, don't update.  
  4.     try {  
  5.         rf = RowFilter.regexFilter(filterText.getText(), 0);  
  6.     } catch (java.util.regex.PatternSyntaxException e) {  
  7.         return;  
  8.     }  
  9.     sorter.setRowFilter(rf);  
  10. }  

 

    filterText 文本框的值每次改變時,newFilter() 都會被調用。try catch 防止了用戶在界面的文本框中輸進錯誤的正則表達式。

 

    當一個 table 使用一個 sorter 時,用戶看到的數據順序可能跟 data model指定的順序不同,也許沒有包含 data model 指定的全部行。 用戶真正看到的數據被稱爲 「view」,擁有本身的一套座標。 JTable 提供了方法用於轉換 model 座標 到 view 座標——convertColumnIndexToView and convertRowIndexToView 方法。固然也提供了model 到 view 的轉換——convertColumnIndexToModel and convertRowIndexToModel.

 

    提醒:每次使用 sorter 時,記得轉換單元格的索引,數據真正是要在 model 上處理的。

 

    下面代碼整合這節所討論的技術。

    TableFilterDemo.java 對 TableDemo 作了一些修改,包括前面提到的一些代碼。這個例子給 table 提供了一個 sorter ,使用一個文本框提供過濾的正則表達式。下面是截圖是:未排序 和 未過濾,注意,model中的第三行依然爲view 的第三行。


    若是用戶點擊第二列兩次,第四行就會變成第一行——這只是view的改變,model中的列順序沒改變。


    如前面所描述的同樣,用戶向「Filter Text」文本域輸入正則表達式,符合這些表達式的行將被顯示。跟排序同樣,過濾也是產生 view,與 mode 分離。


    下面的代碼,根據當前 selection 更新 status 文本框:

Java代碼   收藏代碼
  1. table.getSelectionModel().addListSelectionListener(  
  2.         new ListSelectionListener() {  
  3.             public void valueChanged(ListSelectionEvent event) {  
  4.                 int viewRow = table.getSelectedRow();  
  5.                 if (viewRow < 0) {  
  6.                     //Selection got filtered away.  
  7.                     statusText.setText("");  
  8.                 } else {  
  9.                     int modelRow =   
  10.                         table.convertRowIndexToModel(viewRow);  
  11.                     statusText.setText(  
  12.                         String.format("Selected Row in view: %d. " +  
  13.                             "Selected Row in model: %d.",   
  14.                             viewRow, modelRow));  
  15.                 }  
  16.             }  
  17.         }  
  18. );  

 

(13)使用combo box做爲編輯器

    讓 combo box 做爲一個 editor 是簡單的,下面粗體部分的代碼,指定某列的editor 爲一個 combo box

Java代碼   收藏代碼
  1. TableColumn sportColumn = table.getColumnModel().getColumn(2);  
  2. ...  
  3. JComboBox comboBox = new JComboBox();  
  4. comboBox.addItem("Snowboarding");  
  5. comboBox.addItem("Rowing");  
  6. comboBox.addItem("Chasing toddlers");  
  7. comboBox.addItem("Speed reading");  
  8. comboBox.addItem("Teaching high school");  
  9. comboBox.addItem("None");  
  10. sportColumn.setCellEditor(new DefaultCellEditor(comboBox));  

 

    效果圖:


(14)使用其餘編輯器

    不管你是設置一個列的 editor (使用 TableColumn.setCellEditor 方法),仍是爲一個數據類型設置 eidtor (使用 JTable.setDefaultEditor 方法),你能夠指定你個實現了TableCellEditor 的類做爲 editor。幸運的是 DefaultCellEditor 類實現了這個藉口,而且提供了參數爲編輯組件(JTextField,JCheckBox 或 JComboBox)的構造函數。一般你不須要明確的指定一個check box 爲 editor,由於 Boolean 類型的數據自動使用 單選框 render 和 editor。

 

    你的單元格 ediotr 類須要定義至少兩個方法—— getCellEditorValue 和 getTableCellEditorComponent。getCellEditorValue 返回單元格當前值, 該方法是 CellEditor 接口要求的; getTableCellRendererComponent 返回你想要用做編輯器的組件,該方法是 TableCellEditor 接口要求的。

 

    下面截圖包含一個表格和一個對話框,表格使用對話框間接地做爲單元格編輯器。當用戶開始編輯 Favorite Color 列是,呈現一個按鈕(真正的cell editor),帶出對話框,讓用戶選擇不一樣的顏色。


    下面是 ColorEditor.java, 中的代碼:

Java代碼   收藏代碼
  1. public class ColorEditor extends AbstractCellEditor  
  2.                          implements TableCellEditor,  
  3.                                     ActionListener {  
  4.     Color currentColor;  
  5.     JButton button;  
  6.     JColorChooser colorChooser;  
  7.     JDialog dialog;  
  8.     protected static final String EDIT = "edit";  
  9.   
  10.     public ColorEditor() {  
  11.         button = new JButton();  
  12.         button.setActionCommand(EDIT);  
  13.         button.addActionListener(this);  
  14.         button.setBorderPainted(false);  
  15.   
  16.         //Set up the dialog that the button brings up.  
  17.         colorChooser = new JColorChooser();  
  18.         dialog = JColorChooser.createDialog(button,  
  19.                                         "Pick a Color",  
  20.                                         true,  //modal  
  21.                                         colorChooser,  
  22.                                         this,  //OK button handler  
  23.                                         null); //no CANCEL button handler  
  24.     }  
  25.   
  26.     public void actionPerformed(ActionEvent e) {  
  27.         if (EDIT.equals(e.getActionCommand())) {  
  28.             //The user has clicked the cell, so  
  29.             //bring up the dialog.  
  30.             button.setBackground(currentColor);  
  31.             colorChooser.setColor(currentColor);  
  32.             dialog.setVisible(true);  
  33.   
  34.             fireEditingStopped(); //Make the renderer reappear.  
  35.   
  36.         } else { //User pressed dialog's "OK" button.  
  37.             currentColor = colorChooser.getColor();  
  38.         }  
  39.     }  
  40.   
  41.     //Implement the one CellEditor method that AbstractCellEditor doesn't.  
  42.     public Object getCellEditorValue() {  
  43.         return currentColor;  
  44.     }  
  45.   
  46.     //Implement the one method defined by TableCellEditor.  
  47.     public Component getTableCellEditorComponent(JTable table,  
  48.                                                  Object value,  
  49.                                                  boolean isSelected,  
  50.                                                  int row,  
  51.                                                  int column) {  
  52.         currentColor = (Color)value;  
  53.         return button;  
  54.     }  
  55. }  

 

    這段代碼很簡單。比較難懂的是 fireEditingStopped。若是沒有這句,editor 保持激活,即使對話框再也不可視。它的調用讓 table 知道 editor 已經能夠無效,讓 renderer 來處理單元格。

 

(15)使用編輯器驗證用戶的文本輸入

    若是一個單元格默認的 editor 容許空文本, 當文本不是你指定的某些東西,你想得到錯誤提示。錯誤檢查伴隨發生在輸入的文本轉換爲合適類型的對象的時候。

 

    當默認editor視圖建立一個關聯單元格列的 class 實例時,這種對用戶輸入的字符串自動檢測就會發生。這個默認的 editor 使用字符串爲參數的夠着函數建立該實例。例如,在單元格的數據類型爲Integer的列中,當用戶敲入「123」,默認的 editor 建立對應的Integer類型,使用等同於 new Integer(「123」)。若是構造器拋出一個異常,單元格的outline變成紅,該單元格不容許失去聚焦。若是你的列數據類型對應的 class 可使用字符串參數構造實例,則能夠是哦那個默認的 editor 完成檢測。

 

    若是你喜歡用 文本域 做爲單元格的 editor,又想自定義某些檢測方式,或是定義發現錯誤時的不一樣表現。你可使用 formatted text field. ,formatted text field 能夠檢測在輸入期間或者輸入完成時檢測是否錯誤。

 

    例子 TableFTFEditDemo.java,,創建一個 formatted text field 做爲 editor。限制全部整數值只能爲 0~100之間:

    下面這句代碼使 formatted text field 成爲全部包含 Integer 類型數據的列的 editor。IntegerEditor.java查看

Java代碼   收藏代碼
  1. table.setDefaultEditor(Integer.class,  
  2.                        new IntegerEditor(0, 100));  

 

    IntegerEditor 是 DefaultCellEditor 的子類。使用 DefaultCellEditor 支持的 JFormattedTextField 代替 JTextField。使用 integer format 創建一個 formatted text field,而後指定最大最小值。參考  How to Use Formatted Text Fields. 接着覆蓋 DefaultCellEditor 要求的 getCellEditorValue 和 stopCellEditing 方法

 

    getTableCellEditorComponent 這個覆蓋的方法,在 editor 現實以前,正確的設置 formatted text field 的值(不是簡單的繼承JTextField的值)。 getCellEditorValue方法則保持 單元格的值爲 一個Integer,而不是 formatted text field 試圖返回的long或者之類的。最後 stopCellEditing 是的能夠檢測文本是否合法,可能防止了 editor 被解除。若是文本不合法,你的 stopCellEditing 給出對話框,讓用戶選擇是繼續編輯仍是會退到最後輸入的合法值。代碼太長,請查看:IntegerEditor.java

(16)打印

    JTable 提供了一個簡單的 API 用於打印表格。打印表格的最簡單的方法就是直接調用無參數的 JTable.print

Java代碼   收藏代碼
  1. try {  
  2.     if (! table.print()) {  
  3.         System.err.println("User cancelled printing");  
  4.     }  
  5. catch (java.awt.print.PrinterException e) {  
  6.     System.err.format("Cannot print %s%n", e.getMessage());  
  7. }  

 

    在一個標準Swing應用程序中,調用 print 方法會彈出一個標準的打印對話框。返回值指示用戶繼續仍是取消打印做業。 JTable.print 可能拋出 java.awt.print.PrinterException  ,是check Exception,因此要trycatch

 

    JTable 提供了多種 print 的重載。  來自 TablePrintDemo.java 的代碼戰士如何定義一個 page header:

Java代碼   收藏代碼
  1. MessageFormat header = new MessageFormat("Page {0,number,integer}");  
  2. try {  
  3.     table.print(JTable.PrintMode.FIT_WIDTH, header, null);  
  4. catch (java.awt.print.PrinterException e) {  
  5.     System.err.format("Cannot print %s%n", e.getMessage());  
  6. }  

 

    有關更復雜的打印應用,使用 JTable.getPrintable得到一個 Printable 對象。有關 Printable 的內容,參考refer to the Printing lesson in the 2D Graphics trail.

(17)例子列表

Example Where Described Notes

SimpleTableDemo Creating a Simple Table A basic table with no custom model. Does not include code tospecify column widths or detect user editing.
SimpleTable- 
SelectionDemo
Detecting User Selections Adds single selection and selection detection to SimpleTableDemo. By modifying the program's ALLOW_COLUMN_SELECTION andALLOW_ROW_SELECTION constants, you can experiment with alternatives to the table default of allowing only rows to be selected.
TableDemo Creating a Table Model A basic table with a custom model.
TableFTFEditDemo Using an Editor to Validate User-Entered Text Modifies TableDemo to use a custom editor (a formatted text field variant) for all Integer data.
TableRenderDemo Using a Combo Box as an Editor Modifies TableDemo to use a custom editor (a combo box) for all data in the Sport column. Also intelligently picks column sizes. Uses renderers to display tool tips for the sport cells.
TableDialogEditDemo Using Other Editors Modifies TableDemo to have a cell renderer and editor that display a color and let you choose a new one, using a color chooser dialog.
TableToolTipsDemo Specifying Tool Tips for Cells,Specifying Tool Tips for Column Headers, Demonstrates how to use several techniques to set tool tip text for cells and column headers.
TableSortDemo Sorting and Filtering Demonstrates the default sorter, which allows the user to sort columns by clicking on their headers.
TableFilterDemo Sorting and Filtering Demonstrates sorting and filtering, and how this can cause the view coordinates to diverge from the model coordinates.
TablePrintDemo Printing Demonstrates table printing.
ListSelectionDemo How to Write a List Selection Listener Shows how to use all list selection modes, using a list selection listener that's shared between a table and list.
SharedModelDemo Nowhere Builds on ListSelectionDemo making the data model be shared between the table and list. If you edit an item in the first column of the table, the new value is reflected in the list.
TreeTable, TreeTable II Creating TreeTables in SwingCreating TreeTables: Part 2 Examples that combine a tree and table to show detailed information about a hierarchy such as a file system. The tree is a renderer for the table.
相關文章
相關標籤/搜索