在Java窗體表格中插入復選框


  最近接觸了一點Java的GUI編程,也就是由Java AWT更新而來的Java Swing。

  總體上而言,Java Swing編程有兩大特點:麻煩、效果差。

  麻煩是說由於設計器的使用不方便(如果您希望使用窗體設計器通過快速拖拽控件建立您的Java Swing GUI程序,請您使用MyEclipse 8.5以上版本,並且需要最高使用權限),所有代碼都得手寫,如果沒有好的編碼規范和注釋習慣。自己都會被代碼淹沒。

  效果差是指運行時的界面。具體的您可以自己嘗試發現。

  

  那么我們通過一段代碼來創建屬於我們的窗體:

 1 import javax.swing.JFrame;
 2 
 3 /**
 4  * 我的第一個Java窗體
 5  * 
 6  * @author Johness
 7  *
 8  */
 9 public class MyJFrame extends JFrame{
10     
11     
12     
13 }

  然后通過main方法來測試:

  

  運行后,窗體在屏幕左上角顯現並且是最小化的形式。

  

  呵呵,那么關於設置窗體的顯示我就不再贅述了,值得注意的是窗體的布局必須設置。

  小貼士:使用setLayout設置布局,參數傳遞null;

 

  我們討論一下怎樣在窗體的表格中顯示復選框。

  即實現如下效果:

  

  我隨便拖了些控件(數據是老師給的……)。

  好了,我們來一步步實現。

 

  小貼士二:使用add方法向控件添加內容控件。

  ①首先我們需要面板(JPanel)或其他容器控件承載表格(JTable),值得一提的是:由於窗體本身就是容器型控件,您可以考慮將表格單個地放置在窗體上。

  ②然后我們需要將表格對象創建出來並放入該容器控件,大家可以參考手冊(如JDK_API_1_6_zh_CN.CHM)創建表格控件。值得一提的是在這七個構造方法中,設計器(如果您使用了MyEclipse)使用的是JTable(TableModel dm)這個版本。而一般情況使用JTable(Vector rowData, Vector columnNames)這個版本的居多(不包括我)。如果是我,可能會選擇使用設計器的版本。

  可能有細心的朋友會發現說:設計器的版本很不方便,因為需要傳遞的是接口,我們必須寫一個類實現該接口並構造實例作為參數傳遞,麻煩,不如直接使用JTable(Object[][] rowData, Object[] columnNames)這個版本。

  那么在這里我向不知道“匿名內部類”(老師是這樣稱呼的,沒考證)的朋友普及一下Java的匿名內部類。

  在Java方法中,如果參數需要傳遞接口,可以在調用方法時傳遞一個(匿名)對象,該對象是一個不具名的類的實例,該對象所屬類實現了方法參數的接口。

  比如上面的例子JTable(TableModel dm),這是JTable的構造方法,需要的是一個TableModel接口類型的參數(這里只是舉例,實際運用比較復雜),我們可以使用如下寫法:JTable table = new JTable(new TableModel());

  毫無疑問,這種寫法是錯誤的,但是如果這樣寫就不是了:

 1 import javax.swing.*;
 2 import javax.swing.event.*;
 3 import javax.swing.table.*;
 4 
 5 
 6 public class MyFirstJFrame extends JFrame {
 7     public MyFirstJFrame() {
 8         setLayout(null);
 9         
10         JTable table = new JTable(new TableModel(){
11 
12             @Override
13             public int getRowCount() {
14                 // TODO Auto-generated method stub
15                 return 0;
16             }
17 
18             @Override
19             public int getColumnCount() {
20                 // TODO Auto-generated method stub
21                 return 0;
22             }
23 
24             @Override
25             public String getColumnName(int columnIndex) {
26                 // TODO Auto-generated method stub
27                 return null;
28             }
29 
30             @Override
31             public Class<?> getColumnClass(int columnIndex) {
32                 // TODO Auto-generated method stub
33                 return null;
34             }
35 
36             @Override
37             public boolean isCellEditable(int rowIndex, int columnIndex) {
38                 // TODO Auto-generated method stub
39                 return false;
40             }
41 
42             @Override
43             public Object getValueAt(int rowIndex, int columnIndex) {
44                 // TODO Auto-generated method stub
45                 return null;
46             }
47 
48             @Override
49             public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
50                 // TODO Auto-generated method stub
51                 
52             }
53 
54             @Override
55             public void addTableModelListener(TableModelListener l) {
56                 // TODO Auto-generated method stub
57                 
58             }
59 
60             @Override
61             public void removeTableModelListener(TableModelListener l) {
62                 // TODO Auto-generated method stub
63                 
64             }});
65     }
66 }

  我可能需要解釋一下這些代碼:首先是JTable table = new JTable(new TableModel(){});可以看出來,大括號中間的部分是一些需要重寫的方法。

  那么大家應該怎樣理解這一句代碼呢?我們分解一下(new TableModel(){})。我們應該怎么看待?大家回想一下我以上說過的匿名內部類的定義。我們可以這樣看,new ……()是構造方法,調用來構造一個匿名對象,其后的{}不是Java的特殊語法,但是Java中可以將方法定義在里面(這里的方法生命周期與匿名對象相同),當然,此處是用於實現接口的方法。

  清晰一點了吧?我們再來拆分:TableModel我們可以在其前面補充一個不存在的類類名,比如MyTableModel。好了,我們完整再現一下:new MyTableModel:TableModel(){}也就是說大家可以想象成(new TableModel(){})是在聲明一個匿名對象,它屬於一個不具名的類(如MyTableModel),該類實現了TableModel接口。而由於語法限制,不能全部寫出來所以省略了[MyTableModel:]。當然,這只是我們的推理,大家理解記憶哈。

  注:這里的匿名對象只沒有引用指向(即沒有變量名)的對象。

  實際上我們使用匿名內部類的地方很多,比如添加事件監聽。但是“上面創建JTable的方法是只作為示例,絕大多數是不會如此用的”,大家謹記。

  我會在隨筆結尾貼出全部代碼,其中創建JTable的代碼是使用了設計器的構造方式。

  ③設置表格渲染。在詳細說明之前我先解釋一下JTable的顯示原理:

  首先是數據來源,您使用JTable的構造方法,大部分重載中參數即包含了數據,比如JTable(Vector rowData, Vector columnNames)中Vector保存的數據(Vector相當於數組)。

  其次是表格樣式,表格將數據和如何顯示數據(比如列數量、列名稱、是否可編輯)保存在其數據模版中,該模版實現自接口TableModel。

  最后,表格(每一個單元格)可以設置渲染效果。

  我把完整的代碼貼出來:

  1 import java.awt.Component;
  2  import java.awt.event.ActionEvent;
  3  import java.awt.event.ActionListener;
  4  
  5  import javax.swing.*;
  6  import javax.swing.table.*;
  7  
  8  
  9  public class MyFirstJFrame extends JFrame {
 10      
 11      // 作為測試的main方法
 12      public static void main(String[] args) {
 13          new MyFirstJFrame().setVisible(true);
 14      }
 15      
 16      /**
 17       * 構造方法
 18       */
 19      public MyFirstJFrame() {
 20          InitialComponent();
 21      }
 22      
 23      /**
 24       * 初始化組件的方法
 25       */
 26      private void InitialComponent(){
 27          // 設置窗體參數
 28  
 29          // 設置布局模式
 30           setLayout(null);
 31          // 設置窗體大小
 32          setSize(480, 360);
 33          // 設置窗體居中(非常規方法)
 34           setLocationRelativeTo(null);
 35          // 關閉窗體退出程序
 36          setDefaultCloseOperation(DISPOSE_ON_CLOSE);
 37          
 38          // 初始化面板
 39          panel = new JPanel();
 40          panel.setSize(this.getWidth(), this.getHeight());
 41          panel.setLocation(0,0);
 42          panel.setLayout(null);
 43          
 44          // 初始化表格
 45          table = new JTable(new DefaultTableModel(new Object[][]{{"第一行"},{"第二行"},{"第三行"},{"第四行"}}, new String[]{"測試行1","測試行2"}){
 46              /* (non-Javadoc)
 47               * 重寫方法,判斷表單元格是否可編輯
 48               * 可以通過row和column索引判斷某一個單元格是否可編輯
 49               * 此處設為都不可編輯
 50               * @see javax.swing.table.DefaultTableModel#isCellEditable(int, int)
 51               */
 52              @Override
 53              public boolean isCellEditable(int row, int column) {
 54                  return false;
 55              }
 56          });
 57          
 58          // 開始向表格中添加復選框(注意:此示例較為簡單,缺省很多判斷,也沒有動態代碼支持)
 59          // 通過設置列渲染
 60          
 61          // 方法一:直接方式 使用TableColumn的setCellRenderer方法(推薦)
 62          // 此方法可以設置某一列的渲染(即使用某一個組件--即控件來顯示單元格數據)
 63          table.getColumnModel().getColumn(1).setCellRenderer(new TableCellRenderer(){
 64  
 65               /*(non-Javadoc)
 66               * 此方法用於向方法調用者返回某一單元格的渲染器(即顯示數據的組建--或控件)
 67               * 可以為JCheckBox JComboBox JTextArea 等
 68               * @see javax.swing.table.TableCellRenderer#getTableCellRendererComponent(javax.swing.JTable, java.lang.Object, boolean, boolean, int, int)
 69               */
 70              @Override
 71              public Component getTableCellRendererComponent(JTable table,
 72                      Object value, boolean isSelected, boolean hasFocus,
 73                      int row, int column) {
 74                  // 創建用於返回的渲染組件
 75                  JCheckBox ck = new JCheckBox();
 76                  // 使具有焦點的行對應的復選框選中
 77                  ck.setSelected(isSelected);
 78                  // 設置單選box.setSelected(hasFocus);
 79                  // 使復選框在單元格內居中顯示
 80                  ck.setHorizontalAlignment((int) 0.5f);
 81                  return ck;
 82              }});
 83          
 84          // 方法二:先設置列編輯器,然后設置單元格渲染
 85          // 設置列編輯器
 86          // 在以復選框為對象設置列編輯器時,必須保證該列能夠被編輯,否則無法更改狀態
 87          // (此步驟可以省略,省略時不要忘記將列設為不可編輯)
 88          // table.getColumnModel().getColumn(1).setCellEditor(new DefaultCellEditor(new JCheckBox()));
 89          
 90          // 設置單元格渲染(這里是設置表格級別的渲染)
 91          /*table.setDefaultRenderer(Object.class, new TableCellRenderer(){
 92  
 93              @Override
 94              public Component getTableCellRendererComponent(JTable table,
 95                      Object value, boolean isSelected, boolean hasFocus,
 96                      int row, int column) {
 97                  // 判斷是否為需要渲染的列
 98                  if(column == 1){
 99                      // 和方法一基本一致
100                      JCheckBox box = new JCheckBox();
101                      box.setSelected(isSelected);
102                      // 設置單選box.setSelected(hasFocus);
103                      box.setHorizontalAlignment((int) CENTER_ALIGNMENT);    // 0.5f
104                      return box;
105                      }
106                  // 如果不是需要渲染的列,封裝文本域顯示數據
107                  return new JTextArea(value.toString());
108              }});*/
109          
110          // 在多選是需要按住Ctrl鍵或者鼠標按住拖過連續的需要選中的行,應該給用戶說明
111          // 第一種方法是被推薦的,因為它具有選中的高亮顯示,界面能更加友好
112          table.setSize(panel.getWidth(),panel.getHeight() - 90);
113          table.setLocation(0, 0);
114          
115          
116          btn = new JButton("Test");
117          btn.setSize(80,40);
118          btn.setLocation((panel.getWidth()) / 2 - 40, panel.getHeight() - 80);
119          
120          // 按鈕點擊時顯示當前選中項
121          btn.addActionListener(new ActionListener(){
122  
123              @Override
124              public void actionPerformed(ActionEvent e) {
125                  for(int rowindex : table.getSelectedRows()){
126                      JOptionPane.showMessageDialog(null, rowindex + " " + table.getValueAt(rowindex, 0));
127                  }
128              }});
129          
130          panel.add(table);
131          panel.add(btn);
132          this.add(panel);    
133          
134      }
135      
136      // 定義一些必要的組件
137      private JPanel panel;
138      private JTable table;
139      private JButton btn;
140  }

 

  上面的代碼有一些缺陷,大家需要做一些修改。實際上我也不希望貼上完全無誤的perfect的代碼,對需要學習的朋友不是好事兒。

 

  總結:充分理解Java的方法返回值作為判斷依據。

  1、匿名內部類(匿名對象后{}的妙用)。

  2、窗體的布局:默認布局為(最后添加?)的控件占據其窗體的全部空間。

  3、編輯器、渲染。

 

  最近斷斷續續地看WPF了,因為在看C語言了……

  我創建了QQ群:35142661。作為能夠與我一起學習或者指導我學習的朋友們近來討論。

  2012-05-02 18:42:59


免責聲明!

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



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