當很表格中有很多列的時候出現Scrollbar的時候,當用戶拖動Scrollbar那麼有的列就會看不見,而用戶需要輸入數據的時候,需要對照第一列或前幾列以方便輸入數據,則需要固定前幾列的需求了。像Excle表格中可以固定前幾列,而在JTable中沒有直接的方法實現,網上比較流行的方法是用兩個JTable,如下圖(一)其中一個talbe渲染固定列的數據,另外一個主table渲染其他數據,然後把渲染固定列數據的表格當做裝載主table的JScrollPane的Row Header。這樣實現就要把表格中的數據拆分成兩個TableModel用於其渲染。
其中實現的效果如圖二
圖一
圖二
很顯然這種實現方式有一下缺點:
1)、當用戶對於自己的表格有自己實現的自定義的列頭,比如行的序列號或增加了選擇框等等這樣就會有沖突。
2)、如上所說當拆分成兩個TableModel的時候,那麼要很好的維護兩個表格一些屬性保持一致將很麻煩,比如選擇,排序等。
3)、最重要的一點就是如果在其原有的項目中增加這一需求,那麼這種方法就要修改很多地方,侵入性太強。
基於上述缺點,Elie Levy 用了另外一種方法,盡管也有些缺點(暫且後面再說),他實現的方法很簡單(效果如圖三),就是將要固定的列的內容畫在一個另外一個組件上然後將這個組件放在JTable之上,其總是占據其表格的指定需要固定的列上,這表格的前幾列看起來就是固定了的,如圖三,我們需要固定前三列,那麼我們將前三列的內容畫在一個Label上如圖中黑色部分,這時候的關鍵技術就是利用JLayeredPane的原理了,獲得JTable的JLayeredPane,然後將這個畫出所要固定列的內容的JLabel加進JLayeredPane,就能忽悠成固定了。
圖三
主要實現代碼分析,實現主要監聽鼠標事件,捕捉所要固定的列的內容予以即時更新,這個類FixTableManager為了方便我們繼承於JTableHeader,那麼在這裡我們重寫paint()方法:以更新拖動Scrollbar的時候列頭的現實信息,代碼如:
@Override
public void paint(Graphics g) {
super.paint(g);
//int division = mouseListener.getDivision();
int division = mouseListener.getDivision();
if (division > 0){
Rectangle r = getVisibleRect();
BufferedImage image = new BufferedImage(division, r.height,
BufferedImage.TYPE_INT_ARGB);
Graphics g2 = image.getGraphics();
g2.setClip(0, 0, division, r.height);
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, division, r.height);
super.paint(g2);
g.drawImage(image, r.x, r.y, division, r.height, null);
g2.dispose();
}
}
畫完了固定的列的列頭,我們就要畫表格中的內容了,這裡我們就是把這些內容畫在一個JLabel上,如下:
private class FixedColumnsDelegate extends JLabel{
public void paintComponent(Graphics g) {
Rectangle r = table.getBounds();
if (division > 0) {
table.invalidate();
table.validate();
Rectangle visibleRect = table.getVisibleRect();
BufferedImage image = new BufferedImage(division, r.height,
BufferedImage.TYPE_INT_ARGB);
Graphics g2 = image.getGraphics();
g2.setClip(0, visibleRect.y, division,
table.getBounds().height);
g2.setColor(Color.RED);
g2.fillRect(0, 0, division, table.getBounds().height);
table.paint(g2);
g.drawImage(image, 0, 0, division,
table.getBounds().height, 0, visibleRect.y,
division, visibleRect.y + table.getBounds().height,
null);
// g.setColor(Color.BLACK);
// for (int i = 0; i < visibleRect.y
// + table.getBounds().height; i += 8) {
// g.drawLine(division - 1, i, division - 1, i + 4);
// g.drawLine(division - 2, i, division - 2, i + 4);
// }
g2.dispose();
}
}
}
接下來就是鼠標等一些事件來監聽畫出固定列的信息了。當捕捉到需要將固定的列固定住,就調用如下方法:
/** *//**
*固定列
*利用JLayeredPane使其顯示在JTable之上
**/
public void freeze() {
JLayeredPane pane = table.getRootPane().getLayeredPane();
if (added) {
pane.remove(fixedColumns);
}
pane.add(fixedColumns, JLayeredPane.POPUP_LAYER);
setBoundsOnFrozenColumns();
added = true;
fixedColumns.setVisible(true);
}
還有一些繁雜的計算ixedColumns的位置大小大家可以下載代碼自己看了,大致原理就是如此簡單,就是利用JLayeredPane層的概念,用起來也很方便,只需要在原有的代碼傳入JTable,以及裝在這個JTable的JScrollPane,如
JTable table = new JTable(data, columnNames);
JScrollPane scrollPane = new JScrollPane(table);
FixTableManager tableHeader = new FixTableManager(table,scrollPane);
//固定前三列
tableHeader.setFixCol(2);
總之這樣能忽悠成看起來像是固定了,那它也有感覺不帶勁的地方,大家如有興趣,可以在下面的鏈接中下載代碼,運行其看看效果 ,效果是Scrollbar不會的最小值停留的位置不是在固定列的最後位置,隨之scrollbar的拖動,我們可以看到有的列會被固定的列擋住,正如前面所說,這個所謂的固定是個假象。還有一些缺點如有的皮膚可能算出來的結果會和原有的Table看起來不一致等。
本文配套源碼