JTable的應用(二)


目錄

  1. 如何使用Table
  2. (1)創建一個簡單的表格
  3. (2)向容器添加表格
  4. (3)改變每每一列的寬度
  5. (4)用戶選擇
  6. (5)創建表格模型
  7. (6)監聽數據改變
  8. (7)點燃數據改變事件
  9. (8)概念:編輯器和渲染器(Editors and Renderers)
  10. (9)使用自定義渲染器
  11. (10)為單元格指定提示工具
  12. (11)為列頭指定工具集
  13. (12)排序和過濾
  14. (13)使用combo box作為編輯器
  15. (14)使用其他編輯器
  16. (15)使用編輯器驗證用戶的文本輸入
  17. (16)打印
  18. (17)例子列表
 

如何使用Table

    利用 JTable 類,可以以表格的形式展示數據,可設置允許用戶編輯數據。JTable 本身不擁有或者緩存數據;它只是數據的視圖。這里有一個放在滾動面板上的典型表格:



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

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

(2)向容器添加表格

(3)改變每每一欄的寬度

(4)用戶選擇

(5)創建表格模型

(6)監聽數據改變

(7)點燃數據改變事件

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

(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.classnew 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(0100));  

 

    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.


免責聲明!

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



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