TableViewer的单元格中编辑日期

 

单元格编辑器的需求出现

最近我需要自己做一个小的应用程序,使用SWT来设计界面,其中需要用到JFace的TableViewer,在使用之后,发现它比Swing的JTable更好用,而且在向表格的单元格中加入编辑器的时候,更加方便、容易(也有可能是我对Swing的JTable中的单元格编辑器研究的不够造成的错觉……)

我需要向TableViewer生成的对象的单元格中加入编辑日期的编辑器(如图1所示)

1156161268b.jpg

图1 单元格中的日期编辑器

在网上利用关键字“JFace CellEditor”等等组合,可以容易地搜索到很多关于JFace界面组件中CellEditor这个类的覆写方法,但是,我查找了几天,也只是找到了一些对我没有用处的文章。

从8月29日到9月1日,用了四天的时候,终于很好的解决了这个编辑器的问题,所以现在用代码来小结一下收获:

自定义的日期单元格编辑器的代码

实现这个单元格编辑器的代码如下:

注:请快速浏览代码之后,详细的了解后面关于代码的解释

/**

 * Updated at 2007-8-30 下午04:31:44

 * <p>

 *

 * @author 郭凯

 *

 */

public class DatepickerCellEditor extends CellEditor {

       public static Logger log = Logger.getLogger(DatepickerCellEditor.class);

       private DatePickerCombo datepickerWidget;

       protected Date selectedDate;

       public DatepickerCellEditor() {

       }

       public DatepickerCellEditor(Composite parent, int style) {

              super(parent, style);

       }

       protected Control createControl(Composite parent) {

              datepickerWidget = new DatePickerCombo(parent, getStyle());

              datepickerWidget.getDp().addSelectionListener(new SelectionAdapter() {

                     public void widgetDefaultSelected(SelectionEvent event) {

                            applyEditorValueAndDeactivate();

                     }

                     public void widgetSelected(SelectionEvent event) {

                            selectedDate = datepickerWidget.getDate();

                     }

              });

              datepickerWidget.addTraverseListener(new TraverseListener() {

                     public void keyTraversed(TraverseEvent e) {

                            if (e.detail == SWT.TRAVERSE_ESCAPE

                                          || e.detail == SWT.TRAVERSE_RETURN) {

                                   e.doit = false;

                            }

                     }

              });

              return datepickerWidget;

       }

       protected void applyEditorValueAndDeactivate() {

              selectedDate = datepickerWidget.getDate();

              markDirty();

              fireApplyEditorValue();

              deactivate();

       }

       protected Object doGetValue() {

              focusLost(); // 在获取该单元格编辑器的值的时候,也应该令该单元格编辑器失去焦点

              return Constants.format(selectedDate);

       }

       protected void doSetFocus() {

              datepickerWidget.getDp().setFocus();

       }

       protected void doSetValue(Object value) {

              Assert.isTrue(datepickerWidget != null && (value instanceof String));

              selectedDate = Constants.parse((String) value);

              datepickerWidget.setDate(selectedDate);

       }

       protected void focusLost() {

              System.out.println("focusLost");

              if (isActivated()) {

                     applyEditorValueAndDeactivate();

              }

       }

       public Date getSelectedDate() {

              return datepickerWidget.getDate();

       }

       public void setSelectedDate(Date date) {

              datepickerWidget.setDate(date);

       }

}

首先,得关注几个与其他CellEditor的实现不一样的地方

1.       在createControl()方法中,我们没有对datepickerCombo或者datepickerCombo中的属性dp添加焦点监听器(代码中addFocusListener这部分被注释掉了)。

2.       doGetValue()方法中,我们让当前的CellEditor失去了焦点(这里可以参考ComboBoxCellEditor的代码,不难发现它的doGetValue()方法是不处理焦点的)

这段代码依旧不完美,因为doGetValue()方法中如果让CellEditor失去焦点的话,还会出现问题——因为doGetValue()调用的时机是在CellEditor的值发生改变的时候,即是说每次我们打开日期CellEditor,修改一次日期,就会丢失该单元格编辑器的焦点,那样的结果使得用户不得不点击一下其他的单元格,再点回到这个单元格,重新选择其他日期(假定他第一次没有选对的话)。这就意味着这里的实现很ugly!

该怎么办呢?重新分析~~

我们的最初的问题是这样的

来自http://sourceforge.net/projects/swt-datepicker的SWT的日期控件是相当不错的,当然自己用的时候,还是需要修改相关代码。

该控件如图2所示,分为三个部分——一个编辑框、一个按钮、一个画有日历的面板

1156162c92e.jpg

图2 SWT-Datepicker控件截图

根据源代码,我们了解到他们各自的事件控制分别在textEvent()、arrowEvent()、dpEvent(),辅助的事件控制在popupEvent()、comboEvent()之中

直接使用没有修改过的DatepickerCombo到我们的DatepickerCellEditor当中的时候,会出现焦点无法捕获的问题。我们在DatepickerCombo类的构造器中,可以找到一段定义Listener接口对象的代码

              Listener listener = new Listener() {

                     public void handleEvent(Event event) {

                            log.info(event);

                            if (popup == event.widget) {

                                   popupEvent(event);

                                   return;

                            }

                            ……

                     }

       };

我们在handleEVent()方法的第一行加上log,而后看看这些事件信息。

结果使用未修改的代码的时候,我们看到当日历面板被箭头按钮弹出的时候,它获得焦点后马上就丢失了焦点。

………………………………(这里省略了思路出现问题绕弯子的过程)

最终我们关注到了一个已有的实现类ComboBoxCellEditor类的代码,发现这个类里面使用的是CCombo,而CCombo的代码与DatepickerCombo之间存在着惊人的相似。可是在使用ComboBoxCellEditor的时候却不会出现焦点方面的问题。

仔细查看了代码之后,发现CCombo与DatepickerCombo最大的不同就在于焦点处理的代码:

DatepickerCombo类是将FocusIn和FocusOut两个事件的代码放到textEvent()、dpEvent()、arrowEvent()等几个事件处理方法之中,而CCombo则将焦点处理集中交给handleFocus()方法,其代码如下:

void handleFocus (int type) {

       if (isDisposed ()) return;

       switch (type) {

              case SWT.FocusIn: {

                     if (hasFocus) return;

                     if (getEditable ()) text.selectAll ();

                     hasFocus = true;

                     Shell shell = getShell ();

                     shell.removeListener (SWT.Deactivate, listener);

                     shell.addListener (SWT.Deactivate, listener);

                     Display display = getDisplay ();

                     display.removeFilter (SWT.FocusIn, filter);

                     display.addFilter (SWT.FocusIn, filter);

                     Event e = new Event ();

                     notifyListeners (SWT.FocusIn, e);

                     break;

              }

              case SWT.FocusOut: {

                     if (!hasFocus) return;

                     Control focusControl = getDisplay ().getFocusControl ();

                     if (focusControl == arrow || focusControl == list || focusControl == text) return;

                     hasFocus = false;

                     Shell shell = getShell ();

                     shell.removeListener(SWT.Deactivate, listener);

                     Display display = getDisplay ();

                     display.removeFilter (SWT.FocusIn, filter);

                     Event e = new Event ();

                     notifyListeners (SWT.FocusOut, e);

                     break;

              }

       }

}

       关键代码以蓝低白字显示。而CCombo类中的一个重要的属性对象的filter的声明代码则放在CCombo的构造器中,其代码如下:

       filter = new Listener() {

              public void handleEvent(Event event) {

                     Shell shell = ((Control)event.widget).getShell ();

                     if (shell == CCombo.this.getShell ()) {

                            handleFocus (SWT.FocusOut);

                     }

              }

       };

这个监听器对象意味着,shell的任何响应事件将会引发FocusOut事件处理代码的执行,这也使得FocusIn和FocusOut事件处理代码中都应该及时的清除filter监听器,否则可能导致GUI直接当在事件的响应死循环中。(另外我们可以看到SWT文档中声明了filter的对性能可能造成的影响,所以在使用这种可能影响性能的工具的时候,还是应该多多参考Eclipse附带的源代码)

最终DatepickerCellEditor类的源代码为:

/**

 * Updated at 2007-8-30 下午04:31:44

 * <p>

 *

 * @author 郭凯

 *

 */

public class DatepickerCellEditor extends CellEditor {

       public static Logger log = Logger.getLogger(DatepickerCellEditor.class);

       private DatePickerCombo datepickerWidget;

       protected Date selectedDate;

       public DatepickerCellEditor() {

       }

       public DatepickerCellEditor(Composite parent, int style) {

              super(parent, style);

       }

       protected Control createControl(Composite parent) {

              datepickerWidget = new DatePickerCombo(parent, getStyle());

              datepickerWidget.getDp().addSelectionListener(new SelectionAdapter() {

                     public void widgetDefaultSelected(SelectionEvent event) {

                            applyEditorValueAndDeactivate();

                     }

                     public void widgetSelected(SelectionEvent event) {

                            selectedDate = datepickerWidget.getDate();

                     }

              });

              datepickerWidget.addTraverseListener(new TraverseListener() {

                     public void keyTraversed(TraverseEvent e) {

                            if (e.detail == SWT.TRAVERSE_ESCAPE

                                          || e.detail == SWT.TRAVERSE_RETURN) {

                                   e.doit = false;

                            }

                     }

              });

              datepickerWidget.addFocusListener(new FocusAdapter() {

                     public void focusLost(FocusEvent e) {

                            log.info("datepickerWidget focus lost caused by :\n" + e);

                            DatepickerCellEditor.this.focusLost();

                     }

              });

              return datepickerWidget;

       }

       protected void applyEditorValueAndDeactivate() {

              selectedDate = datepickerWidget.getDate();

              markDirty();

              fireApplyEditorValue();

              deactivate();

       }

       protected Object doGetValue() {

              return Constants.format(selectedDate);

       }

       protected void doSetFocus() {

              datepickerWidget.setFocus();

       }

       protected void doSetValue(Object value) {

              Assert.isTrue(datepickerWidget != null && (value instanceof String));

              selectedDate = Constants.parse((String) value);

              datepickerWidget.setDate(selectedDate);

       }

……

}

小结:参考Eclipse官方的源代码,仍然是学习的重要手段,加上多看示例代码,这两种方法可以大大减少重复劳动。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用 TableViewer 的 `addFilter` 方法来实现搜索列值的功能。首先,你需要创建一个实现了 `IViewerFilter` 接口的过滤器类。在该类,你需要实现 `select` 方法来判断给定元素是否应该显示在表格。在 `select` 方法,你可以通过传入的参数 `element` 来访问表格的每一行数据。然后,你可以使用 `TableItem` 对象的 `getText` 方法来获取该行每个单元格的文本值。最后,你可以将搜索关键字与单元格文本值进行比较,如果匹配则返回 `true`,否则返回 `false`。 接下来,你可以将该过滤器类实例添加到 TableViewer ,以便在搜索时调用。你可以使用 `addFilter` 方法将过滤器添加到 TableViewer 。当用户输入搜索关键字时,你可以在 Text 控件的 `ModifyListener` 更新过滤器的搜索关键字,并调用 `refresh` 方法重新加载数据以显示匹配的结果。 以下是一个示例代码片段,其 `MyFilter` 是实现了 `IViewerFilter` 接口的过滤器类: ``` // 创建一个 TableViewer 实例 TableViewer viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION); // 设置 TableViewer 的内容提供者和标签提供者 viewer.setContentProvider(new MyContentProvider()); viewer.setLabelProvider(new MyLabelProvider()); // 添加一个搜索过滤器 MyFilter filter = new MyFilter(); viewer.addFilter(filter); // 创建一个 Text 控件用于输入搜索关键字 Text searchText = new Text(parent, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH); searchText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL)); // 为 Text 控件添加 ModifyListener,在用户输入搜索关键字时更新过滤器 searchText.addModifyListener(e -> { filter.setSearchText(searchText.getText()); viewer.refresh(); }); ``` 在上面的代码,`MyContentProvider` 和 `MyLabelProvider` 分别是内容提供者和标签提供者类,你需要根据自己的需求实现这两个类。`MyFilter` 类是搜索过滤器类,你需要根据自己的需求实现该类。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值