Java Swing (JTable詳解1)


好久沒有玩Swing了,算是練習英語,參考Sun公司官方給出的Java Tutorial的教程,來回顧一下JTable的用法,也希望大神來拍磚!

JTable顧名思義就是一個將數據以表格顯示的組件,但是需要特別注意的是:因為Java Swing采用了MVC的設計,所以JTable不是用來存放數據的,它只是用來作為視圖顯示,而真正用來存儲和維護數據的是TableModel這個接口的實現類。

 

 

 

從上面的圖我們可以看出幾個特點:

1.JTable的表頭其實也是一個單獨的組件,TableHeader

2.而每一列也可以被划分出來作為一個組件,TableColumn

3.JTable的數據選項不僅僅是可以顯示字符串,還可以顯示checkbox,img等等,其實只要你懂得如何去重寫它內部的方法,你想怎么顯示就怎么顯示.

好了,廢話不說了,來看

1.第一個例子(這個例子對官方的內容進行了少許變動,可以讓別人更加理解這個屬性的含義是什么)

  1 package org.plx.jtable;
  2 
  3 import java.awt.BorderLayout;
  4 import java.awt.Color;
  5 import java.awt.event.ActionEvent;
  6 import java.awt.event.ActionListener;
  7 import java.awt.event.MouseAdapter;
  8 import java.awt.event.MouseEvent;
  9 
 10 import javax.swing.JButton;
 11 import javax.swing.JFrame;
 12 import javax.swing.JPanel;
 13 import javax.swing.JScrollPane;
 14 import javax.swing.JTable;
 15 
 16 @SuppressWarnings("serial")
 17 public class SimpleTableDemo extends JPanel {
 18     private boolean DEBUG = true;
 19 
 20     public SimpleTableDemo() {
 21         super(new BorderLayout());
 22 
 23         //創建表頭
 24         String[] columnNames = { "First Name", "Last Name", "Sport",
 25                 "# of Years", "Vegetarian" };
 26 
 27         //創建顯示數據
 28         Object[][] data = {
 29                 { "Kathy", "Smith", "Snowboarding", new Integer(5),
 30                         new Boolean(false) },
 31                 { "John", "Doe", "Rowing", new Integer(3), new Boolean(true) },
 32                 { "Sue", "Black", "Knitting", new Integer(2),
 33                         new Boolean(false) },
 34                 { "Jane", "White", "Speed reading", new Integer(20),
 35                         new Boolean(true) },
 36                 { "Joe", "Brown", "Pool", new Integer(10), new Boolean(false) } };
 37         
 38         /*
 39          * JTable還提供了一個重載的構造方法,傳入兩個Vector
 40          * JTable(Vector rowData, Vector columnNames)
 41          * 
 42          */
 43         
 44         final JTable table = new JTable(data, columnNames);
 45     
 46         table.setBackground(Color.YELLOW);
 47         
 48         //table.setPreferredScrollableViewportSize(new Dimension(500, 0));
 49         
 50         if (DEBUG) {
 51             table.addMouseListener(new MouseAdapter() {
 52                 public void mouseClicked(MouseEvent e) {
 53                     printDebugData(table);
 54                 }
 55             });
 56         }
 57 
 58         // Create the scroll pane and add the table to it.
 59         //這也是官方建議使用的方式,否則表頭不會顯示,需要單獨獲取到TableHeader自己手動地添加顯示
 60         JScrollPane scrollPane = new JScrollPane(table);
 61 
 62         add(scrollPane);
 63             
 64         
 65         JPanel panel2 = new JPanel();
 66         this.add(panel2,BorderLayout.SOUTH);
 67         JButton btn1 = new JButton("表格填充整個視圖");
 68         JButton btn2 = new JButton("表格不添加整個視圖(默認不填充)");
 69         panel2.add(btn1);
 70         panel2.add(btn2);
 71         
 72         btn1.addActionListener(new ActionListener() {
 73             @Override
 74             public void actionPerformed(ActionEvent e) {
 75                 //設置表格填充整個視圖,在默認情況下,如果表格的大小小於視圖(窗體),你會發現下面的內容是其他顏色,可以將上面的yellow去掉做個比較
 76                 table.setFillsViewportHeight(true);
 77             }
 78         });
 79         
 80         btn2.addActionListener(new ActionListener() {
 81             @Override
 82             public void actionPerformed(ActionEvent e) {
 83                 table.setFillsViewportHeight(false);
 84             }
 85         });
 86         
 87     }
 88 
 89     private void printDebugData(JTable table) {
 90         int numRows = table.getRowCount();
 91         int numCols = table.getColumnCount();
 92         javax.swing.table.TableModel model = table.getModel();
 93 
 94         System.out.println("Value of data: ");
 95         for (int i = 0; i < numRows; i++) {
 96             System.out.print("    row " + i + ":");
 97             for (int j = 0; j < numCols; j++) {
 98                 System.out.print("  " + model.getValueAt(i, j));
 99             }
100             System.out.println();
101         }
102         System.out.println("--------------------------");
103     }
104 
105     /**
106      * Create the GUI and show it. For thread safety, this method should be
107      * invoked from the event-dispatching thread.
108      */
109     private static void createAndShowGUI() {
110         // Create and set up the window.
111         JFrame frame = new JFrame("SimpleTableDemo");
112         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
113 
114         // Create and set up the content pane.
115         SimpleTableDemo newContentPane = new SimpleTableDemo();
116         newContentPane.setOpaque(true); // content panes must be opaque
117         frame.setContentPane(newContentPane);
118 
119         // Display the window.
120         //frame.pack();
121         frame.setSize(800, 600);
122         frame.setVisible(true);
123     }
124 
125     public static void main(String[] args) {
126         // Schedule a job for the event-dispatching thread:
127         // creating and showing this application's GUI.
128         javax.swing.SwingUtilities.invokeLater(new Runnable() {
129             public void run() {
130                 createAndShowGUI();
131             }
132         });
133     }
134 }

我在這里故意將表格的背景設置成YELLOW,可以讓大家更加看清JTable在默認的情況下當它自身顯示的大小小於容器時,它是不會去填充整個容器的,當設置setFillsViewportHeight為true的時候,表格就會填充整個容器。官方對於設置這個的作用是可以表格更加容易作為被拖拽的目標,但是我至今還沒試過往表格里托東西,事后准備試試。。。

在上面的代碼中還有一點需要注意的是:表格的顯示通常都會將其放置在JScrollpane,如果不放置,那么表格的表頭就無法顯示,而此時如果要顯示的話,那么就得將表頭(TableHeader,前面已經提到了)作為一個組件顯示,而JTable再作為一個組件顯示。

 

container.setLayout(new BorderLayout());

container.add(table.getTableHeader(),BorderLayout.PAGE_START);


container.add(table, BorderLayout.CENTER);

 

分析一下上面的表格存在的缺陷:

 (1).這個表格是可以被編輯的

 (2).我們發現,傳入的數據是Boolan類型,按照正常情況下,應該顯示成checkbox,但是這里卻是以字符串的形式顯示(比較第一個圖可以看到,它最后顯示的是一個checkbox,可以讓用戶查看更加的直觀,而第二種卻只能死板的顯示字符串),而為什么會造成這樣的原因呢,后面我們分析完源碼就徹底明白了。

  (3).這里的創建需要我們自己先去創建數據或者Vector,但在很多情況下這種方式會對開發造成代碼的冗余。

 比如:我們從數據庫中查詢到一組對象,此時就需要將對象轉成數組或者集合
(當然,如果你用AOP來解決這類問題可能大材小用了)

 

2.修改單元格的寬度:

我們可以試着去觀察一下上面的表格,我們發現每列都是一樣的寬度,但是在實際的開發中可能會遇到對象的某些屬性是內容特別長的,但是某些屬性內容卻是特別端,因為可能會導致內容較長的列被遮擋,造成不好的用戶體驗.因此,設置表格列的寬度也是實際中必不可少的部分。OK,下面來完成一個對表格列寬度的設置。

 

  1 package org.plx.jtable;
  2 
  3 import java.awt.BorderLayout;
  4 import java.awt.Color;
  5 import java.awt.Dimension;
  6 import java.awt.event.ActionEvent;
  7 import java.awt.event.ActionListener;
  8 import java.awt.event.MouseAdapter;
  9 import java.awt.event.MouseEvent;
 10 
 11 import javax.swing.JButton;
 12 import javax.swing.JFrame;
 13 import javax.swing.JPanel;
 14 import javax.swing.JScrollPane;
 15 import javax.swing.JTable;
 16 import javax.swing.table.TableColumn;
 17 
 18 @SuppressWarnings("serial")
 19 public class SetColumnSizeDemo extends JPanel {
 20     private boolean DEBUG = true;
 21 
 22     public SetColumnSizeDemo() {
 23         super(new BorderLayout());
 24 
 25         //創建表頭
 26         String[] columnNames = { "First Name", "Last Name", "Sport",
 27                 "# of Years", "Vegetarian" };
 28 
 29         //創建顯示數據
 30         Object[][] data = {
 31                 { "Kathy", "Smith", "Snowboarding", new Integer(5),
 32                         new Boolean(false) },
 33                 { "John", "Doe", "Rowing", new Integer(3), new Boolean(true) },
 34                 { "Sue", "Black", "Knitting", new Integer(2),
 35                         new Boolean(false) },
 36                 { "Jane", "White", "Speed reading", new Integer(20),
 37                         new Boolean(true) },
 38                 { "Joe", "Brown", "Pool", new Integer(10), new Boolean(false) } };
 39         
 40         /*
 41          * JTable還提供了一個重載的構造方法,傳入兩個Vector
 42          * JTable(Vector rowData, Vector columnNames)
 43          * 
 44          */
 45         
 46         final JTable table = new JTable(data, columnNames);
 47     
 48         table.setBackground(Color.YELLOW);
 49         
 50         table.setPreferredScrollableViewportSize(new Dimension(800, 100));
 51         
 52         if (DEBUG) {
 53             table.addMouseListener(new MouseAdapter() {
 54                 public void mouseClicked(MouseEvent e) {
 55                     printDebugData(table);
 56                 }
 57             });
 58         }
 59 
 60         // Create the scroll pane and add the table to it.
 61         //這也是官方建議使用的方式,否則表頭不會顯示,需要單獨獲取到TableHeader自己手動地添加顯示
 62         JScrollPane scrollPane = new JScrollPane(table);
 63 
 64         add(scrollPane);
 65             
 66         
 67         JPanel panel2 = new JPanel();
 68         this.add(panel2,BorderLayout.SOUTH);
 69         JButton btn1 = new JButton("表格填充整個視圖");
 70         JButton btn2 = new JButton("表格不添加整個視圖(默認不填充)");
 71         panel2.add(btn1);
 72         panel2.add(btn2);
 73         
 74         btn1.addActionListener(new ActionListener() {
 75             @Override
 76             public void actionPerformed(ActionEvent e) {
 77                 //設置表格填充整個視圖,在默認情況下,如果表格的大小小於視圖(窗體),你會發現下面的內容是其他顏色,可以將上面的yellow去掉做個比較
 78                 table.setFillsViewportHeight(true);
 79             }
 80         });
 81         
 82         btn2.addActionListener(new ActionListener() {
 83             @Override
 84             public void actionPerformed(ActionEvent e) {
 85                 table.setFillsViewportHeight(false);
 86             }
 87         });
 88         initColumnSize(table);
 89     }
 90 
 91     private void printDebugData(JTable table) {
 92         int numRows = table.getRowCount();
 93         int numCols = table.getColumnCount();
 94         javax.swing.table.TableModel model = table.getModel();
 95 
 96         System.out.println("Value of data: ");
 97         for (int i = 0; i < numRows; i++) {
 98             System.out.print("    row " + i + ":");
 99             for (int j = 0; j < numCols; j++) {
100                 System.out.print("  " + model.getValueAt(i, j));
101             }
102             System.out.println();
103         }
104         System.out.println("--------------------------");
105     }
106 
107     /**
108      * Create the GUI and show it. For thread safety, this method should be
109      * invoked from the event-dispatching thread.
110      */
111     private static void createAndShowGUI() {
112         // Create and set up the window.
113         JFrame frame = new JFrame("SimpleTableDemo");
114         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
115 
116         // Create and set up the content pane.
117         SetColumnSizeDemo newContentPane = new SetColumnSizeDemo();
118         newContentPane.setOpaque(true); // content panes must be opaque
119         frame.setContentPane(newContentPane);
120 
121         // 這里直接使用pack來顯示
122         frame.pack();
123         frame.setVisible(true);
124     }
125     
126     /**
127      * 設置Column的寬度
128      */
129     private void initColumnSize(JTable table){
130         //表格的每一列也是一個組件
131         TableColumn tc = null;
132         
133         for(int i = 0 ;i < table.getColumnCount();i++){
134             //注意:這里需要使用TableColumnModel來獲取
135             //如果直接使用table.getColumn(identifier)會報錯,
136             tc = table.getColumnModel().getColumn(i);
137             tc.setPreferredWidth(50 * (i+1));
138         }
139     }
140 
141     public static void main(String[] args) {
142         // Schedule a job for the event-dispatching thread:
143         // creating and showing this application's GUI.
144         javax.swing.SwingUtilities.invokeLater(new Runnable() {
145             public void run() {
146                 createAndShowGUI();
147             }
148         });
149     }
150 }

可以顯示一下效果,這邊圖片上傳一直失敗。還有一點要注意的是:表格的列默認情況下是可以拖動的,那么我們可以設置

tc.setMaxWidth(maxWidth);和 tc.setMinWidth(minWidth);來確定它拖動到最大和最小的寬度。最后補充一點:JTable可以設置setAutoResizeMode,可以傳入一下的五個值: 

AUTO_RESIZE_OFF,

AUTO_RESIZE_NEXT_COLUMN,

AUTO_RESIZE_SUBSEQUENT_COLUMNS,

AUTO_RESIZE_LAST_COLUMN,

AUTO_RESIZE_ALL_COLUMNS

    /**
     * @see #getUIClassID
     * @see #readObject
     */
    private static final String uiClassID = "TableUI";

    /** Do not adjust column widths automatically; use a horizontal scrollbar instead. */
    public static final int     AUTO_RESIZE_OFF = 0;

    /** When a column is adjusted in the UI, adjust the next column the opposite way. */
    public static final int     AUTO_RESIZE_NEXT_COLUMN = 1;

    /** During UI adjustment, change subsequent columns to preserve the total width;
      * this is the default behavior. */
    public static final int     AUTO_RESIZE_SUBSEQUENT_COLUMNS = 2;

    /** During all resize operations, apply adjustments to the last column only. */
    public static final int     AUTO_RESIZE_LAST_COLUMN = 3;

    /** During all resize operations, proportionately resize all columns. */
    public static final int     AUTO_RESIZE_ALL_COLUMNS = 4;

默認情況下的值是2。上面代碼中的注釋已經說得很清楚了。

現在回到前面第一個案例中遺留下來的問題就是為什么有些單元格可以顯示checkbox,有些只能顯示字符串呢?

我們來看下面的這個例子:

package org.plx.jtable;

import java.awt.Dimension;
import java.awt.GridLayout;

import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;

@SuppressWarnings("serial")
public class TableRenderDemo extends JPanel {

    private boolean DEBUG = false;

    public TableRenderDemo() {
        super(new GridLayout(1, 0));

        JTable table = new JTable(new MyTableModel());
        table.setPreferredScrollableViewportSize(new Dimension(500, 400));
        table.setFillsViewportHeight(true);

        // Create the scroll pane and add the table to it.
        JScrollPane scrollPane = new JScrollPane(table);

        // Fiddle with the Sport column's cell editors/renderers.
        setUpSportColumn(table, table.getColumnModel().getColumn(2));

        // Add the scroll pane to this panel.
        add(scrollPane);
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public void setUpSportColumn(JTable table, TableColumn sportColumn) {
        // Set up the editor for the sport cells.
        JComboBox comboBox = new JComboBox();
        comboBox.addItem("Snowboarding");
        comboBox.addItem("Rowing");
        comboBox.addItem("Knitting");
        comboBox.addItem("Speed reading");
        comboBox.addItem("Pool");
        comboBox.addItem("None of the above");
        sportColumn.setCellEditor(new DefaultCellEditor(comboBox));

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

    class MyTableModel extends AbstractTableModel {
        private String[] columnNames = { "First Name", "Last Name", "Sport",
                "# of Years", "Vegetarian" };
        private Object[][] data = {
                { "Kathy", "Smith", "Snowboarding", new Integer(5),
                        new Boolean(false) },
                { "John", "Doe", "Rowing", new Integer(3), new Boolean(true) },
                { "Sue", "Black", "Knitting", new Integer(2),
                        new Boolean(false) },
                { "Jane", "White", "Speed reading", new Integer(20),
                        new Boolean(true) },
                { "Joe", "Brown", "Pool", new Integer(10), new Boolean(false) } };

        public final Object[] longValues = { "Jane", "Kathy",
                "None of the above", new Integer(20), Boolean.TRUE };

        public int getColumnCount() {
            return columnNames.length;
        }

        public int getRowCount() {
            return data.length;
        }

        public String getColumnName(int col) {
            return columnNames[col];
        }

        public Object getValueAt(int row, int col) {
            return data[row][col];
        }

        /*
         * JTable uses this method to determine the default renderer/ editor for
         * each cell. If we didn't implement this method, then the last column
         * would contain text ("true"/"false"), rather than a check box.
         */
        @SuppressWarnings({ "rawtypes", "unchecked" })
        public Class getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }

        /*
         * Don't need to implement this method unless your table's editable.
         */
        public boolean isCellEditable(int row, int col) {
            // Note that the data/cell address is constant,
            // no matter where the cell appears onscreen.
            if (col < 2) {
                return false;
            } else {
                return true;
            }
        }

        /*
         * Don't need to implement this method unless your table's data can
         * change.
         */
        public void setValueAt(Object value, int row, int col) {
            if (DEBUG) {
                System.out.println("Setting value at " + row + "," + col
                        + " to " + value + " (an instance of "
                        + value.getClass() + ")");
            }

            data[row][col] = value;
            fireTableCellUpdated(row, col);

            if (DEBUG) {
                System.out.println("New value of data:");
                printDebugData();
            }
        }

        private void printDebugData() {
            int numRows = getRowCount();
            int numCols = getColumnCount();

            for (int i = 0; i < numRows; i++) {
                System.out.print("    row " + i + ":");
                for (int j = 0; j < numCols; j++) {
                    System.out.print("  " + data[i][j]);
                }
                System.out.println();
            }
            System.out.println("--------------------------");
        }
    }

    /**
     * Create the GUI and show it. For thread safety, this method should be
     * invoked from the event-dispatching thread.
     */
    private static void createAndShowGUI() {
        // Create and set up the window.
        JFrame frame = new JFrame("TableRenderDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Create and set up the content pane.
        TableRenderDemo newContentPane = new TableRenderDemo();
        newContentPane.setOpaque(true); // content panes must be opaque
        frame.setContentPane(newContentPane);

        // Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        // Schedule a job for the event-dispatching thread:
        // creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

此時再次運行上面的程序就可以顯示checkbox。首先我們這里自己去創建了一個TableModel的實現類

public interface TableModel
{
    /**
     * Returns the number of rows in the model. A
     * <code>JTable</code> uses this method to determine how many rows it
     * should display.  This method should be quick, as it
     * is called frequently during rendering.
     *
     * @return the number of rows in the model
     * @see #getColumnCount
     */
    public int getRowCount();

TableModel是一個接口,而我們自己所編寫的TableModel是繼承了AbstractTableModel,該類是一個抽象類,實現了一部分的方法。但是最最核心和還是

 

        /*
         * JTable uses this method to determine the default renderer/ editor for
         * each cell. If we didn't implement this method, then the last column
         * would contain text ("true"/"false"), rather than a check box.
         */
        @SuppressWarnings({ "rawtypes", "unchecked" })
        public Class getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }

從以上的注釋我們可以知道:JTable是使用這個方法來決定對於每個單元格是如何渲染的,如果我們不實現這個方法,那么默認返回的值就是true或者false,而不是一個checkbox。

其本質的原因在於如果當返回的是一個Object.class,那么JTable在渲染的時候就是使用字符串去顯示,如果你返回的是Boolean.class,那么就是以checkbox來渲染,而如果是Image.class,那么就可以以圖片的顯示來顯示。我們的第一個例子中,直接使用數組的方式來構建JTable,它的低層是通過自身的一個TableModel來維護,這個類是一個匿名內部類,

 public JTable(final Object[][] rowData, final Object[] columnNames) {
        this(new AbstractTableModel() {
            public String getColumnName(int column) { return columnNames[column].toString(); }
            public int getRowCount() { return rowData.length; }
            public int getColumnCount() { return columnNames.length; }
            public Object getValueAt(int row, int col) { return rowData[row][col]; }
            public boolean isCellEditable(int row, int column) { return true; }
            public void setValueAt(Object value, int row, int col) {
                rowData[row][col] = value;
                fireTableCellUpdated(row, col);
            }
        });
    }

通過以上的源碼我們可以看到,它是繼承了AbstractTableModel,而AbstractTableModel中的getColumnClass方法返回的就是一個Object.class,所以無論傳入什么內容,JTable在顯示的時候都是通過字符串去顯示。

AbstractTableModel的getColumnClass方法:

package org.plx.jtable;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.table.AbstractTableModel;

@SuppressWarnings("serial")
public class TableSelectionDemo extends JPanel implements ActionListener {
    private JTable table;
    private JCheckBox rowCheck;
    private JCheckBox columnCheck;
    private JCheckBox cellCheck;
    private ButtonGroup buttonGroup;
    private JTextArea output;

    public TableSelectionDemo() {
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        table = new JTable(new MyTableModel());
        table.setPreferredScrollableViewportSize(new Dimension(500, 70));
        table.setFillsViewportHeight(true);
        table.getSelectionModel().addListSelectionListener(new RowListener());
        table.getColumnModel().getSelectionModel()
                .addListSelectionListener(new ColumnListener());
        add(new JScrollPane(table));
        

        add(new JLabel("Selection Mode"));
        buttonGroup = new ButtonGroup();
        //可以選中多行,使用ctrl或者shift都可以進行選擇,默認情況下就是可以選擇多行
        addRadio("Multiple Interval Selection").setSelected(true);
        //只能選中單行
        addRadio("Single Selection");
        //只能選中連續的多行
        addRadio("Single Interval Selection");
        
        
        add(new JLabel("Selection Options"));
        
        
        //默認情況下就是以行為單位進行選擇
        rowCheck = addCheckBox("Row Selection");
        rowCheck.setSelected(true);
        
        columnCheck = addCheckBox("Column Selection");
        
        
        cellCheck = addCheckBox("Cell Selection");
        cellCheck.setEnabled(false);


        
        output = new JTextArea(5, 40);
        output.setEditable(false);
        add(new JScrollPane(output));
    }

    private JCheckBox addCheckBox(String text) {
        JCheckBox checkBox = new JCheckBox(text);
        checkBox.addActionListener(this);
        add(checkBox);
        return checkBox;
    }

    private JRadioButton addRadio(String text) {
        JRadioButton b = new JRadioButton(text);
        b.addActionListener(this);
        buttonGroup.add(b);
        add(b);
        return b;
    }

    public void actionPerformed(ActionEvent event) {
        String command = event.getActionCommand();
        // Cell selection is disabled in Multiple Interval Selection
        // mode. The enabled state of cellCheck is a convenient flag
        // for this status.
        if ("Row Selection" == command) {
            table.setRowSelectionAllowed(rowCheck.isSelected());
            // In MIS mode, column selection allowed must be the
            // opposite of row selection allowed.
            if (!cellCheck.isEnabled()) {
                table.setColumnSelectionAllowed(!rowCheck.isSelected());
            }
        } else if ("Column Selection" == command) {
            table.setColumnSelectionAllowed(columnCheck.isSelected());
            // In MIS mode, row selection allowed must be the
            // opposite of column selection allowed.
            if (!cellCheck.isEnabled()) {
                table.setRowSelectionAllowed(!columnCheck.isSelected());
            }
        } else if ("Cell Selection" == command) {
            table.setCellSelectionEnabled(cellCheck.isSelected());
        } else if ("Multiple Interval Selection" == command) {
            table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
            // If cell selection is on, turn it off.
            if (cellCheck.isSelected()) {
                cellCheck.setSelected(false);
                table.setCellSelectionEnabled(false);
            }
            // And don't let it be turned back on.
            cellCheck.setEnabled(false);
        } else if ("Single Interval Selection" == command) {
            table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
            // Cell selection is ok in this mode.
            cellCheck.setEnabled(true);
        } else if ("Single Selection" == command) {
            table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            // Cell selection is ok in this mode.
            cellCheck.setEnabled(true);
        }

        // Update checkboxes to reflect selection mode side effects.
        rowCheck.setSelected(table.getRowSelectionAllowed());
        columnCheck.setSelected(table.getColumnSelectionAllowed());
        if (cellCheck.isEnabled()) {
            cellCheck.setSelected(table.getCellSelectionEnabled());
        }
    }

    private void outputSelection() {
        output.append(String.format("Lead: %d, %d. ", table.getSelectionModel()
                .getLeadSelectionIndex(), table.getColumnModel()
                .getSelectionModel().getLeadSelectionIndex()));
        output.append("Rows:");
        for (int c : table.getSelectedRows()) {
            output.append(String.format(" %d", c));
        }
        output.append(". Columns:");
        for (int c : table.getSelectedColumns()) {
            output.append(String.format(" %d", c));
        }
        output.append(".\n");
    }

    private class RowListener implements ListSelectionListener {
        public void valueChanged(ListSelectionEvent event) {
            if (event.getValueIsAdjusting()) {
                return;
            }
            output.append("ROW SELECTION EVENT. ");
            outputSelection();
        }
    }

    private class ColumnListener implements ListSelectionListener {
        public void valueChanged(ListSelectionEvent event) {
            if (event.getValueIsAdjusting()) {
                return;
            }
            output.append("COLUMN SELECTION EVENT. ");
            outputSelection();
        }
    }

    class MyTableModel extends AbstractTableModel {
        private String[] columnNames = { "First Name", "Last Name", "Sport",
                "# of Years", "Vegetarian" };
        private Object[][] data = {
                { "Kathy", "Smith", "Snowboarding", new Integer(5),
                        new Boolean(false) },
                { "John", "Doe", "Rowing", new Integer(3), new Boolean(true) },
                { "Sue", "Black", "Knitting", new Integer(2),
                        new Boolean(false) },
                { "Jane", "White", "Speed reading", new Integer(20),
                        new Boolean(true) },
                { "Joe", "Brown", "Pool", new Integer(10), new Boolean(false) } };

        public int getColumnCount() {
            return columnNames.length;
        }

        public int getRowCount() {
            return data.length;
        }

        public String getColumnName(int col) {
            return columnNames[col];
        }

        public Object getValueAt(int row, int col) {
            return data[row][col];
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        public Class getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }

        /*
         * Don't need to implement this method unless your table's editable.
         */
        public boolean isCellEditable(int row, int col) {
            // Note that the data/cell address is constant,
            // no matter where the cell appears onscreen.
            if (col < 2) {
                return false;
            } else {
                return true;
            }
        }

        /*
         * Don't need to implement this method unless your table's data can
         * change.
         */
        public void setValueAt(Object value, int row, int col) {
            data[row][col] = value;
            fireTableCellUpdated(row, col);
        }

    }

    /**
     * Create the GUI and show it. For thread safety, this method should be
     * invoked from the event-dispatching thread.
     */
    private static void createAndShowGUI() {
        try {
            UIManager.setLookAndFeel(new MetalLookAndFeel());
            JFrame frame = new JFrame("TableSelectionDemo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            TableSelectionDemo newContentPane = new TableSelectionDemo();
            newContentPane.setOpaque(true); // content panes must be opaque
            frame.setContentPane(newContentPane);

            // Display the window.
            frame.pack();
            frame.setVisible(true);
        } catch (UnsupportedLookAndFeelException e) {
            throw new RuntimeException(e);
        }

    }

    public static void main(String[] args) {
        // Schedule a job for the event-dispatching thread:
        // creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

以上代碼的核心是理解API中的幾個方法:

selectRowAllowed是否允許選中行,selectColumnAllowed是否選中列,當設置兩個方法都為true的時候,那么此時就是設置你選中的是單元格.如果兩個都設置false,那么就表示什么都無法選中。最后,如果你只能選中一個單元格,還需要設置selectListModel為singleSelection,對於singleSelection方法中的參數是通過獲取到ListSelectionModel中的常量來完成設置。

package javax.swing;

import javax.swing.event.*;

/**
 * This interface represents the current state of the
 * selection for any of the components that display a
 * list of values with stable indices.  The selection is
 * modeled as a set of intervals, each interval represents
 * a contiguous range of selected list elements.
 * The methods for modifying the set of selected intervals
 * all take a pair of indices, index0 and index1, that represent
 * a closed interval, i.e. the interval includes both index0 and
 * index1.
 *
 * @author Hans Muller
 * @author Philip Milne
 * @see DefaultListSelectionModel
 */

public interface ListSelectionModel
{
    /**
     * A value for the selectionMode property: select one list index
     * at a time.
     *
     * @see #setSelectionMode
     */
    int SINGLE_SELECTION = 0;

    /**
     * A value for the selectionMode property: select one contiguous
     * range of indices at a time.
     *
     * @see #setSelectionMode
     */
    int SINGLE_INTERVAL_SELECTION = 1;

    /**
     * A value for the selectionMode property: select one or more
     * contiguous ranges of indices at a time.
     *
     * @see #setSelectionMode
     */
    int MULTIPLE_INTERVAL_SELECTION = 2;


    /**
     * Changes the selection to be between {@code index0} and {@code index1}
     * inclusive. {@code index0} doesn't have to be less than or equal to
     * {@code index1}.
     * <p>
     * In {@code SINGLE_SELECTION} selection mode, only the second index
     * is used.
     * <p>
     * If this represents a change to the current selection, then each
     * {@code ListSelectionListener} is notified of the change.
     *
     * @param index0 one end of the interval.
     * @param index1 other end of the interval
     * @see #addListSelectionListener
     */
    void setSelectionInterval(int index0, int index1);


    /**
     * Changes the selection to be the set union of the current selection
     * and the indices between {@code index0} and {@code index1} inclusive.
     * {@code index0} doesn't have to be less than or equal to {@code index1}.
     * <p>
     * In {@code SINGLE_SELECTION} selection mode, this is equivalent
     * to calling {@code setSelectionInterval}, and only the second index
     * is used. In {@code SINGLE_INTERVAL_SELECTION} selection mode, this
     * method behaves like {@code setSelectionInterval}, unless the given
     * interval is immediately adjacent to or overlaps the existing selection,
     * and can therefore be used to grow the selection.
     * <p>
     * If this represents a change to the current selection, then each
     * {@code ListSelectionListener} is notified of the change.
     *
     * @param index0 one end of the interval.
     * @param index1 other end of the interval
     * @see #addListSelectionListener
     * @see #setSelectionInterval
     */
    void addSelectionInterval(int index0, int index1);


    /**
     * Changes the selection to be the set difference of the current selection
     * and the indices between {@code index0} and {@code index1} inclusive.
     * {@code index0} doesn't have to be less than or equal to {@code index1}.
     * <p>
     * In {@code SINGLE_INTERVAL_SELECTION} selection mode, if the removal
     * would produce two disjoint selections, the removal is extended through
     * the greater end of the selection. For example, if the selection is
     * {@code 0-10} and you supply indices {@code 5,6} (in any order) the
     * resulting selection is {@code 0-4}.
     * <p>
     * If this represents a change to the current selection, then each
     * {@code ListSelectionListener} is notified of the change.
     *
     * @param index0 one end of the interval.
     * @param index1 other end of the interval
     * @see #addListSelectionListener
     */
    void removeSelectionInterval(int index0, int index1);


    /**
     * Returns the first selected index or -1 if the selection is empty.
     */
    int getMinSelectionIndex();


    /**
     * Returns the last selected index or -1 if the selection is empty.
     */
    int getMaxSelectionIndex();


    /**
     * Returns true if the specified index is selected.
     */
    boolean isSelectedIndex(int index);


    /**
     * Return the first index argument from the most recent call to
     * setSelectionInterval(), addSelectionInterval() or removeSelectionInterval().
     * The most recent index0 is considered the "anchor" and the most recent
     * index1 is considered the "lead".  Some interfaces display these
     * indices specially, e.g. Windows95 displays the lead index with a
     * dotted yellow outline.
     *
     * @see #getLeadSelectionIndex
     * @see #setSelectionInterval
     * @see #addSelectionInterval
     */
    int getAnchorSelectionIndex();


    /**
     * Set the anchor selection index.
     *
     * @see #getAnchorSelectionIndex
     */
    void setAnchorSelectionIndex(int index);


    /**
     * Return the second index argument from the most recent call to
     * setSelectionInterval(), addSelectionInterval() or removeSelectionInterval().
     *
     * @see #getAnchorSelectionIndex
     * @see #setSelectionInterval
     * @see #addSelectionInterval
     */
    int getLeadSelectionIndex();

    /**
     * Set the lead selection index.
     *
     * @see #getLeadSelectionIndex
     */
    void setLeadSelectionIndex(int index);

    /**
     * Change the selection to the empty set.  If this represents
     * a change to the current selection then notify each ListSelectionListener.
     *
     * @see #addListSelectionListener
     */
    void clearSelection();

    /**
     * Returns true if no indices are selected.
     */
    boolean isSelectionEmpty();

    /**
     * Insert length indices beginning before/after index.  This is typically
     * called to sync the selection model with a corresponding change
     * in the data model.
     */
    void insertIndexInterval(int index, int length, boolean before);

    /**
     * Remove the indices in the interval index0,index1 (inclusive) from
     * the selection model.  This is typically called to sync the selection
     * model width a corresponding change in the data model.
     */
    void removeIndexInterval(int index0, int index1);

    /**
     * Sets the {@code valueIsAdjusting} property, which indicates whether
     * or not upcoming selection changes should be considered part of a single
     * change. The value of this property is used to initialize the
     * {@code valueIsAdjusting} property of the {@code ListSelectionEvent}s that
     * are generated.
     * <p>
     * For example, if the selection is being updated in response to a user
     * drag, this property can be set to {@code true} when the drag is initiated
     * and set to {@code false} when the drag is finished. During the drag,
     * listeners receive events with a {@code valueIsAdjusting} property
     * set to {@code true}. At the end of the drag, when the change is
     * finalized, listeners receive an event with the value set to {@code false}.
     * Listeners can use this pattern if they wish to update only when a change
     * has been finalized.
     * <p>
     * Setting this property to {@code true} begins a series of changes that
     * is to be considered part of a single change. When the property is changed
     * back to {@code false}, an event is sent out characterizing the entire
     * selection change (if there was one), with the event's
     * {@code valueIsAdjusting} property set to {@code false}.
     *
     * @param valueIsAdjusting the new value of the property
     * @see #getValueIsAdjusting
     * @see javax.swing.event.ListSelectionEvent#getValueIsAdjusting
     */
    void setValueIsAdjusting(boolean valueIsAdjusting);

    /**
     * Returns {@code true} if the selection is undergoing a series of changes.
     *
     * @return true if the selection is undergoing a series of changes
     * @see #setValueIsAdjusting
     */
    boolean getValueIsAdjusting();

    /**
     * Sets the selection mode. The following list describes the accepted
     * selection modes:
     * <ul>
     * <li>{@code ListSelectionModel.SINGLE_SELECTION} -
     *   Only one list index can be selected at a time. In this mode,
     *   {@code setSelectionInterval} and {@code addSelectionInterval} are
     *   equivalent, both replacing the current selection with the index
     *   represented by the second argument (the "lead").
     * <li>{@code ListSelectionModel.SINGLE_INTERVAL_SELECTION} -
     *   Only one contiguous interval can be selected at a time.
     *   In this mode, {@code addSelectionInterval} behaves like
     *   {@code setSelectionInterval} (replacing the current selection),
     *   unless the given interval is immediately adjacent to or overlaps
     *   the existing selection, and can therefore be used to grow it.
     * <li>{@code ListSelectionModel.MULTIPLE_INTERVAL_SELECTION} -
     *   In this mode, there's no restriction on what can be selected.
     * </ul>
     *
     * @see #getSelectionMode
     * @throws IllegalArgumentException if the selection mode isn't
     *         one of those allowed
     */
    void setSelectionMode(int selectionMode);

    /**
     * Returns the current selection mode.
     *
     * @return the current selection mode
     * @see #setSelectionMode
     */
    int getSelectionMode();

    /**
     * Add a listener to the list that's notified each time a change
     * to the selection occurs.
     *
     * @param x the ListSelectionListener
     * @see #removeListSelectionListener
     * @see #setSelectionInterval
     * @see #addSelectionInterval
     * @see #removeSelectionInterval
     * @see #clearSelection
     * @see #insertIndexInterval
     * @see #removeIndexInterval
     */
    void addListSelectionListener(ListSelectionListener x);

    /**
     * Remove a listener from the list that's notified each time a
     * change to the selection occurs.
     *
     * @param x the ListSelectionListener
     * @see #addListSelectionListener
     */
    void removeListSelectionListener(ListSelectionListener x);
}

 

TableModel:

在前面我們已經提到過TableModel是用來管理真實的數據.我們在實際的開發中通常會使用DefaultTableModel。在創建JTable的時候,如果你傳入的是一個Object[],那么底層維護的TableModel是一個AbstractModel的匿名內部類,這才前面已經提到,但是如果你傳入的是Vector,那么底層維護的是DefaultTableModel.

底層源碼:

  public JTable(Vector rowData, Vector columnNames) {
        this(new DefaultTableModel(rowData, columnNames));
    }

但是我們最希望的是直接丟一個對象就去,然后顯示。下面我們就自己編寫一個TableModel來完成一下的操作(當然,如果你想要你的程序更加的靈活,那么在TableModel中使用反射和內省機制吧)。對於自己實現TableModel,我們通常會去繼承AbstractTableModel.

下面我們自己編寫一個基於面向對象的TableModel,然后你再拿和傳統的方式去比較,讓你馬上感覺從Jdbc上升到Hibernate的級別。當然,要編寫一個通用的組件可能還需要測試,這里我這是提供一種思路來給大家參考而已。

package org.plx.jtable;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

public class User implements PropertyChangeListener{
    
    
    @BeanColumn(name = "用戶編號", index = 0)
    private Integer id;
    
    
    @BeanColumn(name = "用戶姓名", index = 1)
    private String username;
    
    @BeanColumn(name = "用戶密碼", index = 2)
    private String password;
    
    
    @BeanColumn(name = "用戶年齡", index = 3)
    private int age;

    //這里如果取名字為is開頭,在introspector中可能會存在BUG
    @BeanColumn(name = "婚否", index = 4)
    private Boolean marry;
    
    
    public User() {}
    

    public User(Integer id, String username, String password, int age,
            Boolean marry) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.age = age;
        this.marry = marry;
    }



    public Boolean getMarry() {
        return marry;
    }

    public void setMarry(Boolean marry) {
        this.marry = marry;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        
    }


    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        return result;
    }


    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        User other = (User) obj;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        return true;
    }
    
    

}

在這里,我自己定義了一個Annotation,用來定義這個屬性在JTable中對應的表頭名字。

 

package org.plx.jtable;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BeanColumn {

    String name();//在JTable中的表頭名字
    int index();//在表頭的索引
}

好了,下面我們來測試一下寫的程序。

  1 package org.plx.jtable;
  2 
  3 import java.awt.BorderLayout;
  4 import java.awt.FlowLayout;
  5 import java.awt.Panel;
  6 import java.awt.event.ActionEvent;
  7 import java.awt.event.ActionListener;
  8 import java.beans.BeanInfo;
  9 import java.beans.IntrospectionException;
 10 import java.beans.Introspector;
 11 import java.beans.PropertyDescriptor;
 12 import java.lang.reflect.Field;
 13 import java.util.ArrayList;
 14 import java.util.Enumeration;
 15 import java.util.HashMap;
 16 import java.util.List;
 17 import java.util.Map;
 18 import java.util.TreeMap;
 19 
 20 import javax.swing.AbstractButton;
 21 import javax.swing.ButtonGroup;
 22 import javax.swing.JButton;
 23 import javax.swing.JDialog;
 24 import javax.swing.JFrame;
 25 import javax.swing.JLabel;
 26 import javax.swing.JRadioButton;
 27 import javax.swing.JScrollPane;
 28 import javax.swing.JTable;
 29 import javax.swing.JTextField;
 30 import javax.swing.table.AbstractTableModel;
 31 import javax.swing.table.TableModel;
 32 
 33 /**
 34  * 基於對象格式的TableModel
 35  * 
 36  * @author Administrator
 37  * 
 38  */
 39 public class TableModelDemo {
 40 
 41     public static void main(String[] args) {
 42 
 43         final int WIDTH = 700;
 44         final int HEIGHT = 500;
 45         final int USER_SIZE = 10;
 46 
 47         final JFrame frame = new JFrame();
 48         frame.setTitle("基於面向對象的TableModel測試");
 49         frame.setSize(WIDTH, HEIGHT);
 50         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 51 
 52         final JTable table = new JTable();
 53         table.setFillsViewportHeight(true);
 54         table.setRowHeight(30);
 55         List<User> users = new ArrayList<User>();
 56         for (int i = 0; i < USER_SIZE; i++) {
 57             User u = new User();
 58             u.setId(i);
 59             u.setUsername("username" + i);
 60             u.setPassword("123");
 61             u.setAge(i + 20);
 62             u.setMarry(i % 2 == 0);
 63             users.add(u);
 64         }
 65 
 66         final TableModel tableModel = new MyTableModel<User>(users);
 67         table.setModel(tableModel);
 68 
 69         frame.add(new JScrollPane(table));
 70 
 71         Panel panel = new Panel();
 72 
 73         JButton addBtn = new JButton("添加用戶");
 74         JButton deleteBtn = new JButton("刪除用戶(按照行來刪除)");
 75         JButton deleteBtn2 = new JButton("刪除用戶(按照對象來刪除)");
 76         JButton updateBtn = new JButton("更新用戶");
 77 
 78         panel.add(addBtn);
 79         panel.add(deleteBtn);
 80         panel.add(deleteBtn2);
 81         panel.add(updateBtn);
 82 
 83         addBtn.addActionListener(new ActionListener() {
 84             @Override
 85             public void actionPerformed(ActionEvent e) {
 86                 JDialog dialog = new JDialog(frame, true);
 87                 dialog.setSize(300, 300);
 88 
 89                 JLabel idLab = new JLabel("用戶編號:");
 90                 final JTextField idTextField = new JTextField(25);
 91 
 92                 JLabel nameLab = new JLabel("用戶姓名");
 93                 final JTextField nameTextField = new JTextField(25);
 94 
 95                 JLabel passwordLab = new JLabel("用戶密碼:");
 96                 final JTextField passwordTextField = new JTextField(25);
 97 
 98                 JLabel ageLab = new JLabel("用戶年齡:");
 99                 final JTextField ageTextField = new JTextField(25);
100 
101                 final JRadioButton marry = new JRadioButton();
102                 marry.setText("已婚");
103                 marry.setSelected(true);
104 
105                 final JRadioButton noMarry = new JRadioButton();
106                 noMarry.setText("未婚");
107 
108                 final ButtonGroup group = new ButtonGroup();
109                 group.add(marry);
110                 group.add(noMarry);
111 
112                 JButton button = new JButton("確認添加");
113 
114                 button.addActionListener(new ActionListener() {
115                     @SuppressWarnings({ "unchecked", "rawtypes" })
116                     @Override
117                     public void actionPerformed(ActionEvent e) {
118                         Integer id = Integer.parseInt(idTextField.getText());
119                         String username = nameTextField.getText();
120                         String password = passwordTextField.getText();
121                         int age = Integer.parseInt(ageTextField.getText());
122                         boolean marry = false;
123                         Enumeration<AbstractButton> en = group.getElements();
124                         for (; en.hasMoreElements();) {
125                             AbstractButton ab = en.nextElement();
126                             if (ab.isSelected()) {
127                                 marry = ab.getText().equals("已婚") ? true
128                                         : false;
129                                 break;
130                             }
131                         }
132                         User user = new User(id, username, password, age, marry);
133                         ((MyTableModel) tableModel).addRow(user);
134                     }
135                 });
136 
137                 dialog.setLayout(new FlowLayout());
138                 dialog.add(idLab);
139                 dialog.add(idTextField);
140                 dialog.add(nameLab);
141                 dialog.add(nameTextField);
142                 dialog.add(passwordLab);
143                 dialog.add(passwordTextField);
144                 dialog.add(ageLab);
145                 dialog.add(ageTextField);
146                 dialog.add(marry);
147                 dialog.add(noMarry);
148 
149                 dialog.add(button);
150 
151                 dialog.setVisible(true);
152             }
153         });
154         
155         deleteBtn.addActionListener(new ActionListener() {
156             @SuppressWarnings("rawtypes")
157             @Override
158             public void actionPerformed(ActionEvent e) {
159                 int rowIndex = table.getSelectedRow();
160                 ((MyTableModel)table.getModel()).deleteRow(rowIndex);
161             }
162         });
163         
164         
165         deleteBtn2.addActionListener(new ActionListener() {
166             @SuppressWarnings({ "rawtypes", "unchecked" })
167             @Override
168             public void actionPerformed(ActionEvent e) {
169                 int rowIndex = table.getSelectedRow();
170                 
171                 MyTableModel tableModel = ((MyTableModel)table.getModel());
172                 Integer id = (Integer) tableModel.getValueAt(rowIndex, 0);
173                 User u = new User();
174                 u.setId(id);
175                 tableModel.deleteRow(u);
176             }
177         });
178         
179         updateBtn.addActionListener(new ActionListener() {
180             
181             @Override
182             public void actionPerformed(ActionEvent e) {
183                 final int rowIndex = table.getSelectedRow();
184                 
185                 @SuppressWarnings("rawtypes")
186                 final MyTableModel tableModel = ((MyTableModel)table.getModel());
187                 /*
188                  //傳統的方式是需要通過
189                     for(int i = 0;i < tableModel.getColumnCount();i++){
190                         tableModel.getValueAt(rowIndex, i);
191                     }
192                 */
193                 //現在我們采用基於OO的獲取
194                 User user = (User) tableModel.getObjbyRowIndex(rowIndex);
195                 
196                 JDialog dialog = new JDialog(frame, true);
197                 dialog.setSize(300, 300);
198 
199                 JLabel idLab = new JLabel("用戶編號:");
200                 final JTextField idTextField = new JTextField(String.valueOf(user.getId()),25);
201                 
202 
203                 JLabel nameLab = new JLabel("用戶姓名");
204                 final JTextField nameTextField = new JTextField(user.getUsername(),25);
205 
206                 JLabel passwordLab = new JLabel("用戶密碼:");
207                 final JTextField passwordTextField = new JTextField(user.getPassword(),25);
208 
209                 JLabel ageLab = new JLabel("用戶年齡:");
210                 final JTextField ageTextField = new JTextField(String.valueOf(user.getAge()),25);
211 
212                 final JRadioButton marry = new JRadioButton();
213                 marry.setText("已婚");
214 
215                 final JRadioButton noMarry = new JRadioButton();
216                 noMarry.setText("未婚");
217                 
218                 if(user.getMarry()){
219                     marry.setSelected(true);
220                 }
221                 else{
222                     noMarry.setSelected(true);
223                 }
224 
225                 final ButtonGroup group = new ButtonGroup();
226                 group.add(marry);
227                 group.add(noMarry);
228 
229                 JButton button = new JButton("確認更新");
230 
231                 button.addActionListener(new ActionListener() {
232                     @SuppressWarnings({ "unchecked"})
233                     @Override
234                     public void actionPerformed(ActionEvent e) {
235                         Integer id = Integer.parseInt(idTextField.getText());
236                         String username = nameTextField.getText();
237                         String password = passwordTextField.getText();
238                         int age = Integer.parseInt(ageTextField.getText());
239                         boolean marry = false;
240                         Enumeration<AbstractButton> en = group.getElements();
241                         for (; en.hasMoreElements();) {
242                             AbstractButton ab = en.nextElement();
243                             if (ab.isSelected()) {
244                                 marry = ab.getText().equals("已婚") ? true
245                                         : false;
246                                 break;
247                             }
248                         }
249                         User user = new User(id, username, password, age, marry);
250                         tableModel.update(rowIndex, user);
251                     }
252                 });
253 
254                 dialog.setLayout(new FlowLayout());
255                 dialog.add(idLab);
256                 dialog.add(idTextField);
257                 dialog.add(nameLab);
258                 dialog.add(nameTextField);
259                 dialog.add(passwordLab);
260                 dialog.add(passwordTextField);
261                 dialog.add(ageLab);
262                 dialog.add(ageTextField);
263                 dialog.add(marry);
264                 dialog.add(noMarry);
265 
266                 dialog.add(button);
267 
268                 dialog.setVisible(true);
269             }
270         });
271 
272         frame.add(panel, BorderLayout.SOUTH);
273 
274         frame.setVisible(true);
275     }
276 
277 }
278 
279 /**
280  * 請將你傳入的對象以JavaBean的形式創建
281  * 
282  * 自己定義的TableModel,可以直接放入對象
283  * 
284  * @author Administrator
285  * 
286  * @param <T>
287  */
288 @SuppressWarnings("serial")
289 class MyTableModel<T> extends AbstractTableModel {
290     private List<T> objs;
291     private BeanInfo beanInfo;
292     private Map<Integer, String> columnInfo = null;
293     private Map<Integer, Integer> propertyInfo = null;
294     private PropertyDescriptor[] pd = null;
295     private int columnCount;
296     @SuppressWarnings("unused")
297     private Class<T> clazz;
298 
299     public MyTableModel() {
300         try {
301             columnInfo = new TreeMap<Integer, String>();
302             propertyInfo = new HashMap<Integer, Integer>();
303             Field[] fields = getClz().getDeclaredFields();
304             beanInfo = Introspector.getBeanInfo(getClz());
305             pd = beanInfo.getPropertyDescriptors();
306             for (Field f : fields) {
307                 if (f.isAnnotationPresent(BeanColumn.class)) {
308                     // 這里沒有直接寫成columnCOunt = fileds.length是因為可能某些字段不用來顯示
309                     columnCount++;
310                     // 獲取到Annotation
311                     BeanColumn bc = f.getAnnotation(BeanColumn.class);
312                     // 獲取到該屬性對應的列名稱
313                     String columnName = bc.name();
314                     // 獲取該名稱在Table中的索引值
315                     int index = bc.index();
316                     // 通過TreeMap將列名稱以及它的索引存儲起來,用來顯示表頭信息
317                     columnInfo.put(index, columnName);
318                     /*
319                      * 判斷該屬性在beanInfo中的索引
320                      * 最后顯示是通過columnIndex--PropertyDescriptor數組中的索引,
321                      * 然后獲取到PropertyDescriptor來獲取到具體的數據
322                      */
323                     for (int i = 0; i < pd.length; i++) {
324                         String fieldName = null;
325                         if (f.getName().startsWith("is")) {
326                             fieldName = f
327                                     .getName()
328                                     .substring(
329                                             f.getName().indexOf("is")
330                                                     + "is".length())
331                                     .toLowerCase();
332                         } else {
333                             fieldName = f.getName();
334                         }
335                         if (fieldName.equals(pd[i].getName())) {
336                             propertyInfo.put(index, i);
337                         }
338                     }
339                 }
340             }
341         } catch (IntrospectionException e) {
342             e.printStackTrace();
343         }
344     }
345 
346     public MyTableModel(List<T> list) {
347         this();
348         this.objs = list;
349     }
350 
351     
352     /**
353      * 獲取到泛型中的Class對象
354      * 這里還未解決,暫時先寫死
355      * @return
356      */
357     @SuppressWarnings("unchecked")
358     private Class<T> getClz() {
359         return (Class<T>) User.class;
360     }
361 
362     /**
363      * 返回對象集合
364      * 
365      * @return
366      */
367     public List<T> getObjs() {
368         return objs;
369     }
370 
371     /**
372      * 設置對象集合
373      * 
374      * @param objs
375      */
376     public void setObjs(List<T> objs) {
377         this.objs = objs;
378     }
379 
380     /**
381      * 獲取總的行數
382      */
383     @Override
384     public int getRowCount() {
385         if (objs != null) {
386             return objs.size();
387         } else {
388             return 0;
389         }
390     }
391 
392     /**
393      * 獲取總的列數
394      */
395     @Override
396     public int getColumnCount() {
397         return columnCount;
398     }
399 
400     /**
401      * 返回單元格的數據做顯示
402      */
403     @Override
404     public Object getValueAt(int rowIndex, int columnIndex) {
405         try {
406             if (objs != null) {
407                 // 獲取到行數據
408                 T t = objs.get(rowIndex);
409                 Integer propertyIndex = propertyInfo.get(columnIndex);
410                 return pd[propertyIndex].getReadMethod().invoke(t,
411                         new Object[] {});
412             }
413         } catch (Exception e) {
414             e.printStackTrace();
415         }
416         return null;
417     }
418 
419     /**
420      * 返回類的名稱
421      */
422     @Override
423     public String getColumnName(int column) {
424         return columnInfo.get(column);
425     }
426 
427     /**
428      * 返回TableCellRender渲染的類型
429      */
430     @Override
431     public Class<?> getColumnClass(int columnIndex) {
432         if (pd != null) {
433             return pd[propertyInfo.get(columnIndex)].getPropertyType();
434         }
435         return Object.class;
436     }
437 
438     /**
439      * DefaultTableModel底層也是這樣去完成的
440      */
441     @Override
442     public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
443         try {
444             T t = objs.get(rowIndex);
445             int propIndex = propertyInfo.get(columnIndex);
446             pd[propIndex].getWriteMethod().invoke(t, new Object[] { aValue });
447             // 當數據更新完成之后完成更新視圖層
448             fireTableCellUpdated(rowIndex, columnIndex);
449         } catch (Exception e) {
450             e.printStackTrace();
451         }
452     }
453 
454     /**
455      * 設置是否可以編輯
456      */
457     @Override
458     public boolean isCellEditable(int rowIndex, int columnIndex) {
459         return true;
460     }
461 
462     public void addRow(T t) {
463         if (t == null) {
464             throw new RuntimeException("添加失敗");
465         }
466         objs.add(t);
467         fireTableRowsInserted(getRowCount() - 1, getRowCount() - 1);
468     }
469 
470     /**
471      * 提供重載方法,讓用戶去調用
472      * 
473      * @param data
474      */
475     public void addRow(List<Object> data) {
476 
477     }
478 
479     public void addRow(Object[] data) {
480 
481     }
482     
483     /**
484      * 根據對象來刪除
485      * 此時需要重寫對象的equals和hashCode方法,因為底層ArrayLiST判斷對象是否
486      * 相等是通過equals方法來進行比較
487      * @param t
488      */
489     public void deleteRow(T t){
490         this.objs.remove(t);
491         fireTableRowsDeleted(this.getColumnCount(),this.getColumnCount());
492     }
493     
494     /**
495      * 根據行來刪除
496      * @param rowIndex
497      */
498     public void deleteRow(int rowIndex){
499         this.objs.remove(rowIndex);
500         fireTableRowsDeleted(this.getColumnCount(),this.getColumnCount());
501     }
502 
503     public T getObjbyRowIndex(int rowIndex){
504         return objs.get(rowIndex);
505     }
506     
507     /**
508      * 更新行數據
509      * @param rowIndex
510      * @param t
511      */
512     public void update(int rowIndex,T t){
513         this.objs.set(rowIndex, t);
514         fireTableRowsUpdated(this.getColumnCount() - 1, this.getRowCount() - 1);
515     }
516     
517 }

 如果我們自己編寫TableModel的時候,在更新數據之后還得調用方法讓去通知視圖層去重新顯示,我們在繼承了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.

 下面介紹兩個概念:一個叫渲染,一個叫編輯。

我們先介紹渲染:我們需要知道的是其實每一個單元格都是繪制出來的,而每一個單元格其實也是一個組件(Component)。Swing為了性能的原因,對於每一列使用一種單元格渲染器來渲染所有的單元格。這是我們要介紹的重點。

JTable默認的渲染器是DefaultTableCellRenderer,通過查看源碼我們會發現原來這是一個JLabel啊!

/**
 * @(#)DefaultTableCellRenderer.java    1.48 08/09/18
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package javax.swing.table;

import javax.swing.*;
import javax.swing.table.TableCellRenderer;
import javax.swing.border.*;

import java.awt.Component;
import java.awt.Color;
import java.awt.Rectangle;

import java.io.Serializable;
import sun.swing.DefaultLookup;


/***
 * The standard class for rendering (displaying) individual cells
 * in a <code>JTable</code>.
 * <p>
 *
 * <strong><a name="override">Implementation Note:</a></strong>
 * This class inherits from <code>JLabel</code>, a standard component class. 
 * However <code>JTable</code> employs a unique mechanism for rendering
 * its cells and therefore requires some slightly modified behavior
 * from its cell renderer.  
 * The table class defines a single cell renderer and uses it as a 
 * as a rubber-stamp for rendering all cells in the table; 
 * it renders the first cell,
 * changes the contents of that cell renderer, 
 * shifts the origin to the new location, re-draws it, and so on.
 * The standard <code>JLabel</code> component was not
 * designed to be used this way and we want to avoid 
 * triggering a <code>revalidate</code> each time the
 * cell is drawn. This would greatly decrease performance because the
 * <code>revalidate</code> message would be
 * passed up the hierarchy of the container to determine whether any other
 * components would be affected.  
 * As the renderer is only parented for the lifetime of a painting operation
 * we similarly want to avoid the overhead associated with walking the
 * hierarchy for painting operations.
 * So this class
 * overrides the <code>validate</code>, <code>invalidate</code>,
 * <code>revalidate</code>, <code>repaint</code>, and
 * <code>firePropertyChange</code> methods to be 
 * no-ops and override the <code>isOpaque</code> method solely to improve
 * performance.  If you write your own renderer,
 * please keep this performance consideration in mind.
 * <p>
 *
 * <strong>Warning:</strong>
 * Serialized objects of this class will not be compatible with
 * future Swing releases. The current serialization support is
 * appropriate for short term storage or RMI between applications running
 * the same version of Swing.  As of 1.4, support for long term storage
 * of all JavaBeans<sup><font size="-2">TM</font></sup>
 * has been added to the <code>java.beans</code> package.
 * Please see {@link java.beans.XMLEncoder}.
 *
 * @version 1.48 09/18/08
 * @author Philip Milne 
 * @see JTable
 */
public class DefaultTableCellRenderer extends JLabel
    implements TableCellRenderer, Serializable
{

   /***
    * An empty <code>Border</code>. This field might not be used. To change the
    * <code>Border</code> used by this renderer override the 
    * <code>getTableCellRendererComponent</code> method and set the border
    * of the returned component directly.
    */
    private static final Border SAFE_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);
    private static final Border DEFAULT_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);
    protected static Border noFocusBorder = DEFAULT_NO_FOCUS_BORDER;

    // We need a place to store the color the JLabel should be returned 
    // to after its foreground and background colors have been set 
    // to the selection background color. 
    // These ivars will be made protected when their names are finalized. 
    private Color unselectedForeground; 
    private Color unselectedBackground; 

    /***
     * Creates a default table cell renderer.
     */
    public DefaultTableCellRenderer() {
    super();
    setOpaque(true);
        setBorder(getNoFocusBorder());
        setName("Table.cellRenderer");
    }

    private Border getNoFocusBorder() {
        Border border = DefaultLookup.getBorder(this, ui, "Table.cellNoFocusBorder");
        if (System.getSecurityManager() != null) {
            if (border != null) return border;
            return SAFE_NO_FOCUS_BORDER;
        } else if (border != null) {
            if (noFocusBorder == null || noFocusBorder == DEFAULT_NO_FOCUS_BORDER) {
                return border;
            }
        }
        return noFocusBorder;
    }

    /***
     * Overrides <code>JComponent.setForeground</code> to assign
     * the unselected-foreground color to the specified color.
     * 
     * @param c set the foreground color to this value
     */
    public void setForeground(Color c) {
        super.setForeground(c); 
        unselectedForeground = c; 
    }
    
    /***
     * Overrides <code>JComponent.setBackground</code> to assign
     * the unselected-background color to the specified color.
     *
     * @param c set the background color to this value
     */
    public void setBackground(Color c) {
        super.setBackground(c); 
        unselectedBackground = c; 
    }

    /***
     * Notification from the <code>UIManager</code> that the look and feel
     * [L&F] has changed.
     * Replaces the current UI object with the latest version from the 
     * <code>UIManager</code>.
     *
     * @see JComponent#updateUI
     */
    public void updateUI() {
        super.updateUI(); 
    setForeground(null);
    setBackground(null);
    }
    
    // implements javax.swing.table.TableCellRenderer
    /***
     *
     * Returns the default table cell renderer.
     * <p>
     * During a printing operation, this method will be called with
     * <code>isSelected</code> and <code>hasFocus</code> values of
     * <code>false</code> to prevent selection and focus from appearing
     * in the printed output. To do other customization based on whether
     * or not the table is being printed, check the return value from
     * {@link javax.swing.JComponent#isPaintingForPrint()}.
     *
     * @param table  the <code>JTable</code>
     * @param value  the value to assign to the cell at
     *            <code>[row, column]</code>
     * @param isSelected true if cell is selected
     * @param hasFocus true if cell has focus
     * @param row  the row of the cell to render
     * @param column the column of the cell to render
     * @return the default table cell renderer
     * @see javax.swing.JComponent#isPaintingForPrint()
     */
    public Component getTableCellRendererComponent(JTable table, Object value,
                          boolean isSelected, boolean hasFocus, int row, int column) {

        Color fg = null;
        Color bg = null;

        JTable.DropLocation dropLocation = table.getDropLocation();
        if (dropLocation != null
                && !dropLocation.isInsertRow()
                && !dropLocation.isInsertColumn()
                && dropLocation.getRow() == row
                && dropLocation.getColumn() == column) {

            fg = DefaultLookup.getColor(this, ui, "Table.dropCellForeground");
            bg = DefaultLookup.getColor(this, ui, "Table.dropCellBackground");

            isSelected = true;
        }

        if (isSelected) {
            super.setForeground(fg == null ? table.getSelectionForeground()
                                           : fg);
            super.setBackground(bg == null ? table.getSelectionBackground()
                                           : bg);
    } else {
            Color background = unselectedBackground != null
                                    ? unselectedBackground
                                    : table.getBackground();
            if (background == null || background instanceof javax.swing.plaf.UIResource) {
                Color alternateColor = DefaultLookup.getColor(this, ui, "Table.alternateRowColor");
                if (alternateColor != null && row % 2 == 0)
                    background = alternateColor;
            }
            super.setForeground(unselectedForeground != null
                                    ? unselectedForeground
                                    : table.getForeground());
            super.setBackground(background);
    }

    setFont(table.getFont());

    if (hasFocus) {
            Border border = null;
            if (isSelected) {
                border = DefaultLookup.getBorder(this, ui, "Table.focusSelectedCellHighlightBorder");
            }
            if (border == null) {
                border = DefaultLookup.getBorder(this, ui, "Table.focusCellHighlightBorder");
            }
            setBorder(border);

        if (!isSelected && table.isCellEditable(row, column)) {
                Color col;
                col = DefaultLookup.getColor(this, ui, "Table.focusCellForeground");
                if (col != null) {
                    super.setForeground(col);
                }
                col = DefaultLookup.getColor(this, ui, "Table.focusCellBackground");
                if (col != null) {
                    super.setBackground(col);
                }
        }
    } else {
            setBorder(getNoFocusBorder());
    }

        setValue(value); 

    return this;
    }
    
    /**
     * The following methods are overridden as a performance measure to 
     * to prune code-paths are often called in the case of renders
     * but which we know are unnecessary.  Great care should be taken
     * when writing your own renderer to weigh the benefits and 
     * drawbacks of overriding methods like these.
     */

    /***
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public boolean isOpaque() { 
    Color back = getBackground();
    Component p = getParent(); 
    if (p != null) { 
        p = p.getParent(); 
    }
        
    // p should now be the JTable. 
    boolean colorMatch = (back != null) && (p != null) && 
        back.equals(p.getBackground()) && 
            p.isOpaque();
    return !colorMatch && super.isOpaque(); 
    }

    /***
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     *
     * @since 1.5
     */
    public void invalidate() {}

    /***
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public void validate() {}

    /***
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public void revalidate() {}

    /***
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public void repaint(long tm, int x, int y, int width, int height) {}

    /***
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public void repaint(Rectangle r) { }

    /***
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     *
     * @since 1.5
     */
    public void repaint() {
    }

    /***
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {    
    // Strings get interned...
    if (propertyName=="text"
                || propertyName == "labelFor"
                || propertyName == "displayedMnemonic"
                || ((propertyName == "font" || propertyName == "foreground")
                    && oldValue != newValue
                    && getClientProperty(javax.swing.plaf.basic.BasicHTML.propertyKey) != null)) {

            super.firePropertyChange(propertyName, oldValue, newValue);
        }
    }

    /***
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { }


    /***
     * Sets the <code>String</code> object for the cell being rendered to
     * <code>value</code>.
     * 
     * @param value  the string value for this cell; if value is
     *        <code>null</code> it sets the text value to an empty string
     * @see JLabel#setText
     * 
     */
    protected void setValue(Object value) {
    setText((value == null) ? "" : value.toString());
    }
    

    /***
     * A subclass of <code>DefaultTableCellRenderer</code> that
     * implements <code>UIResource</code>.
     * <code>DefaultTableCellRenderer</code> doesn't implement
     * <code>UIResource</code>
     * directly so that applications can safely override the
     * <code>cellRenderer</code> property with
     * <code>DefaultTableCellRenderer</code> subclasses.
     * <p>
     * <strong>Warning:</strong>
     * Serialized objects of this class will not be compatible with
     * future Swing releases. The current serialization support is
     * appropriate for short term storage or RMI between applications running
     * the same version of Swing.  As of 1.4, support for long term storage
     * of all JavaBeans<sup><font size="-2">TM</font></sup>
     * has been added to the <code>java.beans</code> package.
     * Please see {@link java.beans.XMLEncoder}.
     */
    public static class UIResource extends DefaultTableCellRenderer 
        implements javax.swing.plaf.UIResource
    {
    }

}

在JTable中有這樣一段代碼:

/**
     * Creates default cell renderers for objects, numbers, doubles, dates,
     * booleans, and icons.
     * @see javax.swing.table.DefaultTableCellRenderer
     *
     */
    protected void createDefaultRenderers() {
        defaultRenderersByColumnClass = new UIDefaults(8, 0.75f);

        // Objects
        setLazyRenderer(Object.class, "javax.swing.table.DefaultTableCellRenderer$UIResource");

        // Numbers
        setLazyRenderer(Number.class, "javax.swing.JTable$NumberRenderer");

        // Doubles and Floats
        setLazyRenderer(Float.class, "javax.swing.JTable$DoubleRenderer");
        setLazyRenderer(Double.class, "javax.swing.JTable$DoubleRenderer");

        // Dates
        setLazyRenderer(Date.class, "javax.swing.JTable$DateRenderer");

        // Icons and ImageIcons
        setLazyRenderer(Icon.class, "javax.swing.JTable$IconRenderer");
        setLazyRenderer(ImageIcon.class, "javax.swing.JTable$IconRenderer");

        // Booleans
        setLazyRenderer(Boolean.class, "javax.swing.JTable$BooleanRenderer");
    }

JTableton在HashTable中存放Class對象作為KEY,渲染器的全稱作為Value,當調用TableModel中的getColumnClass的時候,然后就會去判斷到底對於這列的數據使用什么渲染方式,其實TableColumn也可以是設置CellRender。我們就來看看javax.swing.JTable$BooleanRender是怎么實現的。

 1 static class BooleanRenderer extends JCheckBox implements TableCellRenderer, UIResource
 2     {
 3         private static final Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
 4 
 5         public BooleanRenderer() {
 6             super();
 7             setHorizontalAlignment(JLabel.CENTER);
 8             setBorderPainted(true);
 9         }
10 
11         public Component getTableCellRendererComponent(JTable table, Object value,
12                                                        boolean isSelected, boolean hasFocus, int row, int column) {
13             if (isSelected) {
14                 setForeground(table.getSelectionForeground());
15                 super.setBackground(table.getSelectionBackground());
16             }
17             else {
18                 setForeground(table.getForeground());
19                 setBackground(table.getBackground());
20             }
21             setSelected((value != null && ((Boolean)value).booleanValue()));
22 
23             if (hasFocus) {
24                 setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
25             } else {
26                 setBorder(noFocusBorder);
27             }
28 
29             return this;
30         }
31     }

正所謂源碼面前,了無秘密。現在應該知道是怎么回事了吧。這里需要注意的是,我們返回的Class類型是包裝類,而不是原生數據類型。

好了,下面我們自己編寫兩個渲染器給大家瞧瞧。

        table.setDefaultRenderer(Object.class,
                new DefaultTableCellHeaderRenderer() {
                    @Override
                    public Component getTableCellRendererComponent(
                            JTable arg0, Object arg1, boolean arg2,
                            boolean arg3, int row, int arg5) {
                         super.getTableCellRendererComponent(
                                arg0, arg1, arg2, arg3, row, arg5);
                         if(row % 2 == 0){
                                this.setBackground(Color.BLUE);
                            }
                         return this;
                    }

                });

我們將TabeModel中的代碼所謂做修改

    /**
     * 返回TableCellRender渲染的類型
     */
    @Override
    public Class<?> getColumnClass(int columnIndex) {
//        if (pd != null) {
//            return pd[propertyInfo.get(columnIndex)].getPropertyType();
//        }
        return Object.class;
    }

怎么樣,SO Easy吧!

如果我們自己想要編寫特定的渲染器,我們可以實現TableCellRender接口來完成。這部分內容將在下篇中介紹、、、、、、、、

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

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



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