如何用SwingBuilder使用自定義的swing組件

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的形式來重用了。

相關文章
相關標籤/搜索