Java Swing (JTable详解1)

好久没有玩Swing了,算是练习英语,参考Sun公司官方给出的Java Tutorial的教程,来回顾一下JTable的用法,也希望大神来拍砖!

JTable顾名思义就是一个将数据以表格显示的组件,但是需要特别注意的是:因为Java Swing采用了MVC的设计,所以JTable不是用来存放数据的,它只是用来作为视图显示,而真正用来存储和维护数据的是TableModel这个接口的实现类。

Java Swing (JTable详解1)

 

 

 

从上面的图我们可以看出几个特点:

1.JTable的表头其实也是一个单独的组件,TableHeader

2.而每一列也可以被划分出来作为一个组件,TableColumn

3.JTable的数据选项不仅仅是可以显示字符串,还可以显示checkbox,img等等,其实只要你懂得如何去重写它内部的方法,你想怎么显示就怎么显示.

好了,废话不说了,来看

1.第一个例子(这个例子对官方的内容进行了少许变动,可以让别人更加理解这个属性的含义是什么)

Java Swing (JTable详解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 }
Java Swing (JTable详解1)

Java Swing (JTable详解1)

我在这里故意将表格的背景设置成YELLOW,可以让大家更加看清JTable在默认的情况下当它自身显示的大小小于容器时,它是不会去填充整个容器的,当设置setFillsViewportHeight为true的时候,表格就会填充整个容器。官方对于设置这个的作用是可以表格更加容易作为被拖拽的目标,但是我至今还没试过往表格里托东西,事后准备试试。。。

在上面的代码中还有一点需要注意的是:表格的显示通常都会将其放置在JScrollpane,如果不放置,那么表格的表头就无法显示,而此时如果要显示的话,那么就得将表头(TableHeader,前面已经提到了)作为一个组件显示,而JTable再作为一个组件显示。

 

Java Swing (JTable详解1)
container.setLayout(new BorderLayout());

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


container.add(table, BorderLayout.CENTER);
Java Swing (JTable详解1)

 

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

 (1).这个表格是可以被编辑的

 (2).我们发现,传入的数据是Boolan类型,按照正常情况下,应该显示成checkbox,但是这里却是以字符串的形式显示(比较第一个图可以看到,它最后显示的是一个checkbox,可以让用户查看更加的直观,而第二种却只能死板的显示字符串),而为什么会造成这样的原因呢,后面我们分析完源码就彻底明白了。

  (3).这里的创建需要我们自己先去创建数据或者Vector,但在很多情况下这种方式会对开发造成代码的冗余。

 比如:我们从数据库中查询到一组对象,此时就需要将对象转成数组或者集合
(当然,如果你用AOP来解决这类问题可能大材小用了)

 

2.修改单元格的宽度:

我们可以试着去观察一下上面的表格,我们发现每列都是一样的宽度,但是在实际的开发中可能会遇到对象的某些属性是内容特别长的,但是某些属性内容却是特别端,因为可能会导致内容较长的列被遮挡,造成不好的用户体验.因此,设置表格列的宽度也是实际中必不可少的部分。OK,下面来完成一个对表格列宽度的设置。

 

Java Swing (JTable详解1)
  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 }
Java Swing (JTable详解1)

可以显示一下效果,这边图片上传一直失败。还有一点要注意的是:表格的列默认情况下是可以拖动的,那么我们可以设置

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

Java Swing (JTable详解1)
    /**
     * @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;
Java Swing (JTable详解1)

默认情况下的值是2。上面代码中的注释已经说得很清楚了。

现在回到前面第一个案例中遗留下来的问题就是为什么有些单元格可以显示checkbox,有些只能显示字符串呢?

我们来看下面的这个例子:

Java Swing (JTable详解1)
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();
            }
        });
    }
}
Java Swing (JTable详解1)

此时再次运行上面的程序就可以显示checkbox。首先我们这里自己去创建了一个TableModel的实现类

Java Swing (JTable详解1)
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();
Java Swing (JTable详解1)

TableModel是一个接口,而我们自己所编写的TableModel是继承了AbstractTableModel,该类是一个抽象类,实现了一部分的方法。但是最最核心和还是

 

Java Swing (JTable详解1)
        /*
         * 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();
        }
Java Swing (JTable详解1)

从以上的注释我们可以知道:JTable是使用这个方法来决定对于每个单元格是如何渲染的,如果我们不实现这个方法,那么默认返回的值就是true或者false,而不是一个checkbox。

其本质的原因在于如果当返回的是一个Object.class,那么JTable在渲染的时候就是使用字符串去显示,如果你返回的是Boolean.class,那么就是以checkbox来渲染,而如果是Image.class,那么就可以以图片的显示来显示。我们的第一个例子中,直接使用数组的方式来构建JTable,它的低层是通过自身的一个TableModel来维护,这个类是一个匿名内部类,

Java Swing (JTable详解1)
 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);
            }
        });
    }
Java Swing (JTable详解1)

通过以上的源码我们可以看到,它是继承了AbstractTableModel,而AbstractTableModel中的getColumnClass方法返回的就是一个Object.class,所以无论传入什么内容,JTable在显示的时候都是通过字符串去显示。

AbstractTableModel的getColumnClass方法:

Java Swing (JTable详解1)
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();
            }
        });
    }
}
Java Swing (JTable详解1)

以上代码的核心是理解API中的几个方法:

selectRowAllowed是否允许选中行,selectColumnAllowed是否选中列,当设置两个方法都为true的时候,那么此时就是设置你选中的是单元格.如果两个都设置false,那么就表示什么都无法选中。最后,如果你只能选中一个单元格,还需要设置selectListModel为singleSelection,对于singleSelection方法中的参数是通过获取到ListSelectionModel中的常量来完成设置。

Java Swing (JTable详解1)
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);
}
Java Swing (JTable详解1)

 

TableModel:

在前面我们已经提到过TableModel是用来管理真实的数据.我们在实际的开发中通常会使用DefaultTableModel。在创建JTable的时候,如果你传入的是一个Object[],那么底层维护的TableModel是一个AbstractModel的匿名内部类,这才前面已经提到,但是如果你传入的是Vector,那么底层维护的是DefaultTableModel.

底层源码:

Java Swing (JTable详解1)
  public JTable(Vector rowData, Vector columnNames) {
        this(new DefaultTableModel(rowData, columnNames));
    }
Java Swing (JTable详解1)

但是我们最希望的是直接丢一个对象就去,然后显示。下面我们就自己编写一个TableModel来完成一下的操作(当然,如果你想要你的程序更加的灵活,那么在TableModel中使用反射和内省机制吧)。对于自己实现TableModel,我们通常会去继承AbstractTableModel.

下面我们自己编写一个基于面向对象的TableModel,然后你再拿和传统的方式去比较,让你马上感觉从Jdbc上升到Hibernate的级别。当然,要编写一个通用的组件可能还需要测试,这里我这是提供一种思路来给大家参考而已。

Java Swing (JTable详解1)
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;
    }
    
    

}
Java Swing (JTable详解1)

在这里,我自己定义了一个Annotation,用来定义这个属性在JTable中对应的表头名字。

 

Java Swing (JTable详解1)
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();//在表头的索引
}
Java Swing (JTable详解1)

好了,下面我们来测试一下写的程序。

Java Swing (JTable详解1)
  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 }
Java Swing (JTable详解1)

 如果我们自己编写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啊!

Java Swing (JTable详解1)
/**
 * @(#)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
    {
    }

}
Java Swing (JTable详解1)

在JTable中有这样一段代码:

Java Swing (JTable详解1)
/**
     * 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");
    }
Java Swing (JTable详解1)

JTableton在HashTable中存放Class对象作为KEY,渲染器的全称作为Value,当调用TableModel中的getColumnClass的时候,然后就会去判断到底对于这列的数据使用什么渲染方式,其实TableColumn也可以是设置CellRender。我们就来看看javax.swing.JTable$BooleanRender是怎么实现的。

Java Swing (JTable详解1)
 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     }
Java Swing (JTable详解1)

正所谓源码面前,了无秘密。现在应该知道是怎么回事了吧。这里需要注意的是,我们返回的Class类型是包装类,而不是原生数据类型。

好了,下面我们自己编写两个渲染器给大家瞧瞧。

Java Swing (JTable详解1)
        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;
                    }

                });
Java Swing (JTable详解1)

我们将TabeModel中的代码所谓做修改

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

Java Swing (JTable详解1)

怎么样,SO Easy吧!

如果我们自己想要编写特定的渲染器,我们可以实现TableCellRender接口来完成。这部分内容将在下篇中介绍、、、、、、、、

Java Swing (JTable详解1),布布扣,bubuko.com

Java Swing (JTable详解1)

上一篇:小程序接口请求数据


下一篇:C++学习之路—引用(一)—基础知识