从组合框控件看SWT与JFace的区别

Combo是SWT的组合框,ComboViewer是JFace的组合框,都是组合框,ComboViewer其实就是在Combo上面加上MVC的封装。记住下面的公式:

JFace = SWT + MVC

比如说要实现如下图的任务,选中一本书,显示它的价格。


首先定义领域类(Domain Class),如下所示:

/**
 * @author liuwenzhe2008@qq.com
 */
public class Book {
  private String name = "";
  private int price = 0;
  
  public Book(String name, int price) {
    this.name = name;
    this.price = price;
  }
  public String getName() {
    return name;
  }
  public int getPrice() {
    return price;
  }
}

现在有个书架,摆了好几本书:

  Book[] books = new Book[] {
      new Book("Java", 20),
      new Book("SWT", 30),
      new Book("JFace", 40),
      new Book("Eclipse RCP", 50)
  };

下面第一种方案采用SWT的Combo:

final Combo combo = new Combo(shell, SWT.READ_ONLY);
combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

把书架的书放在combo里面:

    for (Book book : books) {
      combo.add(book.getName());
    }

考虑到每一本书有不同的价格,换另一本书时就需要在更新价格控件,这样需要为Combo增加事件响应:

    combo.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        int index = combo.getSelectionIndex();
        Book book = books[index];
        text.setText(Integer.toString(book.getPrice()));
      }
    });

可以看到,SWT的Combo主要是通过索引(index)来访问item的,这样不够直接,也不够方便。

下面看看JFace的ComboViewer是怎么实现以上功能的。

首先定义一个ComboViewer控件:

ComboViewer comboViewer = new ComboViewer(shell, SWT.NONE);

需要的话,你也可以从ComboViewer中拿到SWT的Combo控件:

    Combo combo = comboViewer.getCombo();
    combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

由于JFace采用MVC模式,需要我们先回答3个问题:

1. 你要往控件输入(Input)什么东西?这相当于MVC模式中的模型(Model),可以是任意引用类型(即Object及其子类,只要不是int这些基本类型就行)。

这里我们需要往comboViewer输入书架上的书本,即:

comboViewer.setInput(books);

2. 控件如何组织它的内容?也就是需要找一个内容提供者(ContentProvider),这相当于MVC模式中的控制器(Controller),必须实现IContentProvider接口。

作为输入的书本已经是数组了,因此我们可以考虑使用JFace专门为数组(array,List,Vector)提供的实现类ArrayContentProvider,即:

comboViewer.setContentProvider(new ArrayContentProvider());

3. 控件如何显示?也就需要找一个标签提供者(LabelProvider),这相当于MVC模式中的视图(Viewer),必须实现ILabelProvider接口。

这里我们只需要显示书名就可以了,即:

    comboViewer.setLabelProvider(new LabelProvider() {
      @Override
      public String getText(Object element) {
        assert element instanceof Book;
        Book book = (Book)element;
        return book.getName();
      }
    });

好啦,通过回答以上3个问题,总算把书架上的书放到ComboViewer上了。累吧?你是不是觉得还不如SWT的Combo方便,一个for循环就搞定了,MVC搞得这么麻烦,是吧?有得必有失,有失必有得,MVC给我们带来更多的灵活性和可扩展性。先喝杯咖啡吧,听我慢慢道来,待会你会永远抛弃Combo,而衷心地爱上ComboViewer的。废话少说,我们先继续吧。

同样地,考虑到每一本书有不同的价格,换另一本书时就需要在更新价格控件,这样需要为ComboViewer增加事件响应:

    comboViewer.addSelectionChangedListener(new ISelectionChangedListener() {
      @Override
      public void selectionChanged(SelectionChangedEvent event) {
        ISelection selection = event.getSelection();
        assert selection instanceof StructuredSelection;
        StructuredSelection ss = (StructuredSelection)selection;
        Object obj = ss.getFirstElement();
        if (obj instanceof Book) {
          Book book = (Book)obj;
          text.setText(Integer.toString(book.getPrice()));
        }
      }
    });

看起来比SWT Combo的事件响应要复杂点,但你发现没有,这里不再通过索引(index)来访问元素(item),而是直接访问。

你可以会想,这又有什么所谓呢?

这么简单的需求,现在当然用哪种方法都无所谓。但客户总是得寸进尺的,软件总是善变的,好的软件设计一定要拥抱变化。

好啦,客户A提出第一个改进的需求:他看书喜欢带书名号,如“《SWT》”。

采用SWT方案的工程师说简单,只要在把书架的书放进Combo时加上书名号就OK了,即:

combo.add("<<" + book.getName() + ">>");

采用JFace方案的工程师也说简单,这个只是显示的问题,就交给视图(Viewer)吧,只要在LabelProvider函数输出加书名号就OK,即:

    comboViewer.setLabelProvider(new LabelProvider() {
      @Override
      public String getText(Object element) {
        assert element instanceof Book;
        Book book = (Book)element;
        return "<<" + book.getName() + ">>";
      }

每个人的兴趣爱好都是不同的,那问题来了,客户B认为简单才是最美,就是不喜欢显示书名号,但考虑到客户A的感受,希望我们加一个复选框(CheckBox),勾上显示书名号,不勾就不显示书名号。


这时候SWT方案就头痛了,那把书架的书放进Combo是究竟要不要加书名号,两个客户都不能得罪啊。不要怪客户的需求太BT,这就是SWT方案没有视图的后果。

JFace方案笑了,可以很轻松的解决:

    comboViewer.setLabelProvider(new LabelProvider() {
      @Override
      public String getText(Object element) {
        assert element instanceof Book;
        Book book = (Book)element;
        if (btnShowBookMark.getSelection()) {
          return "<<" + book.getName() + ">>";
        } else {
          return book.getName();
        }
      }

客户C来了,他希望书名可以排序,比较书"Eclipse"总是排在"SWT"前面。但为了兼顾其他客户,我们需要再加一个复选框(CheckBox),勾上为排序,不勾不排序。


这个需求对于SWT方案又是重拳一击,他快晕倒了,这就是依赖于索引(index)的结果。

JFace方案又笑了,简单啊,谁叫JFace控件天生就有比较器(Comparator)呢?看看下面的吧,该羡慕嫉妒恨了吧。

    comboViewer.setComparator(new ViewerComparator() {
      @Override
      public int compare(Viewer viewer, Object e1, Object e2) {
        if (!btnSort.getSelection()) {
          return 0;
        }
        return super.compare(viewer, e1, e2);
      }
    });

客户D来了,她是个富婆,看不起穷人,只卖贵的,不买对的,她不希望看到35块钱以下的书(包括35块也不行),把这些书通通过滤调。但为了兼顾一下像我们这样的苦孩子,我们需要再加一个复选框(CheckBox),勾上为过滤,不勾不过滤。

这个需求对于SWT方案更加的BT,SWT方案这回彻底疯掉了,为什么有这么多刁钻的客户呀?

JFace方案还是笑了,这样的客户多多益善啊,谁叫JFace控件天生就有过滤器(Filter)呢?

    comboViewer.addFilter(new ViewerFilter() {
      @Override
      public boolean select(Viewer viewer, Object parentElement, Object element) {
        if (!btnFilter.getSelection()) {
          return true;
        }
        assert element instanceof Book;
        Book book = (Book)element;
        return book.getPrice() > 35;
      }
    });

看看上面的实现吧,SWT方案们,JFace的大门永远为你们打开的,过来学学JFace吧!


 ---------------------- 本博客所有内容均为原创,转载请注明作者和出处 -----------------------

 作者:刘文哲

 联系方式:liuwenzhe2008@qq.com

 博客:http://blog.csdn.net/liuwenzhe2008

-------------------------------------------------------------------------------------------------------------



                
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值