概述Swing核心组件

开发 后端
本文通过重用Swing核心组件的方式,我们的自定义组件能够使其在swing核心LAF和第三方LAF下都保持一致。

UI Delegate

如果说模型接口是最编写自定义组件最重要的部分,那么UI delegate则是最复杂的部分。主要问题在于:如何编写绘制逻辑使得他在所有的look and feels上都一致?有时,除非你编写每个look and feels所对应的UI delegate(像 SwingX project 就是通过这种方式),否则是无法实现的。但是,在某些情况下你会发现你可以通过组装现有的Swing核心组件来达到这种仿真的效果,在后面的部分,UI delegates代码将要注意那些平台相关的设置比如说颜色,字体和抗锯齿。


在"  Enhancing Swing Applications" 一文中,作者描述了通过的继承定义好的look and feels的简单UI delegate实现的样板代码。以install*和unstall*方法开始,如果你不打算使用他们,第三方的look and feel 可能会在基本的功能基础上增加一些额外的功能(举个例子来说,在slider上增加鼠标滚轮滚动功能)


在我们这个例子中,我们可以看到这个自定义组件包含一个slider和一组labels(包括图标和文本),由于所有的 JComponent都是容器类,我们可以轻松地通过在我们的installComponents方法中增加JSlider和JLabel(每个 label是一个选项control point)组件来达到这种仿真的虚拟界面效果(别忘了在uninstallComponents方法中移除他们)。通过重用Swing核心组件的方式,我们的自定义组件能够使其在swing核心LAF和第三方LAF下都保持一致。


当我们增加附属组件时,为了在创建和缩放时定位这些附属组件,我们需要实现自定义的LayoutManager。这是一个非常简单的工作(甚至有点乏味):这些range在右侧纵向并排排列,相邻的range则根据其自身的weight值拥有相应空间的垂直区域,滑标则占有全部垂直空间,***个和***一个选项(control points)作为slider的起点和终点。


注意这个特殊的实现非常的困难并且几乎不可能使用单一的UI delegate来完成,举例来说,一些LAFs使用了native Api 来绘制各自的控制(像滑标的滑道与滑块)。一些第三方的LAF可能不遵循UIManager中的设置而是提供自己的颜色和自定义的Api


现在回到我们的实现(使用JSlider),我们现在面临一个有趣的问题:滑标可以通过设置snapToTicks的值来决定是否自动对齐到最近的滑块刻度。这个行为控制通过在BasicSliderUI delegate中安装mouse montion监听器实现。我们要怎么做?其中一个选择是移除这个监听器并且安装上我们所提供的实现。另一个方法是提供自定义的 BoundedRangeModel实现,当设置非关联的range时修改它的值(value)。***种方法并非***-你无法信赖其他LAF下特殊的 SliderUI delegate实现逻辑,有的实现甚至不会去调用父类的方法。第二种方法稍微好一点,不过我们选择另一种方法实现,原因稍后说明。


我们的实现对这些附属组件使用类似树/表的单元格渲染模式(Cell renderer),slider只是用来渲染并且不获取任何事件(参考CellRendererPane)。这能使我们从LAF绘图和鼠标自定义事件中获益。在我们这个特殊的例子中,如果用户在slider滑块外点击鼠标。我们通过直接设置相应的range的值 (value)来代替原本的向鼠标点击方向滚动一个”块”,这就是我们为什么不使用前面提到的第二种方法的原因:我们自定义的鼠标监听器转换鼠标点击转换相应的range(相邻的或非关联的)并且设置他们的值,一旦这个唯一的监听器被安装到组件上(指这个CellRendererPane,而这个 slider 只是一个”橡皮图章”),我们可以保证没有其他的监听器阻碍我们的代码。

组件布局


倘若我们使用单元格渲染面板,我们需要覆盖掉它的paint方法来绘制真正的滑块。我们并没有绘制那些选项标签因为他是这个组件“真正的”子组件,注意滑块的绘制在一个单独的方法中完成,这样可以允许第三方的LAF只覆盖的这个滑块的绘制逻辑而不是改变整个绘制逻辑。

  1. @Override  
  2. publicvoidpaint(Graphicsg,JComponentc){  
  3. super.paint(g,c);  
  4. this.paintSlider(g);  
  5. }  
  6.  
  7. protectedvoidpaintSlider(Graphicsg){  
  8. RectanglesliderBounds=sliderRendererPane.getBounds();  
  9. this.sliderRendererPane.paintComponent(g,this.slider,  
  10. this.flexiSlider,sliderBounds.x,sliderBounds.y,  
  11. sliderBounds.width,sliderBounds.height,true);  


测试应用程序

现在我们拥有了一个完整的自定义滑标组件,是时候该测试它了。这个测试应用程序创建了一个滑标,它包含一些相邻的和非关联的range,并为其注册了改变监听器(change listener)。一旦发生改变事件,我们计算图标的尺寸比例并绘制它(该图标使用Tango Desktop Project的SVG-to-Java2D converte来转换,具体参考  Transcoding SVG to Pure Java2D code一文)。

选择不同值的运行效果


显示了在不同look and feels下的滑标,从左到右,这些 LAF为:Windows (core), Metal (core), Motif (core), Liquid (third party), 和 Napkin (third party).如你所见,这个新组件提供了和当前所设置的LAF一致的外观。

在不同look and feel 下的自定义滑标


现在要何去何从?阅读Swing核心组件的代码。下载并学习开源组件的源代码(像SwingX 或Flamingo),然后开始构造你自己梦想中组件吧!

【编辑推荐】

  1. Swing应用程序处理函数
  2. 浅谈SwingWorker的使用
  3. AWT,SWT和Swing的布局管理器与Look And Feel机制
  4. 概述SWT采用AWT和Swing的一些优点
  5. Swing组件的paint方法的处理流程
责任编辑:佚名 来源: 人民邮电出版社
相关推荐

2009-07-14 18:05:28

轻量级Swing组件

2009-07-16 11:19:48

外部线程Swing组件

2009-07-15 16:18:08

JSci.swing包

2009-07-17 16:19:16

Swing核心组件

2009-07-10 17:43:10

Swing.table

2009-07-16 16:46:39

Swing窗体

2009-07-16 17:09:02

Swing应用程序

2009-07-15 13:06:38

Swing组件

2009-07-10 18:06:59

JTree Swing

2009-07-17 16:32:34

Timer对象Swing

2009-07-14 09:09:08

Swing模型过滤器

2009-07-15 11:02:32

Swing组件

2009-07-10 12:30:12

Swing组件

2009-07-14 17:21:42

Swing组件

2009-07-17 11:13:46

AWT和SwingSwing组件

2009-07-10 17:03:17

AWT组件Swing组件

2009-07-17 14:38:51

轻量级Swing组件

2009-07-10 16:29:32

Swing组件

2009-07-15 10:14:14

JApplet和JFrSwing

2009-07-14 15:39:34

Swing大多数控件
点赞
收藏

51CTO技术栈公众号