booleanselected=false;
if(i==rowSel)
selected=true;
onPaint(gc,i,cx,beginy+(i-beginLine)*lineHeight,
selected);
}
}
});
这里要注意的是从PaintEvent中获取的x,y,height,width是需要重绘的区域,x,y是以控件的左上角为原点的坐标。
在我们的程序中,为了性能起见,我们先根据需要重绘的区域计算出需要重绘的行数,只重绘相应的行,而不是将整个控件重绘。
我们程序中用到的onPaint用于绘制一行。
接下来,我们要让我们的控件响应键盘上下键对列表项进行选择。
我们已对向上键的处理为例,首先当用户按了向上键时,我们需要改变选择,并且重绘旧的和新的选择项。
如果选择项已经到了列表的顶部,我们还需要同时滚动滚动条。
addListener(SWT.KeyDown,newListener(){
publicvoidhandleEvent(Eventevent){
switch(event.keyCode){
caseSWT.ARROW_UP:
//处理向上键
if(rowSel!
=0){
oldRowSel=rowSel;
rowSel--;
if(oldRowSel!
=rowSel){//发送消息让控件重绘
((Canvas)event.widget).redraw(cx,(rowSel+cy
/lineHeight)
*lineHeight,maxX,lineHeight*2,false);
}
if(rowSel<-cy/lineHeight){//如果需要,滚动滚动条
ScrollBarbar=((Canvas)event.widget)
.getVerticalBar();
bar.setSelection(bar.getSelection()-lineHeight);
scrollVertical(bar);
}
selectionChanged();//发送selectionChanged事件
}
break;
caseSWT.ARROW_DOWN:
//downarrorkey
…
break;
}
}
});
接下来,我们要让我们的控件响应鼠标对列表项进行选择。
首先我们要计算出鼠标选中的行号,注意MouseEvent中的y值只是相对于控件左上角的坐标,我们需要加上滚动出了控件的部分。
addMouseListener(newMouseListener(){
publicvoidmouseDoubleClick(MouseEvente){
}
publicvoidmouseDown(MouseEvente){
introw=(e.y-cy)/lineHeight;//计算选中的行
if(row>=0){
oldRowSel=rowSel;
rowSel=row;
}
if(oldRowSel!
=rowSel){//重画旧的和新的选择项
((Canvas)e.getSource()).redraw(cx,(e.y/lineHeight)
*lineHeight,maxX,lineHeight,false);
((Canvas)e.getSource()).redraw(cx,(oldRowSel+cy
/lineHeight)
*lineHeight,maxX,lineHeight,false);
}
selectionChanged();
}
publicvoidmouseUp(MouseEvente){
}
});
当我们的控件获得焦点时,选中的列表项需要有虚框表示控件得到焦点。
当获得或失去焦点是,我们这里只需要简单的通知选中的项重画。
addFocusListener(newFocusListener(){
publicvoidfocusGained(FocusEvente){
((Canvas)e.getSource()).redraw(cx,rowSel*lineHeight,maxX,
lineHeight,true);
}
publicvoidfocusLost(FocusEvente){
((Canvas)e.getSource()).redraw(cx,rowSel*lineHeight,maxX,
lineHeight,true);
}
});
我们在绘制每一个列表项时可以加入判断当前控件是否得到焦点,如果控件得到了焦点,我们就在选中的项目上画一个虚框。
下面是我们绘制一个列表项的代码,注意在代码的最后绘制焦点的虚框。
voidonPaint(GCgc,introw,intbeginx,intbeginy,booleanisSelected){
ColorinitColor=gc.getBackground();
ColorinitForeColor=gc.getForeground();
if(isSelected){
gc.setBackground(Display.getCurrent().getSystemColor(
SWT.COLOR_LIST_SELECTION));
gc.fillRectangle(beginx,beginy,maxX,lineHeight);
gc.setForeground(Display.getCurrent().getSystemColor(
SWT.COLOR_LIST_SELECTION_TEXT));
}else{
gc.setBackground(initColor);
}
gc.drawString((String)colorNames.get(row),beginx+24,beginy);
Colorcolor=Display.getCurrent().getSystemColor(
((Integer)colors.get(row)).intValue());
gc.setBackground(color);
gc.fillRectangle(beginx+2,beginy+2,20,lineHeight-4);
gc.setBackground(initColor);
gc.setForeground(initForeColor);
if(isFocusControl()&&isSelected)
gc.drawFocus(cx,beginy,maxX,lineHeight);
}
作为一个可操作的控件,TAB键的支持也是很重要的。
由于我们的控件是从Canvas继承过来的,不支持TAB键。
下面的代码使我们的控件有TAB键的支持:
addTraverseListener(newTraverseListener(){
publicvoidkeyTraversed(TraverseEvente){
if(e.detail==SWT.TRAVERSE_TAB_NEXT
||e.detail==SWT.TRAVERSE_TAB_PREVIOUS){
e.doit=true;
}
};
});
很多时候,我们需要有滚动条的支持。
对于滚动条,我们只要在上面加上selectionListener,处理它的widgetSelected事件就可以。
bar=getVerticalBar();
if(bar!
=null){
bar.addSelectionListener(newSelectionAdapter(){
publicvoidwidgetSelected(SelectionEventevent){
scrollVertical((ScrollBar)event.widget);
}
});
}
下面是函数scrollVertical的代码。
一旦用户对滚动条操作,我们就可以计算出要滚动的区域,然后调用scroll函数。
对函数scroll函数的调用会导致相应区域的重绘。
voidscrollVertical(ScrollBarscrollBar){
Rectanglebounds=getClientArea();
inty=-scrollBar.getSelection();
if(y+maxYy=bounds.height-maxY;
}
if(y%lineHeight!
=0)
y=y-y%lineHeight-lineHeight;
scroll(cx,y,cx,cy,maxX,maxY,false);
cy=y;
}
现在我们的程序已经基本成形了,我们来进一步完善它。
由于我们开发的控件是提供给程序员的,我们需要提供接口,让外部知道控件中发生的事件。
其中最重要的是列表项的选中事件。
我们需要提供接口让程序员能够添加事件监控器(listener)来监控发生的事件,并且一旦发生事件,我们需要通知监控器。
首先,我们添加一个成员来保存添加的事件监控器:
VectorselectionListeners=newVector();
我们再增加一个函数addSelectionListener,让程序员可以添加监控器
publicvoidaddSelectionListener(SelectionListenerlistener){
selectionListeners.addElement(listener);
}
在我们前面的代码中,我们注意到每次选择项改变,我们都会调用selectionChanged函数。
下面是selectionChanged函数代码。
这里,我们会生成一个SelectionEvent事件,并且逐个调用事件监控器的widgetSelected方法。
这样别人就可以监听到我们的事件了。
publicvoidselectionChanged(){
Eventevent=newEvent();
event.widget=this;
SelectionEvente=newSelectionEvent(event);
for(inti=0;iSelectionListenerlistener=(SelectionListener)selectionListeners.elementAt(i);
listener.widgetSelected(e);
}
}
现在辅助功能(Accessibility)也日益成为软件重要的部分,它是的残疾人也能够方便的使用我们的软件。
美国已经立法,不符合Accessibility规范的软件不能够在政府部门销售。
我们开发的控件也需要支持Accessibility.下面的代码使我们的控件有Accessibility支持。
其中最重要的是getRole和getValue函数。
我们的控件是从Canvas继承,我们在getRole函数中返回ACC.ROLE_LIST,这样我们的控件才能让屏幕阅读软件将我们的控件作为列表控件对待。
Accessibleaccessible=getAccessible();
accessible.addAccessibleControlListener(newAccessibleControlAdapter(){
publicvoidgetRole(AccessibleControlEvente){
introle=0;
intchildID=e.childID;
if(childID==ACC.CHILDID_SELF){
role=ACC.ROLE_LIST;
}elseif(childID>=0&&childIDrole=ACC.ROLE_LISTITEM;
}
e.detail=role;
}
publicvoidgetValue(AccessibleControlEvente){
intchildID=e.childID;
if(childID==ACC.CHILDID_SELF){
e.result=getText();
}elseif(childID>=0&&childIDe.result=(String)colorNames.get(childID);
}
}
publicvoidgetChildAtPoint(AccessibleControlEvente){
PointtestPoint=toControl(newPoint(e.x,e.y));
intchildID=ACC.CHILDID_NONE;
childID=(testPoint.y-cy)/lineHeight;
if(childID==ACC.CHILDID_NONE){
Rectanglelocation=getBounds();
location.height=location.height-getClientArea().height;
if(location.contains(testPoint)){
childID=ACC.CHILDID_SELF;
}
}
e.childID=childID;
}
publicvoidgetLocation(AccessibleControlEvente){
Rectanglelocation=null;
intchildID=e.childID;
if(childID==ACC.CHILDID_SELF){
location=getBounds();
}
if(childID>=0&&childIDlocation=newRectangle(cx,childID*lineHeight+cy,maxX,lineHeight);
}
if(location!
=null){
Pointpt=toDisplay(newPoint(location.x,location.y));
e.x=pt.x;
e.y=pt.y;
e.width=location.width;
e.height=location.height;
}
}
publicvoidgetChildCount(AccessibleControlEvente){
e.detail=colors.size();
}
publicvoidgetState(AccessibleControlEvente){
intstate=0;
intchildID=e.childID;
if(childID==ACC.CHILDID_SELF){
state=ACC.STATE_NORMAL;
}elseif(childID>=0&&childIDstate=ACC.STATE_SELECTABLE;
if(isFocusControl()){
state|=ACC.STATE_FOCUSABLE;
}
if(rowSel==childID){
state|=ACC.STATE_SELECTED;
if(isFocusControl()){
state|=ACC.STATE_FOCUSED;
}
}
}
e.detail=state;
}
});
最后,我们需要提供一些方法方便程序员使用我们的控件。
publicvoidsetSelection(intindex){
if(index>=getItemCount()||index<0)
return;
oldRowSel=rowSel;
rowSel=index;
selectionChanged();
}
publicintgetSelectionIndex(){
returnrowSel;
}
publicintgetItemHeight(){
returnlineHeight;
}
publicvoidsetItemHeight(intheight){
lineHeight=height;
}
publicintgetItemCount(){
returncolors.size();
}
publicvoidadd(intcolorIndex,StringcolorName){
colorNames.add(colorName);
colors.add(newInteger(colorIndex));
}
我们开发的控件的使用也是非常简单的。
CustomListcustomlist=newCustomList(parent,SWT.V_SCROLL|SWT.H_SCROLL);
customlist.add(SWT.COLOR_BLACK,"BLACK");
customlist.add(S