在Google上搜索實現(xiàn)這種效果的方法,只搜到一個網(wǎng)頁,是國外的,一看,還得要給錢注冊!
俗話說,自力更生,艱苦奮斗,就自己想了法子,拿來分享。衷心請各位大蝦指點指點不足

我寫了個程序,里面用到一個JTable,本著MVC的精神,而且考慮到單元格可能不是簡簡單單一個Label能表現(xiàn)的,就寫了個表格渲染器的類,叫NoteLabelRenderer

代碼如下:

//NoteLabelRenderer.java

package com.component;

import javax.swing.*;
import javax.swing.table.*;

import java.awt.*;

import com.data.Note;

/**
?* To render the cells showing the note
?* @author Allen Chue
?*
?*/
public class NoteLabelRenderer extends JPanel implements TableCellRenderer {

?public static ImageIcon ATTACH_ICON=new ImageIcon("res/attach.gif");
?public static ImageIcon BLANK_ICON=new ImageIcon("res/blank.gif");
?
?private JLabel icon=new JLabel();
?private JLabel content=new JLabel();
?private JLabel attach=new JLabel();
?
?private String contentText,title;
?
?public NoteLabelRenderer() {
??super();
??setLayout(new BorderLayout());
??
??icon.setOpaque(true);
??content.setOpaque(true);
??attach.setOpaque(true);
??
??icon.setHorizontalAlignment(SwingConstants.CENTER);
??icon.setVerticalAlignment(SwingConstants.CENTER);
??content.setVerticalAlignment(SwingConstants.NORTH);
??attach.setHorizontalAlignment(SwingConstants.CENTER);
??attach.setVerticalAlignment(SwingConstants.CENTER);
??
??add(icon,BorderLayout.WEST);
??add(content,BorderLayout.CENTER);
??add(attach,BorderLayout.EAST);
??setSize(getPreferredSize());
?}
?
?/**
???? *? Returns the component used for drawing the cell.? This method is
???? *? used to configure the renderer appropriately before drawing.
???? *
???? * @param?table??the <code>JTable</code> that is asking the
???? *????renderer to draw; can be <code>null</code>
???? * @param?value??the value of the cell to be rendered.? It is
???? *????up to the specific renderer to interpret
???? *????and draw the value.? For example, if
???? *????<code>value</code>
???? *????is the string "true", it could be rendered as a
???? *????string or it could be rendered as a check
???? *????box that is checked.? <code>null</code> is a
???? *????valid value
???? * @param?isSelected?true if the cell is to be rendered with the
???? *????selection highlighted; otherwise false
???? * @param?hasFocus?if true, render cell appropriately.? For
???? *????example, put a special border on the cell, if
???? *????the cell can be edited, render in the color used
???? *????to indicate editing
???? * @param?row???????? the row index of the cell being drawn.? When
???? *????drawing the header, the value of
???? *????<code>row</code> is -1
???? * @param?column???????? the column index of the cell being drawn
???? */
?public Component getTableCellRendererComponent(JTable table, Object value,
???boolean isSelected, boolean hasFocus, int row, int column) {
??changeBackground(isSelected);
??setNoteText((Note)value,isSelected);
??return this;
?}
?
?private void changeBackground(boolean isSelected) {
??Color selectedColor=new Color(160,231,160);
??if (isSelected) {
???icon.setBackground(selectedColor);
???content.setBackground(selectedColor);
???attach.setBackground(selectedColor);
??}
??else {
???icon.setBackground(null);
???content.setBackground(null);
???attach.setBackground(null);
??}
?}
?
?/**
? * Show the note according to a note
? * @param n The <code>Note</code>
? * @param flag A flag variable. True for emphasizing the the
? * contents, while false for showing it normally
? */
?private void setNoteText(Note n, boolean flag) {
??String color=flag?"#ffffff":"#000000";
??/*
?? ************************
?? *****Set Icon Area******
?? ************************
?? */
??icon.setIcon(n.getIcon());
??/*
?? ************************
?? ****Set Content Area****
?? ************************
?? */
??title=n.getTitle();
??contentText=n.getContent();
??if (contentText.length()>120) {
???contentText=contentText.substring(0,115)+"...";
??}
??content.setText("<html><font" +
?????" color=#116677>"+title+"</font><br>" +
?????"<font color="+color+">"+contentText+"</font></html>");
??/*
?? ***********************
?? ***Set Attach Area*****
?? ***********************
?? */
??if (n.getAttach() != null) {
???attach.setIcon(ATTACH_ICON);
??}
??else {
???attach.setIcon(BLANK_ICON);
??}
?}
?public String getContent() {
??return this.contentText;
?}
?public String getTitle() {
??return this.title;
?}
}

這是個備忘的程序,一些有Note字眼的不用關心。

這個NoteLableRenderer繼承了JPanel,是為了更好地對其中一些組件進行布局的控制(在這里,每個單元格里有三個JLabel),TableCellRender接口是必須要實現(xiàn)的
可以看到,實現(xiàn)鼠標點擊單元格改變文字顏色和單元格(也就是一個JPanel)背景色并不難,只需要在getTableCellRendererComponent方法中判斷傳入的參數(shù)isSelected是否為true,然后對組件屬性重新設置,返回修改過的Component即可。JTable在鼠標點擊單元格時會重畫表格,于是改變后的單元格即可顯示出來。

我還想要實現(xiàn)單元格的懸停效果,也就是說,當鼠標移動至某個單元格上方時,該單元格可以變化顏色或者加個邊框來突出顯示,就想JButton可以設置RollOverIcon一樣。我想當然地在這個渲染器里添加了鼠標監(jiān)聽,然而測試確發(fā)現(xiàn)不行...
?? 經(jīng)過一番查閱、思考,我終于將解決的方法從單元格渲染器上轉至JTable本身來,因為我發(fā)現(xiàn)JTable里竟然有個rowAtPoint(Point point)方法(高手可不要笑我啊)!于是,我就在表格上監(jiān)聽鼠標動作事件,在mouseMoved(MouseEvent e)方法中用int row=this.rowAt(e.getPoint())得到鼠標移動時當前鼠標在哪一行。這樣的話,至少是邁了一大步,接下來的問題就是如何對鼠標下面的單元格進行屬性的修改。

經(jīng)過查看JTable的源碼,發(fā)現(xiàn)里面有個public Component prepareRenderer方法調(diào)用了單元格渲染器里的getTableCellRenderer方法,用于在繪制表格時準備好單元格內(nèi)放置的組件。既然它是public的,我就重載了這個方法,并使用了個自己感覺有點笨的方法,表格中加了個Vector,存儲每個單元格是否在鼠標下面的信息(用Boolean),也就是說,每一時刻這個Vector中只有一個為true(同一時刻,只能有一個單元格在鼠標下面),其余為false,同時保持Vector的size與單元格行數(shù)相同。
重載的prepareRenderer方法體如下:

/**
? * This method overides the super one, in order to
? * add rollover effects to the <code>JTable</code>
? * A <code>Vector</code> stores information about
? * whether the cell is beneath the mouse. If the value
? * corresponding to the cell is true, this method will
? * return a <code>Component</code> with the border modified;
? * while false, the <code>Renderer</code> will be returned
? * unchanged after calling the super method
? */
?public Component prepareRenderer(TableCellRenderer renderer, int row,
???int column) {
??Component comp = super.prepareRenderer(renderer, row, column);
??if (((Boolean)flagMouseOver.get(row)).booleanValue()) {
???((JComponent)comp).setBorder(BorderFactory.createLineBorder(Color.GRAY,1));
???return comp;
??}
??else {
???((JComponent)comp).setBorder(null);
???return comp;
??}
?}

flagMouseOver即為保存單元格狀態(tài)的Vector。而mouseMoved方法如下

public void mouseMoved(MouseEvent e) {//Add rollover effects
??int row=this.rowAtPoint(e.getPoint());
??if (mouseAtRow == -1) {
???//Init variable mouseAtRow
???mouseAtRow=row;
???flagMouseOver.set(mouseAtRow,new Boolean(true));
???//System.out.println("Mouse first moves to row "+(row+1));
???
???repaint();
?? revalidate();

??}
??else if (row != mouseAtRow) {//The mouse has moved to a new row
???//Clear the border
???flagMouseOver.set(mouseAtRow,new Boolean(false));
???
???//Set new row's border
???flagMouseOver.set(row,new Boolean(true));
???
???//System.out.println("Mouse moves from row "+(mouseAtRow+1)+" to row "+(row+1));
???repaint();
?? revalidate();
???mouseAtRow=row;
???
??}??
?}

mouseAtRow為一個int類型的變量,存儲鼠標移動前在哪一行。當鼠標移動時,獲得當前鼠標在哪一行(表格僅有一列),然后與mouseAtRow比較,若變化了,說明鼠標到了新的一行,同時更新flagMouseOver內(nèi)的數(shù)據(jù),緊接著用repaint和revalidate方法更新表格,實現(xiàn)了表格GUI的變化。這里懸停效果是添加邊框,當然也可以改背景等等。
另外,JLabel如果要setBackground需要先將其setOpaque(true);

這是我的經(jīng)歷,希望有人提出意見,我非常歡迎!

???????????????????????????? Allen Chue

http://blog.csdn.net/allenchue/archive/2005/04/02/335099.aspx