zeyuphoenix

          愿我愛的人快樂,愿愛我的人快樂,為了這些,我愿意不快樂.

          表格(單元格放置組件)

          對于JTable單元格的渲染主要是通過兩個接口來實現的,一個是TableCellRenderer另一個是TableCellEditor,JTable默認是用的是DefaultCellRendererDefaultCellEditor,這兩個都是在類似JTextfield的一個JComponent的基礎上來實現的,如果我們需要在JTable的單元格內放置特殊的控件或者繪制出特殊的效果,就要實現TableCellRendererTableCellEditor接口,在其上繪制出自己需要的樣式,再通過JTablesetCellRenderersetCellEditor方法設置新的外觀呈現.

          首先我們先看看TableCellRendererTableCellEditor接口的區別, TableCellRenderer接口就是用來繪制和展示當前單元格的內容的,可以用文字、圖片、組件、甚至Java2D來繪制效果; TableCellEditor主要是用來當用戶點擊具體的某個單元格進行編輯的時候來展現的,除了繪制之外,在點擊時還會有更加復雜的效果出現.

          先看Sun官方給的簡單的例子,首先是TableCellRenderer

          運行圖示如下:

          我們只需要實現TableCellRenderer就可以了,

          /**

           * This interface defines the method required by any object * that would like to be a renderer for cells in a JTable

           * in there, I put button in it.

          */

          publicclass MyButtonRenderer extends JButton implements TableCellRenderer {

          實現接口的方法:

              @Override

              public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

          然后設置屬性:

          setForeground(table.getSelectionForeground());

                 setBackground(table.getSelectionBackground());

          setText((value == null) ? "" : value.toString());

          使用也很簡單,假如我們希望第一列是JButton,

          table.getColumnModel().getColumn(0).setCellRenderer(new MyButtonRenderer());

          接著是TableCellEditor的實現,還是Sun給的例子:

          運行圖示如下

          Sun公司在DefaultCellEditor類里提供了JComboBox參數的構造函數,直接使用就可以了.

             //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");

          table.getColumnModel().getColumn(2).setCellEditor(new DefaultCellEditor(comboBox));

          在這里看來,這個例子就可以了,但是它還是有問題的,什么問題呢,看下截圖:

          JTable的單元格比較短時,下拉框顯示的內容會出現不全的情況,需要修改一下:

          問題在哪兒呢,在于JComboboxUI,需要設置一下JCombobox的下拉菜單的寬度,具體實現在JCombobox那篇文章里已經實現了,這里我們直接使用,

                  String[] str = new String[] { "Snowboarding", "Rowing", "Knitting", "Speed reading", "None of the above" };

                  MyComboBox combo = new MyComboBox(str);

                  Dimension d = combo.getPreferredSize();

                  combo.setPopupWidth(d.width);

                  table.getColumnModel().getColumn(2).setCellEditor(newDefaultCellEditor(combo));

          運行如下圖:

          到此為止,RendererEditor的簡單實用就完成了,這些例子都是Sun官方給的,我大概

          修改了一下,其實還有問題.

          讓我們回頭看第一個例子:

          當鼠標在JButton按下時,如下圖:

          JButton的效果消失了,因為Renderer只是處理表示的樣式,對于可編輯的單元格就不可

          以了,編輯狀態下呈現的還是默認的JTextField組件,所以對于可編輯的單元格,我們需

          要設置它的Editor.

          我們需要寫一個自己的Editor,為了簡單就不實現TableCellEditor接口了,只需要繼

          DefaultCellEditor.

          /**

           * The default editor for table and tree cells.

           */

          publicclass MyButtonCellEditor extends DefaultCellEditor {

           

          定義兩個屬性:

              //editor show

              private JButton button = null;

              //text

          private String label = null;

          分別代表編輯狀態下顯示的組件和顯示的值.

          然后重寫getTableCellEditorComponent方法,在編輯狀態表示我們自己的組件.

              /**

               * Sets an initial <code>value</code> for the editor.

               */

              @Override

          public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {

          設置組件樣式:

             button.setForeground(table.getSelectionForeground());

             button.setBackground(table.getSelectionBackground());

             label = (value == null) ? "" : value.toString();

             button.setText(label);

             returnbutton;

          然后還需要重寫getCellEditorValue方法,返回編輯完成后的值,

          @Override

              public Object getCellEditorValue() {

                  returnnew String(label);

          }

          使用和以前設置RendererEditor一樣,設置2個就可以了.

          table.getColumnModel().getColumn(0).setCellRenderer(new MyButtonRenderer());

          table.getColumnModel().getColumn(0).setCellEditor(new MyButtonCellEditor());

          最后按下效果正常了:


          到此為止,簡單的RendererEditor就差不多了,但是我們在JTable放置的都是基本的Swing組件,可不可以放置復雜的呢,當然是可以的,下面我們放置一個選擇組:

          如下圖:

          它也需要實現自己的RendererEditor,我們可以把這個顯示選擇按鈕組的單元格看做一個組件,當然首先就是把這個組件作出來:

          /**

           * create the pane that some radio pane in it.

          */

          publicclass MyRadioPanel extends JPanel {

          它只有一個屬性,根據給定數組長度構建Radio數組,

              /** radio button group. */

              private JRadioButton[] buttons = null;

          再看它的構造函數:

              public MyRadioPanel(String[] strButtonText) {

          我們在這里構造JRadioButton

          buttons[i] = new JRadioButton(strButtonText[i]);

          加入到面板:

          add(buttons[i]);

          再添加取得和設置JRadioButton選擇的方法:

              /**

               * get button groups.

               */

              public JRadioButton[] getButtons() {

                 returnbuttons;

              }

              /**

               * set which index select.

               */

              publicvoid setSelectedIndex(int index) {

                 for (int i = 0; i < buttons.length; i++) {

                     buttons[i].setSelected(i == index);

                 }

              }

          然后就是寫Renderer,我們繼承MyRadioPanel并且實現TableCellRenderer接口就可以了.

          publicclass MyRadioCellRenderer extends MyRadioPanel implements

                 TableCellRenderer {

          構造函數直接使用MyRadioCellRenderer

              public MyRadioCellRenderer(String[] strButtonTexts) {

                 super(strButtonTexts);

              }

          然后是實現接口的getTableCellRendererComponent方法:

              @Override

              public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

                 if (value instanceof Integer) {

                     setSelectedIndex(((Integer) value).intValue());

                 }

                 returnthis;

          }

          最后就是Editor,

          /**

           * create cell editor that radio in it.

          */

          publicclass MyRadioCellEditor extends DefaultCellEditor implements

                 ItemListener {

          在它的構造函數里我們為JRadioButton添加監聽:

          JRadioButton[] buttons = panel.getButtons();

          buttons[i].addItemListener(this);

          在監聽處理中我們停止編輯,

              @Override

              publicvoid itemStateChanged(ItemEvent e) {

                 super.fireEditingStopped();

              }

          然后我們需要覆蓋DefaultCellEditorgetTableCellEditorComponent,返回我們需要顯示的MyRadioPanel.

              @Override

              public Component getTableCellEditorComponent(JTable table, Object value,

                     boolean isSelected, int row, int column) {

                 if (value instanceof Integer) {

                     panel.setSelectedIndex(((Integer) value).intValue());

                 }

                 returnpanel;

              }

          最后我們重寫getCellEditorValue,返回編輯完成后我們顯示的值:

          @Override

              public Object getCellEditorValue() {

                 returnnew Integer(panel.getSelectedIndex());

              }

          使用也很簡單,和前面設置RendererEditor一樣:

          String[] answer = { "A", "B", "C" };

          table.getColumnModel().getColumn(1).setCellRenderer(

                new MyRadioCellRenderer(answer));

          table.getColumnModel().getColumn(1).setCellEditor(

                new MyRadioCellEditor(newMyRadioCellRenderer(answer)));

          接下來我們看一個比較綜合的例子,首先還是從畫面開始:

          先從簡單的開始做起,首先使JTable的第三列顯示成進度條,這個和前面的設置Renderer一樣,實現TableCellRenderer就可以了.

          /**

           * This interface defines the method required by any object * that would like to be a renderer for cells in a JTable

           * in there, I put progress bar in it.

          */

          publicclass MyProgressCellRenderer extends JProgressBar implements

                 TableCellRenderer {

          它提供一個屬性放置各個顏色區間需要設置的顏色:

              /** the progress bar's color. */

              private Hashtable<Integer, Color> limitColors = null;

          在構造函數里我們設置顯示的最大和最小值:

              /**

              * Creates a progress bar using the specified orientation, * minimum, and maximum.

               */

              public MyProgressCellRenderer(int min, int max) {

                 super(JProgressBar.HORIZONTAL, min, max);

                 setBorderPainted(false);

              }

          然后實現TableCellRenderer接口的getTableCellRendererComponent方法,設置顯示組件和顏色:

              @Override

              public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

          先根據單元格的值取得顏色:

                  Color color = getColor(n);

                 if (color != null) {

                     setForeground(color);

                 }

          同時設置JProcessBar的值并返回它.

                 setValue(n);

                 returnthis;

          最后還提供一個設置顏色的方法:

          publicvoid setLimits(Hashtable<Integer, Color> limitColors) {

          它把傳入的顏色表按照大小先排序,然后設置好.

          這樣一個簡單的顯示進度條的TabelCellRenderer就完成了.然后通過setRenderer來使用它.

          //create renderer.

           MyProgressCellRenderer renderer = new MyProgressCellRenderer(

                  MyProgressTableModel.MIN, MyProgressTableModel.MAX);

           renderer.setStringPainted(true);

           renderer.setBackground(table.getBackground());

           // set limit value and fill color

           Hashtable<Integer, Color> limitColors = new Hashtable<Integer, Color>();

           limitColors.put(new Integer(0), Color.green);

           limitColors.put(new Integer(20), Color.GRAY);

            limitColors.put(new Integer(40), Color.blue);

           limitColors.put(new Integer(60), Color.yellow);

           limitColors.put(new Integer(80), Color.red);

           renderer.setLimits(limitColors);

           //set renderer      table.getColumnModel().getColumn(2).setCellRenderer(renderer);

          然后我們需要考慮的是這個Renderer的值無法變化,只能根據初始化的時候的數值顯示,這明顯是不行的,所以我們考慮給JTable加上改變,改變第二列的數字,第三列進度條隨之改變,如圖示:



          這時我們需要修改我們的TableModel,默認的已經無法滿足我們的需要了,我們需要自己寫一個:

          publicclass MyProgressTableModel extends DefaultTableModel {

          在它的構造函數里面,我們增加一個監聽:

          this.addTableModelListener(new TableModelListener() {

                     @Override

                     publicvoid tableChanged(TableModelEvent e) {

          當引起TableModel改變的事件是UPDATE時并且是第二列時候:

               //when table action is update.

              if (e.getType() == TableModelEvent.UPDATE) {

                    int col = e.getColumn();

                    if (col == 1) {

          我們取得新設立的value,賦予第三列:

              //get the new set value.

              Integer value = (Integer) model.getValueAt(row, col);

              model.setValueAt(checkMinMax(value), row, ++col);

          重寫isCellEditable方法,設置可編輯的列:

              @Override

              publicboolean isCellEditable(int row, int col) {

                 switch (col) {

                 case 1:

                     returntrue;

                 default:

                     returnfalse;

                 }

              }

          重寫setValueAt方法,設置可賦予的值:

              @Override

              publicvoid setValueAt(Object obj, int row, int col) {

          這樣一個我們需要的TableModel就完成了,修改第二列的值,第三列進度條也隨之改變,使用也很簡單:

                  // set the table model.

                  table.setModel(dm);

          就可以了.

          到這里,這個進度條JTable基本完成了,但是在實際運用中可能會出現這樣的問題:

          我們編輯JTable的時候給它的單元格賦予了一個不正常的值,導致顯示不正常,但是卻無法返回舊有的狀態,這樣我們就需要再次改進它:

          當輸入錯誤的值時:

          然后可以返回以前的狀態:

          這時候我們需要設置的是第二列的Editor,使它編輯狀態時可以驗證我們的輸入,并觸發:

          /**

           * Implements a cell editor that uses a formatted text

           * field to edit Integer values.

           */

          publicclass MyIntegerEditor extends DefaultCellEditor {

          它有一個參數,用來處理編輯值的:

              //show component when cell edit

              private JFormattedTextField ftf;

          然后重寫DefaultCellEditorgetTableCellEditorComponent方法,返回我們定義的JFormattedTextField.

          JFormattedTextField ftf = (JFormattedTextField) super

          .getTableCellEditorComponent(table, value, isSelected, row, column); ftf.setValue(value);

          return ftf;

          重寫getCellEditorValue方法,保證我們返回值正確:

          getCellEditorValue

           @Override

              public Object getCellEditorValue() {

          取得編輯完成的值:

              Object o = ftf.getValue();

          判斷然后返回.

          然后重寫stopCellEditing方法,判斷編輯的值是否正確,不正確的情況下提示用戶,詢問用戶是返回還是重新設置.

              // Override to check whether the edit is valid,

              // setting the value if it is and complaining if it isn't.

              @Override

              publicboolean stopCellEditing() {

                  JFormattedTextField ftf = (JFormattedTextField) getComponent();

                  if (ftf.isEditValid()) {

                      try {

                          ftf.commitEdit();

                      } catch (java.text.ParseException exc) {

                      }

                  } else { // text is invalid

                      if (!userSaysRevert()) {

                          // user wants to edit don't let the editor go away

                          returnfalse;

                      }

                  }

                  returnsuper.stopCellEditing();

              }

          到目前為止,這個類基本完成了,但是只有焦點離開單元格才觸發驗證事件,比較不和邏輯,我們加入一個鍵盤監聽,回車也可以觸發.

          ftf.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "check");

          ftf.getActionMap().put("check", new AbstractAction() {

          @Override

                publicvoid actionPerformed(ActionEvent e) {

                     // The text is invalid.

                     if (!ftf.isEditValid()) {

                         if (userSaysRevert()) {

                             // reverted inform the editor

                             ftf.postActionEvent();

                         }

                      } else

                         try {

                             // The text is valid, so use it.

                             ftf.commitEdit();

                             // stop editing

                             ftf.postActionEvent();

                          } catch (java.text.ParseException exc) {

                          }

                }

          然后就可以使用它了,和前面設置一個Editor一樣:

             table.getColumnModel().getColumn(1).setCellEditor(

                  new MyIntegerEditor(MyProgressTableModel.MIN,

                                  MyProgressTableModel.MAX));

          到目前為止,JTableRendererEditor就完成了,實際使用中也就這樣了,但是還有一種特殊情況需要說一下,雖然這樣變態需求一般現實中很難碰到.上面我們所有的例子都是對某一個列來說的,但是如果有人需要第一行顯示正常單元格,第二行顯示JCombobox,第三行顯示JButton怎么處理呢.其實也相差不大,自己寫個RendererEditor,里面實現一個RendererEditor的序列,依次展現就可以了.

          先看圖:



          首先要做的寫一個類實現TableCellEditor接口,

          publicclass MyCellEditor implements TableCellEditor {

          它有兩個屬性:

              /** save all editor to it. */

              private Hashtable<Integer, TableCellEditor> editors = null;

              /** each cell editor. */

              private TableCellEditor editor = null;

          分別存儲了此Editor上所有的Editor隊列和當前需要使用的Editor.

          再看它的構造函數,

              /**

               * Constructs a EachRowEditor. create default editor

               */

              public MyCellEditor(JTable table) {

          它初始化了Editor隊列

          editors = new Hashtable<Integer, TableCellEditor>();

          然后實現TableCellEditor接口的getTableCellEditorComponent方法

              @Override

              public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {

          根據行號取得當前單元格的Editor

          editor = (TableCellEditor) editors.get(new Integer(row));

          沒有的話,使用默認的:

          if (editor == null) {

                      editor = new DefaultCellEditor(new JTextField());

                  }

          然后返回當前Renderer下的單元格:

               returneditor.getTableCellEditorComponent(table, value, isSelected, row, column);

          接著實現stopCellEditingcancelCellEditingaddCellEditorListener

          removeCellEditorListenerisCellEditableshouldSelectCell方法,

          在這些方法里取得當前那個單元格被編輯,取得正編輯的單元格的Editor,再調用Editor

          同樣的方法就可以了.

                 if (e == null) {

                    row = table.getSelectionModel().getAnchorSelectionIndex();

                  } else {

                      row = table.rowAtPoint(e.getPoint());

                  }

                  editor = (TableCellEditor) editors.get(new Integer(row));

                  if (editor == null) {

                      editor = new DefaultCellEditor(new JTextField());

                  }

          最后提供一個設置單元格Editor的方法,

              /**

               * add cell editor to it.

               */

              publicvoid setEditorAt(int row, TableCellEditor editor) {

                  editors.put(new Integer(row), editor);

              }

          這樣可以實現單元格級別的Editor就實現了,同樣的Renderer也一樣,同樣實現TableCellRenderer接口和它里面的方法就可以了,同樣用對列存儲每個單元格的Renderer,這里就不寫了.

          最后是使用:

          先創建JTable需要用到的Editor,再創建單一Cell用到的Editor,

           //create all cell editor

           MyCellEditor rowEditor = new MyCellEditor(table);

            //create cell editors

            MyButtonCellEditor buttonEditor = new MyButtonCellEditor();

           DefaultCellEditor comboBoxEditor = new

          DefaultCellEditor(comboBox);

          然后為需要的單元格設置Editor,

           //put cell editor in all cell editors

           rowEditor.setEditorAt(0, comboBoxEditor);

           rowEditor.setEditorAt(1, comboBoxEditor);

           rowEditor.setEditorAt(2, buttonEditor);

           rowEditor.setEditorAt(3, buttonEditor);

          最后設置JTableEditor,

           //set table editor

           table.getColumnModel().getColumn(0).setCellEditor(rowEditor);

          同樣的,RendererEditor完全一樣.這樣一個可以為具體單元格設置RendererEditor的例子就完成了.

          到此為止,關于在JTable的單元格放置組件的例子就全部完成了,總結起來也很簡單,就是設置RendererEditor,至于更復雜的效果,比如合并單元格之類的,就需要重寫JTableTableUI了,這就在以后說了

          posted on 2010-04-07 23:19 zeyuphoenix 閱讀(6328) 評論(0)  編輯  收藏 所屬分類: JTable的使用

          導航

          <2010年4月>
          28293031123
          45678910
          11121314151617
          18192021222324
          2526272829301
          2345678

          統計

          常用鏈接

          留言簿(52)

          隨筆分類

          隨筆檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 嘉义市| 蒙城县| 金堂县| 辽阳县| 陈巴尔虎旗| 高邮市| 高要市| 钟山县| 和政县| 中阳县| 石屏县| 屏边| 东辽县| 赣榆县| 古田县| 瑞金市| 鹤壁市| 当阳市| 武川县| 行唐县| 若尔盖县| 全州县| 宣城市| 镇安县| 保康县| 伊宁市| 蓬安县| 河东区| 南岸区| 普兰店市| 大埔区| 威远县| 万荣县| 项城市| 巩留县| 台中县| 黄骅市| 温州市| 濮阳县| 平度市| 荥经县|