插件开发
Hello,大家好~不知道还有没有老朋友记得我。
N年前那个写流程自动化测试的程序媛就是我,可能看完那篇文章很多人认为我是自动化测试方向。
No,No,No~今天破案了,我真正的主要技术方向——Eclipse插件开发。是的,如此小众的技术,有点疑惑都满网找不到答案的技术。
思考了很久这块可以写点啥,基础开发我也不多说了,愿意看这篇文章的我也就默认已经是Eclipse插件开发入门了。
Eclipse插件开发,接触过这块的同学们都知道,无论是控件也好,向导视图也罢。但凡每次开发个不起眼的小功能,从零开始堆代码,都很烦躁,各种composite开始套,各种GridLayout布局开始调。当你的公司要求你开发大量的插件功能时,可能多数的时间你都在堆砌这种烦躁的代码。
在我司的EOS Platform里封装了进行了大量的swt封装,今天我们就来说说其中最基础的控件类封装,即属性编辑器。我们先来看下我们的UI框架。
每个Tab页中我们进行了完整的对象编辑器的封装,每个对象编辑器上,放置了多个属性编辑器。
StringPropertyEditor就是一个属性编辑器,ObjectEditor为一个对象编辑器,我们可以在一个ObjectEditor上添加多个StringPropertyEditor。
StringPropertyEditor基础方法(不仅限于所展示的方法):
- setLabel(String label):标签名称。
- setPropertyName(String name):Text中value的数据映射key。
- doAddValidator(IValidator validator):添加校验器。
这个时候我们是不是可以看出SWT控件开发和属性编辑器开发的差异了?总结一下:
可以看下我们可以进行多少种不同的控件封装。
看到类名大家应该差不多都清楚每个属性编辑器的大体功能。我们就拿一个StringPropertyEditor看看它是如何封装的吧。
StringPropertyEditor
StringPropertyEditor就是Label Text,例如:
1.首先它一定是先有一个对象属性的基类AbstractPropertyAccessor,基类中的方法:
这个基类涉及到三个属性:
- propertyName:value的数据映射key。
- element:默认为当前对象编辑器中的value对象,可给单个属性编辑器设置单独的value对象。But!该value对象中必须包含propertyName设置的key值。
- introspector:这是个什么?这是个数据访问接口对象Introspector。该接口定义了如何根据一个名称(propertyName)来访问一个对象(element)中的值。这样设计为了提供灵活性,因为通常访问数据有两种方式。一种是根据Bean的属性进行访问,还有一种是使用Map中的Key进行访问,所以设计这个接口来访问对象中的数据,用户如果需要的话,也可以提供新的实现,如直接访问对象的Field或者其它。简单看下该接口的一个实现类所提供的方法。
通过Property的方式来访问一个Bean对象,在Property 不存在的情况会抛出异常。对于对象的存储用ognl.Ognl来实现。
2.AbstractPropertyAccessor基类只是提供了数据层的封装,我们还需要一层封装来提供属性编辑器的基本实现AbstractPropertyEditor,这个类封装了所有控件的布局以及可能的操作。
其实这是相当长的一个封装,抽取出核心事务,我们来说一说。
- 添加控件
这是一个核心的方法,你不需要去调用这个方法,在对象编辑器中的基类里会自动调用来这个方法来绘制所有添加的属性编辑器。
buildEditorControl创建用来编辑的SWT控件,即控件,控件的基本提示信息,控件的GridData,控件的错误信息的图片等等。该方法中通过调用保护方法protected abstract IControlCreator createControlCreator()来获取真实的子类控件。
getLayoutDataBuilder用来封装控件的布局,同样,子类可以通过重构来改变自己的布局。
这个方法同样是对控件的处理,之所以同控件的添加分开,是因为这个方法中的操作并不是每个控件必须的,根据需要继承开发。这个封装方法中做了两件事,一个是binding,也就是处理控件的联动,这里封装了控件的属性值绑定联动,以及反向绑定的联动处理。另一个就是为当前的编辑控件增加焦点监听器。
- 动态更改控制值
这个方法除了在load的时候会调用,在对象编辑器使用的过程中,控件的联动也会使用到,比如最常见到的,浏览资源,点击浏览的button弹出资源选择框,根据选中的对象回填对象编辑器中的值,就会用到该方法。在这个setValue方法中,是通过数据改变的封装接口去实现的。我们可以看下这个数据改变的容器接口。
其中fireValueChanged(ValueChangeEvent r_Event)就是调用所有的值监听器,通知当前控件的值已经改变。所以不同的对象编辑器之间都可以使用这个fireValueChanged来进行数据的联动处理。
- 容器布局
在build方法中我们简单提到了getLayoutDataBuilder获取布局,这个方法获取的布局类GridLayoutDataBuilder,同样是我们经过封装。
可以看到,在这个类里,我们已经对布局进行了一系列初始的封装,除非有特殊的布局需求,可以调用其中的方法更改布局,其他大部分的场景默认布局已经可以满足。
这就是对象属性的基类AbstractPropertyAccessor的核心处理,还有很多方法的封装都是可以根据用户的需要自行定义,当然根据控件有需要的可以再封装一层包含各类监听接口的基类,例如:KeyListener, FocusListener, SelectionListener, ModifyListener等。
3.说完了基类,StringPropertyEditor就变得简单了。
值得说一下的就是文本框的绘制方法了。
因为这里用到了文本框的封装,我们来看下文本框的封装。
做的事很简单,将固定样式的Text放入到我们的属性编辑器上,并且增加Modify的监听。除此之外我们还有对其他单个控件的封装。
至此,我们一个属性编辑器就开发完成了。
下面我们看看对象编辑器ObjectEditor的封装。
ObjectEditor
根据对象编辑器和属性编辑器的关系,不难理解,对象编辑器就是对属性编辑器的加载、放置、布局。核心方法build,就是遍历调用属性编辑器的绘制。
这是一个一目了然的方法,对象编辑器中的其他操作方法亦是如此,都是遍历属性编辑器,对属性编辑器的挨个操作。
---
好了,我们完成了控件的封装,是不是觉得封装方法略多,好麻烦啊,我还是复制复制代码,一个个控件添加好了。
But!复杂的封装是一时的,日后便利的开发是永久的!
然后你又要问了,那么我们封装好了对象编辑器又如何,我们也放不到Wizard放不到View上啊。
So
下一期我们来说说
各种让我们挂上对象编辑器的控件工厂吧~
剧透图: