Topic: 怎么实现一个table的Cell显示多行信息?

  Print this page

1.怎么实现一个table的Cell显示多行信息? Copy to clipboard
Posted by: ghuang
Posted on: 2004-09-18 13:01

--------------------------------------------------------
a1 | a2 | a3 | <---Table 第一行
------------------------------------------------------
b1 | b21| b3 | <-- Table第二行
| b22|
---------------------------------------------------
如上面
就是需要table的某些Cell要显示多行信息。
用JFace中的TableViewer很难实现。
看来只可能用JTable了。
我现在用实现了TableCellRender(extend JTextArea)可以把信息显示再多
行上(再JLable中象"aaa\nbbb"不能换行,所以只有用JTextArea了)
但是行的高度不能变为多行,只有一行。大家知道怎么改变行的高度吗?
象上面的例子的第二行,变成了b1 b21 b3

2.Re:怎么实现一个table的Cell显示多行信息? [Re: ghuang] Copy to clipboard
Posted by: floater
Posted on: 2004-09-21 09:28

Here is a piece of code to change the height. However, once you can change the height, there is another problem - you have to check all the columns when you want to shrink the height. Check the main() on how to use it.

/**
* Multiline table cell class.
*
* This class is ugly, need to reinvestigae a little more.
*/
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.border.*;
import java.awt.*;

public class MultilineTableCellRenderer extends JTextArea implements TableCellRenderer
{
// We don't use this, check jdk doc on this.
//char LINE_BREAK = System.getProperty("line.separator").charAt(0);
private static final char LINE_BREAK = '\n';

private boolean isSelected = false;
Color sfore = null;
Color sback = null;
Color fore = null;
Color back = null;

// be warned, turn on debug will cause the program run very slowly.
private static final boolean debug = false;

public MultilineTableCellRenderer()
{
super("", 1, 1);
setLineWrap(true);
setWrapStyleWord(true);
}

public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column)
{
this.isSelected = isSelected;
sfore = table.getSelectionForeground();
sback = table.getSelectionBackground();
fore = table.getForeground();
back = table.getBackground();
//debug("select fore=" + table.getSelectionForeground());
//debug("select back=" + table.getSelectionBackground());
//debug("fore=" + table.getForeground());
//debug("back=" + table.getBackground());

if (this.isSelected)
{
setForeground(sfore);
setBackground(sback);
}
else
{
setForeground(fore);
setBackground(back);
}

setFont(table.getFont());

if (hasFocus)
{
setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
if (table.isCellEditable(row, column))
{
setForeground(UIManager.getColor("Table.focusCellForeground"));
setBackground(UIManager.getColor("Table.focusCellBackground"));
}
}
else
{
setBorder(new EmptyBorder(1, 2, 1, 2));
}

int columnWidth = table.getColumnModel().getColumn(column).getWidth();

if (columnWidth == 0) return this;

if (value == null)
{
this.setText("");
return this;
}

this.setColumns(columnWidth);
this.setText(value.toString());

//debug("columns=" + this.getColumns() + " rows=" + this.getRows() + " linecount=" + this.getLineCount());
//debug("size=" + this.getSize());
//debug("prefer size=" + this.getPreferredSize());
final FontMetrics fm = this.getFontMetrics(this.getFont());
LineNumberString lns = getWrappedText(value.toString().trim(), columnWidth, fm);
int rowNum = lns.getNumber();
//debug("formatted string=[ " + lns + " ]");

int stringHeight = fm.getHeight();
int rowHeight = stringHeight * rowNum;
//debug("table row height=" + table.getRowHeight(row) + " height needed=" + rowHeight);
if (table.getRowHeight(row) < rowHeight) //just stretch it, if needed.
{
table.setRowHeight(row, rowHeight);
}
//before we compress the width, consult with all other columns first.
else if (table.getRowHeight(row) > rowHeight)
{
boolean ok = true;
for (int i = 0, j = table.getColumnCount(); i < j; i++)
{
if (i != column)
{

//if (isCellVisible(table, row, i)) // this is arguable, this is causing 100% cpu
{
Object o = table.getValueAt(row, i);
if (o!=null)
{
String aa = o.toString().trim();
//int cWidth = table.getColumn(table.getColumnName(i)).getWidth();

int cWidth = table.getColumnModel().getColumn(i).getWidth();
int rHeight = getWrappedText(aa, cWidth, fm).getNumber();
if (rowNum<rHeight)
{
ok = false;
break;
}
}
}
}
//debug("checking column==" + i);
}
if (ok)
table.setRowHeight(row, rowHeight);
}
// don't need to do anything for the equal case.
this.setSize(columnWidth, rowHeight);
this.setText(value.toString());
//debug("cell row=" + row + " coloum=" + column + " value=" + value.toString());

return this;
}

/**
* return the number of rows for the String txt with the width. There are
* many ways to break a string into lines, line breaks, word breaks, etc.
* This method will use word breaks, i.e., break a line only after words,
* not in the middle, a word is a-z, A-Z, 0-9, ', ".
* If there is a linebreak, it keeps in, naturally.
* If there is a space, replace space with a linebreak.
* If there is one of , . ! ) } ] > : / | + - _ = then break after these.
* If there is one of @ # $ % ^ & * ( { [ < \ then break before these.
* else, break at the end of line(brutal force for a long string).
* @param txt input text string
* @param wrap out wrapped text string
* @param width the width of the output text
* @param lineBreak the line break, \n, \r\n, or <br>
* @return the number of rows in the returned text
*/
// unfinished! get all the special chars in!
private static final LineNumberString getWrappedText(String txt, int width, FontMetrics fm)
{
//debug("txt=" + txt + " width=" + width + " txtlen=" + txt.length());
if (txt == null || txt.trim().equals("")) return new LineNumberString(txt, 1);

//debug("txt pixels=" + fm.stringWidth(txt));
if (fm.stringWidth(txt) < width) return new LineNumberString(txt, 1);

//final int MARGIN = 5;
// should minus the linkbreak
int width1 = width - 10;
StringBuffer sb = new StringBuffer(txt);
final int txtLength = txt.length();
int start = 0; //new line start position
int stop = 1; //marker for the search
int hold = 0; // value holder when search backward for breaking point
int offset = 0; // counts for inserting new line break.
int lineNum = 1; // line number
// substring(i, j) is indexed from i to j-1.
//debug("sub pixels=" + fm.stringWidth(txt.substring(start, txtLength)));
while (start < txtLength && stop < txtLength)
{
if (fm.stringWidth(txt.substring(start, txtLength)) <= width1) // the <width tail, don't need extra, just count the linebreaks
{
// check for line breaks
//debug("check line breaks for short-than-a-line strings, the last segment, tail");
String tail = txt.substring(start, txtLength);
for (int i=0, j=tail.length(); i<j; i++)
{
if (tail.charAt(i) == LINE_BREAK) lineNum++;
}
break; // since we are done, now get out.
}
else
{
// keep in mind String are indexed from 0 to length()-1
while (fm.stringWidth(txt.substring(start, stop)) < width1)
{
//debug("stop=" + stop + " sub=" + txt.substring(start, stop) + " sub pixels=" + fm.stringWidth(txt.substring(start, stop)));
if (txt.charAt(stop) == LINE_BREAK)
{
start = ++stop;
stop++; //skip the linebreak
lineNum++;
//debug("===============================restart stop counter: new start=" + start + " stop=" + stop);
}
else stop++;
if (stop == txtLength) break; // even we have the check above, this still could happen.
}
if (stop == txtLength) break; // we need to break and go to loop again to do a full check.

hold = stop; // the real length of this row
//debug("hold=" + hold);
// now we need to find a break point, substring(i, j) is indexed from i to j-1.
while ((stop != start) && txt.charAt(stop - 1) != ' ')
{
stop--; //decremented, and then get out, be careful here. we will increment again at the end.
}
if (start == stop) stop = hold; // found no break point
//debug("old line=[" + txt.substring(start, stop) + "] " + " stop=" + stop);
if (sb.charAt(stop - 1 + offset) == ' ')
{
sb.setCharAt(stop - 1 + offset, LINE_BREAK);
//debug("replace space with linebreak");
}
else // when other chars, when the whole word is larger than the width, we have to break at the end.
{
sb.insert(stop - 1 + offset, LINE_BREAK);
//debug("add a line break.");
offset++; // count for added new line breaks, this causes a shift.
}
lineNum++;
//debug("new line=[" + txt.substring(start, stop) + "] " + " stop=" + stop);
start = ++stop; // here we increment again.
stop++; // move one more to get the first char.
//debug("================================== linenumber=" + lineNum +
//" offset=" + offset + " new start=" + start + " stop=" + stop
//+ "column with=" + width1);
}
}

return new LineNumberString(sb.toString(), lineNum);
}

// The following methods override the defaults for performance reasons
// check DefaultTableCellRenderer implementation
//public void validate() {}
//public void revalidate() {}
public void repaint(long tm, int x, int y, int width, int height) {}
public void repaint(Rectangle r) {}
/*
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue)
{
// Strings get interned...
if (propertyName=="text")
{
super.firePropertyChange(propertyName, oldValue, newValue);
}
}
*/
// If we disable this one, the text won't show up.
//public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {}

protected void setValue(Object value)
{
setText((value == null) ? "" : value.toString());
}

//protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {}
//public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {}

private final static void debug(String s)
{
if (debug == true && s != null && !s.trim().equals(""))
System.out.println("MultilineTableCellRenderer: -- " + s);
}

public static void main(String[] args)
{
long b = System.currentTimeMillis();
//testing data
final String[][] rows = {{"one", "Terminator I werw3r w3rw3r", "USA", "Terminator I", "USA"},
{"two", "Terminator II wrw3rw3r w3r", "USA", "Terminator I", "USA"},
{"three", "Terminator III wrw 3rw", "USA", "Terminator I", "USA"},
{"one", "Terminator I sdfsd sdf", "USA", "Terminator I", "USA"},
{"one", "Terminator Is sdfsd sd", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I", "USA", "Terminator I", "USA"},
{"one", "Terminator I sdfsd sdf", "USA", "Terminator I", "USA"},
{"one", "Terminator I sdfsd sdf", "USA", "Terminator I", "USA"},
{"one", "Terminator I sdfsd sdf", "USA", "Terminator I", "USA"},
{"one", "Terminator I sdfsd sdf", "USA", "Terminator I", "USA"},
{"one", "Terminator I sdfsd sdf", "USA", "Terminator I", "USA"},
{"one", "Terminator I sdfsd sdf", "USA", "Terminator I", "USA"},
{"one", "Terminator I sdfsd sdf", "USA", "Terminator I", "USA"},
{"one", "Terminator I sdfsd sdf", "USA", "Terminator I", "USA"},
{"one", "Terminator I sdfsd sdf", "USA", "Terminator I", "USA"},
{"one", "Terminator I sdfsd sdf", "USA", "Terminator I", "USA"},
{"one", "Terminator I sdfsd sdf", "USA", "Terminator I", "USA"},
{"two", new String("Terminator II"), "USA", "Terminator I", "USA"},
{"three", new String("Terminator III aksdjf asd faksld faksd fksdf"), "USA", "Terminator I", "USA"}

};
String[] headers = {"Number", "Video name", "Country", "Video name", "Country"};

//testing frame
JFrame frame = new JFrame();
frame.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//testing table
JTable table = new JTable(new ProperClassTableModel(rows, headers));
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.doLayout();
table.setDefaultRenderer(String.class, new MultilineTableCellRenderer());
System.out.println("table 1=" + table.getDefaultRenderer(String.class));
JScrollPane scrollPane = new JScrollPane(table);

frame.getContentPane().add(scrollPane);
frame.setTitle("Table cell rendering testing");
frame.pack();
long e = System.currentTimeMillis();
frame.setVisible(true);
System.out.println("total time=" + (e-b));
}

// This will cause 100% CPU when using in this class.
private static boolean isCellVisible(JTable table, int rowIndex, int vColIndex)
{
if (!(table.getParent() instanceof JViewport))
{
return false;
}

JViewport viewport = (JViewport)table.getParent();
// This rectangle is relative to the table where the
// northwest corner of cell (0,0) is always (0,0)
Rectangle rect = table.getCellRect(rowIndex, vColIndex, true);
// The location of the viewport relative to the table
Point pt = viewport.getViewPosition();
// Translate the cell location so that it is relative
// to the view, assuming the northwest corner of the
// view is (0,0)
rect.setLocation(rect.x-pt.x, rect.y-pt.y);
// Check if view completely contains cell
return new Rectangle(viewport.getExtentSize()).contains(rect);
}

}

// a holder class for the output.
class LineNumberString
{
private String s = null;
private int i = 0;

public LineNumberString(String s, int i)
{
this.s = s;
this.i = i;
}

public int getNumber() { return i; }
public String getString() { return s; }

public String toString() { return "string = " + s + " line number = " + i; }
}


3.Re:怎么实现一个table的Cell显示多行信息? [Re: ghuang] Copy to clipboard
Posted by: ghuang
Posted on: 2004-09-22 00:16

thank you
后来改用了SWT的TableTree.


   Powered by Jute Powerful Forum® Version Jute 1.5.6 Ent
Copyright © 2002-2021 Cjsdn Team. All Righits Reserved. 闽ICP备05005120号-1
客服电话 18559299278    客服信箱 714923@qq.com    客服QQ 714923