学习定制表格树的项(一)
原文:http://www.eclipse.org/articles/article.php?file=Article-CustomDrawingTableAndTreeItems/index.html
本来将原来那篇拷贝过来,想写一些注释的,但太大了,太慢了,还是另写吧:
??总述
1,为什么要定制去画呢?
因为原来的Item有自己的局限性:?For?example,?an?item?in?a?table?or?tree?can?only?contain?one?image,?and?it?must?appear?before?its?text.?Item的一个单元只能支持一个图像,而且只能默认画在字符串的前面。
?
2,在定制Item的时候,是基于Cell来画制的,什么是Cell,如果一个Table有2列3行,那么它就有6个Cell。
?
3,定制Item是由三个事件完成的,SWT.MeasureItem,SWT.EraseItem和SWT.PaintItem。
?1,?SWT.MeasureItem
1,SWT.MeasureItem是第一调用的用户定制事件。
2,SWT.MeasureItem中控制的content?size而不是cell?size,因为cell?size包括了额外的装饰如checkboxes?or?tree?indentation
SWT.MeasureItem事件预定的变量:
?
?3,例子
??
public?class?Snippet271?{
public?static?void?main(String[]?args)?{
Display?display?=?new?Display();
Shell?shell?=?new?Shell(display);
shell.setBounds(10,10,150,150);
shell.setLayout(new?FillLayout());
?
Table?table?=?new?Table(shell,?SWT.NONE);
table.setLinesVisible(true);
for?(int?i?=?0;?i<3;?i++)?{
new?TableItem(table,?SWT.NONE).setText("item?"?+?i);
}
table.addListener(SWT.MeasureItem,?new?Listener()?{
public?void?handleEvent(Event?event)?{
event.height?=?event.gc.getFontMetrics().getHeight()?*?2;
event.width?*=??2;
}
});
shell.open();
while?(!shell.isDisposed())?{
if?(!display.readAndDispatch())?display.sleep();
}
display.dispose();
}
}
?
?
?
?
?程序中设置了Item的高度为gc字体高度的两倍,宽度为本来宽度的两倍。
??

?
这个是不改变Item高和宽的情况。
4,利用SWT.MeasureItem?改变Item的宽和高的有如下的限制:
a,现在宽和高时不可以缩小到比默认的小的,只能放大,不能缩小。
b,所有的Item在一个表格或一个Tree中,只有一个大小的高,增大一个,增大所有。
c,对于已经设置了宽度的column,SWT.MeasureItem?不起作用,在column调用Pack的时候,起作用。
例子:
?
public?class?Snippet272?{
public?static?void?main(String[]?args)?{
Display?display?=?new?Display();?
Shell?shell?=?new?Shell(display);
shell.setBounds(10,10,400,200);
Table?table?=?new?Table(shell,?SWT.NONE);
table.setBounds(10,10,350,150);
table.setHeaderVisible(true);
table.setLinesVisible(true);
final?TableColumn?column0?=?new?TableColumn(table,?SWT.NONE);
column0.setWidth(100);?column0.setText("col1");
final?TableColumn?column1?=?new?TableColumn(table,?SWT.NONE);
column1.setWidth(100);?column1.setText("col2");
column0.addListener(SWT.Selection,?new?Listener()?{
public?void?handleEvent(Event?event)?{
column0.pack();
}
});
for?(int?i?=?0;?i?<?5;?i++)?{
TableItem?item?=?new?TableItem(table,?SWT.NONE);
item.setText(0,?"item?"?+?i?+?"?col?0");
item.setText(1,?"item?"?+?i?+?"?col?1");
}
table.addListener(SWT.MeasureItem,?new?Listener()?{
public?void?handleEvent(Event?event)?{
event.width?*=?2;
}
});
?
shell.open();
while?(!shell.isDisposed())?{
if?(!display.readAndDispatch())?display.sleep();
}
display.dispose();
}
}
?
?
?
?
??
col1只用在点击时调用column0.pack(),SWT.MeasureItem中定义的宽带加倍才起作用。
?
2,SWT.EraseItem?
SWT.EraseItem是被调用的第二个事件,这个事件是在画背景前调用。背景包括背景和选择背景,这个事件允许用户画背景和选择背景。
该事件的预定义的变量:
detail:?the?logical?OR?of?one?or?more?of?the?following?bits,?indicating?what?will?be?drawn?by?default:?
?
?
doit:?this?boolean?indicates?whether?the?table?will?do?the?drawing?specified?in?the?detail?field?once?this?listener?has?completed?(default?is?true)
?
?例子:
?
public?class?Example3?{
public?static?void?main(String[]?args)?{
Display?display?=?new?Display();?
Shell?shell?=?new?Shell(display);
final?Color?red?=?display.getSystemColor(SWT.COLOR_RED);
final?Color?yellow?=?display.getSystemColor(SWT.COLOR_YELLOW);
final?Table?table?=?new?Table(shell,?SWT.FULL_SELECTION);
table.setHeaderVisible(true);
new?TableColumn(table,?SWT.NONE).setWidth(100);
new?TableColumn(table,?SWT.NONE).setWidth(100);
new?TableColumn(table,?SWT.NONE).setWidth(100);
for?(int?i?=?0;?i?<?5;?i++)?{
TableItem?item?=?new?TableItem(table,?SWT.NONE);
item.setText(0,?"item?"?+?i?+?"?col?0");
item.setText(1,?"item?"?+?i?+?"?col?1");
item.setText(2,?"item?"?+?i?+?"?col?2");
}
table.addListener(SWT.EraseItem,?new?Listener()?{
public?void?handleEvent(Event?event)?{
event.detail?&=?~SWT.HOT;
if?((event.detail?&?SWT.SELECTED)?==?0)?return;?/*?item?not?selected?*/
int?clientWidth?=?table.getClientArea().width;
GC?gc?=?event.gc;
Color?oldForeground?=?gc.getForeground();
Color?oldBackground?=?gc.getBackground();
gc.setForeground(red);
gc.setBackground(yellow);
gc.fillGradientRectangle(event.x,?event.y,?event.width,?event.height,?false);
gc.setForeground(oldForeground);
gc.setBackground(oldBackground);
event.detail?&=?~SWT.SELECTED;
}
});
?
table.pack();
shell.pack();
shell.open();
while?(!shell.isDisposed())?{
if?(!display.readAndDispatch())?display.sleep();
}
display.dispose();
}
}
?
?
?
?实现了选择的在Item中,画上一个渐变的背景色。
这里要注意:
a,不能用event.detail==SWT.SELECTED的方法来判断有没有被选择上,这里detail可能好多位有效。
b,处理完最好要取消SWT.SELECTED事件,event.detail?&=?~SWT.SELECTED取消,要不然默认的处理SELECTED的事件还是要被调用。
c,这里的gc.fillGradientRectangle(0,?event.y,?clientWidth,?event.height,?false);坐标用的0和clientWidth,如果用gc.fillGradientRectangle(event.x,?event.y,?event.width,?event.height,?false);就成了下图的效果,想想就知道了,EraseItem事件是针对每一个cell,这样就是三个独立的cell画了三次,实际上上面也是画了三次,只不过都是重复了。

?
?
?
再一个画背景的例子:
?
public?class?Snippet273?{
public?static?void?main(String[]?args)?{
final?String[]?MONTHS?=?{
"Jan",?"Feb",?"Mar",?"Apr",?"May",?"Jun",
"Jul",?"Aug",?"Sep",?"Oct",?"Nov",?"Dec"
};
final?int[]?HIGHS?=?{-7,?-4,?1,?11,?18,?24,?26,?25,?20,?13,?5,?-4};
final?int[]?LOWS?=?{-15,?-13,?-7,?1,?7,?13,?15,?14,?10,?4,?-2,?-11};
final?int?SCALE_MIN?=?-30;?final?int?SCALE_MAX?=?30;
final?int?SCALE_RANGE?=?Math.abs(SCALE_MIN?-?SCALE_MAX);
?
Display?display?=?new?Display();
Shell?shell?=?new?Shell(display);
shell.setBounds(10,10,400,350);
shell.setText("Ottawa?Average?Daily?Temperature?Ranges");
final?Color?blue?=?display.getSystemColor(SWT.COLOR_BLUE);
final?Color?white?=?display.getSystemColor(SWT.COLOR_WHITE);
final?Color?red?=?display.getSystemColor(SWT.COLOR_RED);
//?final?Image?parliamentImage?=?new?Image(display,?"./parliament.jpg");
final?Table?table?=?new?Table(shell,?SWT.NONE);
table.setHeaderVisible(true);
table.setLinesVisible(true);
table.setBounds(10,10,350,300);
//?table.setBackgroundImage(parliamentImage);
for?(int?i?=?0;?i?<?12;?i++)?{
TableItem?item?=?new?TableItem(table,?SWT.NONE);
item.setText(MONTHS[i]?+?"?("?+?LOWS[i]?+?"C..."?+?HIGHS[i]?+?"C)");
}
final?int?clientWidth?=?table.getClientArea().width;
?
table.addListener(SWT.MeasureItem,?new?Listener()?{
public?void?handleEvent(Event?event)?{
int?itemIndex?=?table.indexOf((TableItem)event.item);
int?rightX?=?(HIGHS[itemIndex]?-?SCALE_MIN)?*?clientWidth/SCALE_RANGE;
event.width?=?rightX;
}
});
table.addListener(SWT.EraseItem,?new?Listener()?{
public?void?handleEvent(Event?event)?{
int?itemIndex?=?table.indexOf((TableItem)event.item);
int?leftX?=?(LOWS[itemIndex]?-?SCALE_MIN)?*?clientWidth/SCALE_RANGE;
int?rightX?=?(HIGHS[itemIndex]?-?SCALE_MIN)?*?clientWidth/SCALE_RANGE;
GC?gc?=?event.gc;
Rectangle?clipping?=?gc.getClipping();
clipping.x?=?leftX;
clipping.width?=?rightX?-?leftX;
gc.setClipping(clipping);
?
Color?oldForeground?=?gc.getForeground();
Color?oldBackground?=?gc.getBackground();
gc.setForeground(blue);
gc.setBackground(white);
gc.fillGradientRectangle(event.x,?event.y,?event.width/2,?event.height,?false);
gc.setForeground(white);
gc.setBackground(red);
gc.fillGradientRectangle(
event.x?+?event.width/2,?event.y,?event.width/2,?event.height,?false);
gc.setForeground(oldForeground);
gc.setBackground(oldBackground);
event.detail?&=?~SWT.BACKGROUND;
event.detail?&=?~SWT.HOT;
}
});
shell.open();
while?(!shell.isDisposed())?{
if?(!display.readAndDispatch())?display.sleep();
}
//?parliamentImage.dispose();
display.dispose();
}
}
?

?
这个程序画了渥太华的12个月的温度情况,画的非常的逼真啊。
?画法就是每一列的长度为【(HIGHS[itemIndex]?-?SCALE_MIN)?*?clientWidth/SCALE_RANGE;】,画的时候把gc中设置一个clipping,这样就只有这个回形针区域可以画上去了。然后两段都是渐进第一段蓝色到白色渐进,第二段是白色到红色渐进,蓝色表示冷,红色表示热了。还是比较形象的。
?
?3,SWT.PaintItem?
这个事件在默认的前景已经画好后,再调用。
这个事件的预定义的变量有:
例子:
?
public?class?Snippet220?{
public?static?void?main(String?[]?args)?{
Display?display?=?new?Display();
Shell?shell?=?new?Shell(display);
shell.setBounds(10,?10,?350,?200);
Image?xImage?=?new?Image?(display,?16,?16);
GC?gc?=?new?GC(xImage);
gc.setForeground(display.getSystemColor(SWT.COLOR_RED));
gc.drawLine(1,?1,?14,?14);
gc.drawLine(1,?14,?14,?1);
gc.drawOval(2,?2,?11,?11);
gc.dispose();
final?int?IMAGE_MARGIN?=?2;
final?Tree?tree?=?new?Tree(shell,?SWT.CHECK);
tree.setBounds(10,?10,?300,?150);
TreeItem?item?=?new?TreeItem(tree,?SWT.NONE);
item.setImage(xImage);
item.setText("root?item");
for?(int?i?=?0;?i?<?4;?i++)?{
TreeItem?newItem?=?new?TreeItem(item,?SWT.NONE);
newItem.setText("descendent?"?+?i);
if?(i?%?2?==?0)?{
newItem.setData(xImage);
}
newItem.setImage(xImage);
item.setExpanded(true);
item?=?newItem;
}
?
tree.addListener(SWT.MeasureItem,?new?Listener()?{
public?void?handleEvent(Event?event)?{
TreeItem?item?=?(TreeItem)event.item;
Image?trailingImage?=?(Image)item.getData();
if?(trailingImage?!=?null)?{
event.width?+=?trailingImage.getBounds().width?+?IMAGE_MARGIN;
}
}
});
tree.addListener(SWT.PaintItem,?new?Listener()?{
public?void?handleEvent(Event?event)?{
TreeItem?item?=?(TreeItem)event.item;
Image?trailingImage?=?(Image)item.getData();
if?(trailingImage?!=?null)?{
int?x?=?event.x?+?event.width?+?IMAGE_MARGIN;
int?itemHeight?=?tree.getItemHeight();
int?imageHeight?=?trailingImage.getBounds().height;
int?y?=?event.y?+?(itemHeight?-?imageHeight)?/?2;
event.gc.drawImage(trailingImage,?x,?y);
}
}
});
?
shell.open();
while?(!shell.isDisposed())?{
if?(!display.readAndDispatch())?display.sleep();
}
xImage.dispose();
display.dispose();
}
}
?
?
?
?
在Item的后面,画图像。
可不可完全由我们来画,不好默认的呢?
下面例子:
?
public?class?Snippet231?{
public?static?void?main(String?[]?args)?{
final?int?COLUMN_COUNT?=?4;
final?int?ITEM_COUNT?=?8;
final?int?TEXT_MARGIN?=?3;
Display?display?=?new?Display();
Shell?shell?=?new?Shell(display);
final?Table?table?=?new?Table(shell,?SWT.FULL_SELECTION);
table.setHeaderVisible(true);
table.setLinesVisible(true);
for?(int?i?=?0;?i?<?COLUMN_COUNT;?i++)?{
new?TableColumn(table,?SWT.NONE);
}
for?(int?i?=?0;?i?<?ITEM_COUNT;?i++)?{
TableItem?item?=?new?TableItem(table,?SWT.NONE);
for?(int?j?=?0;?j?<?COLUMN_COUNT;?j++)?{
String?string?=?"item?"?+?i?+?"?col?"?+?j;
if?((i?+?j)?%?3?==?1)?{
string?+="\nnew?line1";
}
if?((i?+?j)?%?3?==?2)?{
string?+="\nnew?line1\nnew?line2";
}
item.setText(j,?string);
}
}
?
table.addListener(SWT.MeasureItem,?new?Listener()?{
public?void?handleEvent(Event?event)?{
TableItem?item?=?(TableItem)event.item;
String?text?=?item.getText(event.index);
Point?size?=?event.gc.textExtent(text);
event.width?=?size.x?+?2?*?TEXT_MARGIN;
event.height?=?Math.max(event.height,?size.y?+?TEXT_MARGIN);
}
});
table.addListener(SWT.EraseItem,?new?Listener()?{
public?void?handleEvent(Event?event)?{
event.detail?&=?~SWT.FOREGROUND;
}
});
table.addListener(SWT.PaintItem,?new?Listener()?{
public?void?handleEvent(Event?event)?{
TableItem?item?=?(TableItem)event.item;
String?text?=?item.getText(event.index);
/*?center?column?1?vertically?*/
int?yOffset?=?0;
if?(event.index?==?1)?{
Point?size?=?event.gc.textExtent(text);
yOffset?=?Math.max(0,?(event.height?-?size.y)?/?2);
}
event.gc.drawText(text,?event.x?+?TEXT_MARGIN,?event.y?+?yOffset,?true);
}
});
?
for?(int?i?=?0;?i?<?COLUMN_COUNT;?i++)?{
table.getColumn(i).pack();
}
table.pack();
shell.pack();
shell.open();
while?(!shell.isDisposed())?{
if?(!display.readAndDispatch())?display.sleep();
}
display.dispose();
}
}
?
?

?
??
?表格中width取字符串宽加空白边框,height是字符串高加空白边框。这里的高和宽都是有GC计算的。
SWT.EraseItem事件取消的SWT.FOREGROUND事件,完全有我们自己去画了,画的时候用event.gc.drawText就行了。注意最后的column.pack()是必须的,前面没有设置column的宽度,调用pack才会计算column的宽度。
?
?文章看完了,比较的长,看几遍,有些问题还没有拓展。在网页上编辑,代码总是乱,没有办法只有在word编辑了再考上去了。
原文链接:http://www.eclipse.org/articles/article.php?file=Article-CustomDrawingTableAndTreeItems/index.html
?