学习定制表格树的项(一)
原文: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。
· SWT.MeasureItem: allows a client to specify the dimensions of a cell's content【长宽】
· SWT.EraseItem: allows a client to custom draw a cell's background and/or selection, and to influence whether the cell's foreground should be drawn【背景、选择时的处理、是否画默认的前景】
· SWT.PaintItem: allows a client to custom draw or augment a cell's foreground and/or focus rectangle【自己画或者增加前景】
1, SWT.MeasureItem
1,SWT.MeasureItem是第一调用的用户定制事件。
2,SWT.MeasureItem中控制的content size而不是cell size,因为cell size包括了额外的装饰如checkboxes or tree indentation
SWT.MeasureItem事件预定的变量:
· item: the item
· index: the column index of item to be measured
· width: the default content width that the table would use if it were to draw the cell itself, based on its text, image, and checkbox
· height: the default content height that the table would use, based on the height of all of its items
· gc: a GC that is configured with the cell's font, which can be useful for performing string measurements
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是被调用的第二个事件,这个事件是在画背景前调用。背景包括背景和选择背景,这个事件允许用户画背景和选择背景。
该事件的预定义的变量:
· item: the item【事件的源】
· index: the column index of item being drawn
· x, y: the table-relative co-ordinates of the top-left corner of the cell
· width, height: the width and height of the full cell, or the content width of the cell if the table has no columns; if these values were previously set by an SWT.MeasureItem listener then they are the same here
· gc: the GC configured with the default foreground, background, font and clipping for the cell
detail: the logical OR of one or more of the following bits, indicating what will be drawn by default:
· SWT.FOREGROUND: this bit will be set if the default cell foreground will be drawn once the background has been drawn (default is true)【我将这里理解为要不要完全由自己来画】
· SWT.BACKGROUND: this bit will be set if either a cell-specific or item-specific background color will be drawn for this cell (ie.- a color has previously been set with either TableItem.setBackground(Color) or TableItem.setBackground(int, Color))
· SWT.SELECTED: this bit will be set if the selection will be drawn for this cell, implying that this item is selected
· SWT.FOCUSED: this bit will be set if the focus rectangle will be drawn for this cell, implying that this item is the focus item in the table
· SWT.HOT: (@since 3.3) this bit will be set if the mouse hover background will be drawn for this cell (not all platforms draw this)
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
这个事件在默认的前景已经画好后,再调用。
这个事件的预定义的变量有:
· item: the item
· index: the column index of item being drawn
· x, y: the table-relative co-ordinates of the top-left corner of the cell's content (ie.- its image or text)
· width, height: the width and height of the cell's content; if these values were previously set by an SWT.MeasureItem listener then they are the same here
· gc: the GC configured with the default foreground, background, font and clipping for the cell
· detail: the logical OR of zero or more of the following bits:
o SWT.SELECTED: this bit will be set if the item is selected
o SWT.FOCUSED: this bit will be set if the item is the table's focus item
o SWT.HOT: (@since 3.3) this bit will be set if the mouse hover background will be drawn for this cell (not all platforms draw this)
例子:
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