现在基于 Eclipse 的应用越来越多很多桌面应用都是用Eclipse开发的Eclipse提供了一套 SWT/JFACE 的控件库使得人们开发界面应用极大的方便但是SWT/JFACE的控件库毕竟有限在应用开发是我们不可避免地要自己开发一些自定义的控件本文通过开发一个颜色列表控件的实例介绍了Eclipse自定义控件开发中所要用到的技术
目标读者必须熟悉Java开发并且有一定的Eclipse开发经验
在Eclipse网站上有一篇相关的文章Creating Your Own Widgets using SWT该文介绍了开发自己控件的很多基本概念方法并且通过实例进行了介绍非常好但是其所用的实例比较简单还有很多控件开发中所要涉及到的内容例如键盘鼠标事件的处理滚动条焦点的处理等等没有提及本文通过开发一个自定义的颜色列表控件的实例全面地介绍了自定义控件所涉及的技术同时读者也可以对该实例进行扩展实现自己的列表控件
SWT中提供的标准列表控件非常简单只能提供字符串的选择我们经常需要提供一些图形列表供用户选择这就需要自己开发自定义的列表控件颜色选择列表是我们常用的一种图形列表我们就以此为例进行介绍以下是我们将要开发的颜色列表
我们在开发自定义控件时主要考虑以下问题
自定义控件的绘制通常我们需要自己对控件的形状或图案进行绘制
控件对键盘事件的响应当焦点进入控件用户进行键盘操作通过键盘对控件进行控制时我们需要让控件对用户的操作进行响应例如在列表中用户会通过上下箭头改变列表的选择项
控件对鼠标事件的响应当用户用鼠标选中控件进行操作时控件必须作出相应的反应
控件对焦点事件的响应当界面焦点进入或移出控件通常我们需要将控件绘制成得到或失去焦点的形状例如当焦点进入列表时一般被选中的列表项会有虚框表示选中
响应TAB键对于一个可操纵的控件用户可以用TAB键将焦点移入或移出
响应滚动条事件当控件有滚动条时我们需要响应用户对滚动条的操作完成对控件的绘制工作
提供事件监听机制程序员使用你的控件时通常需要监听控件中发生的一些事件这样当事件发生时他们能够进行相应处理
提供辅助功能(Accessibility)辅助功能是方便残障人士使用时必须的标准控件都会提供相应的支持我们自定义的控件也不例外
提供功能接口方便程序员访问通常为方便程序员使用时获取控件中的信息或进行设置我们需要提供一些接口
首先我们要开发的列表控件是一个基本控件所以我们选择Canvas作为我们开发的基类
public class ColorList extends Canvas {
Vector colors = new Vector();// 用于保存我们颜色控件中的颜色值
Vector colorNames = new Vector(); // 用于保存颜色控件中的颜色名字
int rowSel = ; // 用于保存当前选中的行号
int oldRowSel = ; // 用于保存上一次选中的行号
int maxX maxY;// 用于保存列表的宽度和高度
int lineHeight; // 用于设置行高
int cx = ;// 滚动条滚动后控件的图形相对于控件可见区域左上角的x坐标
int cy = ;// 滚动条滚动后控件的图形相对于控件可见区域左上角的y坐标
}
控件开发最重要的就是控件的绘制了控件的绘制可以通过添加PaintListener在它的paintControl方法中进行
addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
GC gc = egc;
Point size = getSize();
int beginx = ex;
int beginy = (ey / lineHeight) * lineHeight;
int beginLine = (ey cy) / lineHeight;
int endLine = beginLine + eheight / lineHeight + ;
if (endLine > getItemCount())
endLine = getItemCount();
for (int i = beginLine; i < endLine; i++) {
boolean selected = false;
if (i == rowSel)
selected = true;
onPaint(gc i cx beginy + (i beginLine) * lineHeight
selected);
}
}
});
这里要注意的是从PaintEvent中获取的xyheightwidth是需要重绘的区域xy是以控件的左上角为原点的坐标在我们的程序中为了性能起见我们先根据需要重绘的区域计算出需要重绘的行数只重绘相应的行而不是将整个控件重绘我们程序中用到的onPaint用于绘制一行
接下来我们要让我们的控件响应键盘上下键对列表项进行选择我们已对向上键的处理为例首先当用户按了向上键时我们需要改变选择并且重绘旧的和新的选择项如果选择项已经到了列表的顶部我们还需要同时滚动滚动条
addListener(SWTKeyDown new Listener() {
public void handleEvent(Event event) {
switch (eventkeyCode) {
case SWTARROW_UP: // 处理向上键
if (rowSel != ) {
oldRowSel = rowSel;
rowSel;
if (oldRowSel != rowSel) { //发送消息让控件重绘
((Canvas) eventwidget)redraw(cx (rowSel + cy
/ lineHeight)
* lineHeight maxX lineHeight* false);
}
if (rowSel < cy / lineHeight) { //如果需要滚动滚动条
ScrollBar bar = ((Canvas) eventwidget)
getVerticalBar();
barsetSelection(bargetSelection() lineHeight);
scrollVertical(bar);
}
selectionChanged(); // 发送selectionChanged事件
}
break;
case SWTARROW_DOWN: // down arror key
…
break;
}
}
});
接下来我们要让我们的控件响应鼠标对列表项进行选择首先我们要计算出鼠标选中的行号注意MouseEvent中的y值只是相对于控件左上角的坐标我们需要加上滚动出了控件的部分
addMouseListener(new MouseListener() {
public void mouseDoubleClick(MouseEvent e) {
}
public void mouseDown(MouseEvent e) {
int row = (ey cy) / lineHeight; //计算选中的行
if (row >= ) {
oldRowSel = rowSel;
rowSel = row;
}
if (oldRowSel != rowSel) { // 重画旧的和新的选择项
((Canvas) egetSource())redraw(cx (ey / lineHeight)
* lineHeight maxX lineHeight false);
((Canvas) egetSource())redraw(cx (oldRowSel + cy
/ lineHeight)
* lineHeight maxX lineHeight false);
}
selectionChanged();
}
public void mouseUp(MouseEvent e) {
}
});
当我们的控件获得焦点时选中的列表项需要有虚框表示控件得到焦点当获得或失去焦点是我们这里只需要简单的通知选中的项重画
addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e) {
((Canvas) egetSource())redraw(cx rowSel * lineHeight maxX
lineHeight true);
}
public void focusLost(FocusEvent e) {
((Canvas) egetSource())redraw(cx rowSel * lineHeight maxX
lineHeight true);
}
});
我们在绘制每一个列表项时可以加入判断当前控件是否得到焦点如果控件得到了焦点我们就在选中的项目上画一个虚框下面是我们绘制一个列表项的代码注意在代码的最后绘制焦点的虚框
void onPaint(GC gc int row int beginx int beginy boolean isSelected) {
Color initColor = gcgetBackground();
Color initForeColor = gcgetForeground();
if (isSelected) {
gcsetBackground(DisplaygetCurrent()getSystemColor(
SWTCOLOR_LIST_SELECTION));
gcfillRectangle(beginx beginy maxX lineHeight);
gcsetForeground(DisplaygetCurrent()getSystemColor(
SWTCOLOR_LIST_SELECTION_TEXT));
} else {
gcsetBackground(initColor);
}
gcdrawString((String) colorNamesget(row) beginx + beginy);
Color color = DisplaygetCurrent()getSystemColor(
((Integer) colorsget(row))intValue());
gcsetBackground(color);
gcfillRectangle(beginx + beginy + lineHeight );
gcsetBackground(initColor);
gcsetForeground(initForeColor);
if (isFocusControl() && isSelected)
gcdrawFocus(cx beginy maxX lineHeight);
}
作为一个可操作的控件TAB键的支持也是很重要的由于我们的控件是从Canvas继承过来的不支持TAB键下面的代码使我们的控件有TAB键的支持
addTraverseListener(new TraverseListener() {
public void keyTraversed(TraverseEvent e) {
if (edetail == SWTTRAVERSE_TAB_NEXT
|| edetail == SWTTRAVERSE_TAB_PREVIOUS) {
edoit = true;
}
};
});