Swing-JTable用法-入門


注:本文為學習筆記,原文為How to Use Tables,本文所有素材與代碼均源於原文,可能會有部分更改。

JTable是Swing中的表格控件,它的外觀如下所示:

沒錯,excel或者access數據庫的編輯區就是JTable這樣的控件了。

創建JTable

JTable提供了2個構造器可以讓你用數據和頭部直接生成它:

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

這兩個構造器有一些特性你必須要注意:

1.JTable所有的單元格都是可編輯的;

2.它將所有數據都當做string來處理。本來,JTable可以將布爾型數據用一個checkBox來進行展示,就像表1那樣,在這里就不行了。

3.它要求你把所有數據都放到數組或vector中。如果你的數據來自於數據庫,那么專門再填充到數組實在是多此一舉。

吶,如果你不能忍受以上限制,那么就使用table Model來管理你的數據吧!

創建TableModel

如果程序沒有顯式地指定tableModel,JTable會自動生成一個 DefaultTableModel實例,這樣做的副作用在上面已經說過了。我們自己創建tableModel可以讓數據得到更好的展示,這樣做的方法是繼承AbstractTableModel。它已經提供了tableModel接口的大部分默認實現,在最低限度下,你只需要實現以下三個方法:

public int getRowCount();

public int getColumnCount();

public Object getValueAt(int row, int column);

當然,如果你的應用程序有其他定制化的功能,你可以自己實現AbstractTableModel的其它方法,比如:

public Class getColumnClass(int c)//JTable uses this method to determine the default renderer editor for each cell

public boolean isCellEditable(int row, int col)//Don't need to implement this method unless your table's editable

public void setValueAt(Object value, int row, int col) //Don't need to implement this method unless your table's data can change.

TableDemo.java示意了如何自己創建並使用tableModel。 

將表格添加到容器中

一般情況下我們都是將JTable放到JScrollPane中,從而使用它的滾動功能。JScrollPane會很貼心地把表頭放在表格上方,並在向下滑動時始終保持它可見。如果你就是不要用JScrollPane,那么記得在容器中添加表頭哦~就像下面這樣

container.setLayout(new BorderLayout());
container.add(table.getTableHeader(), BorderLayout.PAGE_START);
container.add(table, BorderLayout.CENTER);

設置和更改列寬

設置列寬,直接上代碼:

TableColumn column = null;
for (int i = 0; i < 5; i++) {
    column = table.getColumnModel().getColumn(i);
    if (i == 2) {
        column.setPreferredWidth(100); //third column is bigger
    } else {
        column.setPreferredWidth(50);
    }
}

當你手動調整列寬時,其它列的寬度也會自動調整,因為窗體尺寸沒變。

用戶選擇

與JList一樣,JTable也支持三種選擇模式:

  • 單獨選擇:SINGLE_SELECTION
  • 單重連續選擇:SINGLE_INTERVAL_SELECTION
  • 多重連續選擇:MULTIPLE_INTERVAL_SELECTION

具體來說,你還可以設置是否允許選擇行、選擇列或選擇單元格。需要注意的是,行、列選擇與單元格選擇會相互影響的。

1.在MULTIPLE_INTERVAL_SELECTION模式下:

選擇單元格被永遠禁止;選擇行、選擇列相互排斥,要么選擇若干行,要么選擇若干列;

2.在SINGLE_INTERVAL_SELECTION模式下:

禁止選擇單元格時,選擇行、選擇列相互排斥,要么選擇連續行,要么選擇連續列。

啟用選擇單元格時,三者必須同時被選中,此時單元格可被單獨或連續選中。

3.在SINGLE_SELECTION模式下:

禁止選擇單元格時,選擇行、選擇列相互排斥,要么選擇一行,要么選擇一列。

啟用選擇單元格時,三者必須同時被選中,每次只能選擇一個單元格。

注意,在JTable中無法同時選擇獨立的多個單元格,因為其Selection模型非常簡單,就是取行與列的交集。

對於已選擇的行、列的提取,使用JTable.getSelectedRowsJTable.getSelectedRows來提取它們的index。而lead selection的提取則有點違反直覺,代碼如下:

String.format("Lead Selection: %d, %d. ",
    table.getSelectionModel().getLeadSelectionIndex(),
    table.getColumnModel().getSelectionModel().getLeadSelectionIndex());

監聽數據變更

為了監聽數據的變更,你需要調用model的addTableModelListener方法來添加一個監聽器,而這個監聽器必須實現了TableModelListener接口。該接口只有一個void tableChanged(TableModelEvent e)方法,你需要在里面進行響應。TableModelEvent將提供必要的信息,來指示發生變更的位置。它的方法有:

int getColumn()//Returns the column for the event.

int getFirstRow()//Returns the first row that changed.

int getLastRow()//Returns the last row that changed.

int getType()//Returns the type of event - one of: INSERT, UPDATE and DELETE.

得到位置后,你就可以調用model的getValueAt方法來獲取最新值。

觸發數據變更事件

為了觸發數據變更事件,model必須知道如何創建數據變更事件。雖然這個過程很復雜,但是DefaultTableModel已經實現了。所以,你要么使用JTable默認的DefaultTableModel,要么自己繼承DefaultTableModel。吶,如果你認為DefaultTableModel不合適而自己繼承了 AbstractTableModel,那么你就得自己動動手啦!當數據被外部源改變時,你需要激活以下方法:

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.

渲染器與編輯器

渲染器決定了單元格內容的展現形式,而編輯器決定單元格內容被編輯的方式。比如,默認情況下,數字使用右對齊的JLabel來展示,布爾型變量使用單選控件來展示;而你在編輯某些列時,可能希望從下拉菜單中選擇內容,這就是編輯器的作用了。出於性能上的考慮,JTable並沒有為每一個單元格提供獨立的渲染器,而是根據數據類型來渲染的。JTable首先會檢查該列是否指定了渲染器,沒有的話就檢查該列的數據類型,並查看對應的渲染器。而基礎類型以外的對象基本都是調用其toString方法,並通過JLable來渲染的。編輯器也是一樣。

當然,我們可以對渲染器進行定制,來滿足特定的需求。對指定的列或指定的數據類型應用定制渲染器都可以。如果是前者你需要調用JTable的setDefaultRenderer方法;如果是后者你需要調用指定列的setCellRenderer方法。如果你要為特定的單元格指定渲染器,那你需要調用繼承JTable並重載getCellRenderer 方法。構造渲染器的最簡單方法是繼承DefaultTableCellRenderer類,然后實現它的setValue方法,你在里面使用setText或者setIcon來定制你渲染的內容。如果這還不夠,那你可以繼承一個已經存在的組件並實現 TableCellRenderer接口,比如讓你的JLable繼承這個接口,然后將它作為渲染器使用。

Table Render Demo Project是一個定制渲染器與編輯器的絕好例子,運行效果如下:

單元格提示(Tool tips for Cells)

單元格提示效果如上圖,鼠標懸停時可顯示出定制內容。在默認情況下,它是由單元格的渲染器決定的;然而,你也可以重載JTable的getToolTipText(MouseEvent)實現。下面分別介紹這兩種方法。

(1)為單元格渲染器添加提示,首先需要建立一個渲染器,確認它是一個JComponent,然后用它調用setToolTipText即可。以下代碼來自於TableRenderDemo.java

//Set up tool tips for the sport cells.
DefaultTableCellRenderer renderer =
        new DefaultTableCellRenderer();
renderer.setToolTipText("Click for combo box");
sportColumn.setCellRenderer(renderer);

以下代碼來自於 ColorRenderer.java

public class ColorRenderer extends JLabel 
                           implements TableCellRenderer {
    ...
    public Component getTableCellRendererComponent(
                            JTable table, Object color,
                            boolean isSelected, boolean hasFocus,
                            int row, int column) {
        Color newColor = (Color)color;
        ...
        setToolTipText("RGB value: " + newColor.getRed() + ", "
                                     + newColor.getGreen() + ", "
                                     + newColor.getBlue());
        return this;
    }
}

(2)重載JTable的getToolTipText(MouseEvent)

在該方法中,找到指定列,然后返回指定的字符串。以下代碼來自於TableToolTipsDemo.java

JTable table = new JTable(new MyTableModel()) {    
    //Implement table cell tool tips.
    public String getToolTipText(MouseEvent e) {
        String tip = null;
        java.awt.Point p = e.getPoint();
        int rowIndex = rowAtPoint(p);
        int colIndex = columnAtPoint(p);
        int realColumnIndex = convertColumnIndexToModel(colIndex);

        if (realColumnIndex == 2) { //Sport column
            tip = "This person's favorite sport to "
                   + "participate in is: "
                   + getValueAt(rowIndex, colIndex);

        } else if (realColumnIndex == 4) { //Veggie column
            TableModel model = getModel();
            String firstName = (String)model.getValueAt(rowIndex,0);
            String lastName = (String)model.getValueAt(rowIndex,1);
            Boolean veggie = (Boolean)model.getValueAt(rowIndex,4);
            if (Boolean.TRUE.equals(veggie)) {
                tip = firstName + " " + lastName
                      + " is a vegetarian";
            } else {
                tip = firstName + " " + lastName
                      + " is not a vegetarian";
            }

        } else { //another column
            //You can omit this part if you know you don't 
            //have any renderers that supply their own tool 
            //tips.
            tip = super.getToolTipText(e);
        }
        return tip;
    }
    ...
}

運行效果如下圖:

表頭提示

通常表頭中的不同列都具有不同的文字提示。你可以通過重載表頭的getToolTipText方法來改變文字提示,也可以調用TableColumn.setHeaderRenderer來為表頭提供一個定制的渲染器。

以下代碼來自於TableSorterDemo.java,它為所有列頭提供了相同的文字提示:

table.getTableHeader().setToolTipText(
        "Click to sort; Shift-Click to sort in reverse order");

以下代碼來自於TableSorterDemo.java,它為后3列的列頭提供不同的文字提示。

protected String[] columnToolTips = {
    null, // "First Name" assumed obvious
    null, // "Last Name" assumed obvious
    "The person's favorite sport to participate in",
    "The number of years the person has played the sport",
    "If checked, the person eats no meat"};
...

JTable table = new JTable(new MyTableModel()) {
    ...

    //Implement table header tool tips.
    protected JTableHeader createDefaultTableHeader() {
        return new JTableHeader(columnModel) {
            public String getToolTipText(MouseEvent e) {
                String tip = null;
                java.awt.Point p = e.getPoint();
                int index = columnModel.getColumnIndexAtX(p.x);
                int realIndex = 
                        columnModel.getColumn(index).getModelIndex();
                return columnToolTips[realIndex];
            }
        };
    }
};

運行效果如下:

排序和過濾

 


免責聲明!

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



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