groovy已经在SwingBuilder里为咱们预设了不少swing组件,以使咱们能够用下面这样的快捷形式来构建界面:java
swingBuilder.frame( //属性集 ){ //子组件 }
对于仅仅使用swing的原生组件,这没有任何问题,但在实际开发中可能会遇到须要使用对原生组件扩展后的自定义组件(如一个继承了JPanel的MyPanel类)的状况,而这个类明显不可能出如今SwingBuilder的预设列表里,那这种状况下是否是就意味着不可使用groovy为咱们提供的便利,只能用回传统的java形式去构建界面呢?ide
答案是否认的。SwingBuilder虽然不会预设用户自定义的组件,但它提供了几个接口可让用户把自定义的组件设置进去,这样就依然能够用groovy的快捷形式来构建界面了。这几个接口分别是:ui
public void registerFactory(String name, Factory factory)this
public void registerBeanFactory(String theName, Class beanClass)spa
registerFactory()
code
registerFactory方法能够注册一个自定义组件,name指定了经过swingBuilder构建组件的名称,如JFrame在swingBuilder里预设的name是"frame",这就使得咱们能够经过swingBuilder.frame()构建一个JFrame。
对象
第二个参数接收一个实现了Factory接口的类,这个类需指明(实现)如何实例化自定义组件(newInstance),如何处理add进来的子组件(setChild)等一系列策略。一般并不须要直接实现Factory接口,groovy为咱们提供了一个抽象类(AbstractFactory),咱们应优先继承这个类,而后再按需挑相应的方法进行重写。下面来看一个如何使用这个接口的例子:继承
/** * 自定义了一个能够显示背景图片的面板 * @author keenlight * */ class ImagePanel extends JPanel{ /** * */ private static final long serialVersionUID = 1L private BufferedImage image public ImagePanel(BufferedImage image){ super() this.image = image } public ImagePanel(BufferedImage image, LayoutManager layout) { super(layout) this.image = image } public void paintComponent(Graphics g) { super.paintComponent(g) if(image != null){ g.drawImage(image, 0, 0, this) } } } /** * ImagePanel的Factory * @author keenlight */ class ImagePanelFactory extends AbstractFactory{ private BufferedImage image ImagePanelFactory(BufferedImage image){ this.image = image } @Override public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map properties) throws InstantiationException, IllegalAccessException { def layout = properties.remove("layout") new ImagePanel(image, layout) } public void setChild(FactoryBuilderSupport builder, Object parent, Object child) { if (!(child instanceof Component) || (child instanceof Window)) { return } parent = parent as ImagePanel try { def constraints = builder.context.constraints if (constraints != null) { parent.add(child, constraints) if (child instanceof JComponent) { child.putClientProperty(LayoutFactory.DEFAULT_DELEGATE_PROPERTY_CONSTRAINT, constraints) } builder.context.remove('constraints') } else { parent.add(child) } } catch (MissingPropertyException mpe) { parent.add(child) } } }
定义好这些类后如何使其生效?只需一步:接口
def bgImage = ImageIO.read(new File(image_path)) swingBuilder.registerFactory("imagePanel", new ImagePanelFactory(bgImage))
注册好以后,ImagePanel组件即可像其余原生组件同样使用了(如上文的JFrame同样)。图片
registerBeanFactory()
registerBeanFactory是registerFactory的一种便利形式,调用此接口无需提供Factory,只需提供自定义类的类名便可。好比原生组件里的JPanel就是用这个接口注册的,SwingBuilder类中注册JPanel的源码以下:
registerBeanFactory("panel", JPanel)
这样就能够把JPanel注册为"panel"来使用了。其实registerBeanFactory这个方法里面同样也实例化了一个继承了AbstractFactory类,它会把调用这个方法注册的组件当作一个javaBean(方法名已经很明显了),因此它在继承AbstractFactory类时重写的newInstance方法里是直接返回调用class.newInstance()的实例化结果,class由第二个入参提供。因此对于那些在实例化时调用默认构造器就足够的组件,用registerBeanFactory()方法来注册会更为便利。固然,对于我上面定义的ImagePanel来讲就不行了,由于ImagePanel类在实例化时须要接受一个图片对象,因此必须使用略麻烦一点的registerFactory()。
小结
SwingBuilder清晰简洁的表现形式能够提升swing开发效率,而有了注册自定义组件的接口咱们也没必要再在java传统形式与SwingBuilder之间纠结,由于全部以往用java编写的自定义组件均可以以SwingBuilder的形式来重用了。