一、需求
这次的嵌入式项目中的画面,每次迁移的时候总是会发生无法忍受的闪屏。系统中的画面时基于AWT的,考虑下来闪屏主要是以下两个方面:
1)、画面生成特别慢,应该是AWT中实现的功能太多,影响了画面生成的效率
2)、画面刷新时,总是会先刷背景,再画内容。由于硬件性能的影响,整个刷新过程可以看到很明显的闪屏。
修正案1:
针对以上的疑点1),通过以下两种途径来修正:
1)、整个画面生成后,再显示到屏幕上。降低画面生成时的时间消耗对显示的影响
2)、将生成后的画面缓存,提升画面生成的效率。
修改完成后发现,虽然画面的生成时间有300毫秒左右提升到70毫秒,但是闪屏依旧。如此看来应该是硬件平台上的AWT实现的问题。
修正案2:
重新实现一个有限功能的画面系统,在这个画面系统中最终生成一个图片,最后直接画到画面上。
二、设计
1、设计目标
1)、绝对不闪,只是实现50毫秒级的画面描画,而不闪屏。
2)、实现业务中所有需要的功能(还好是个嵌入式的系统,画面不超过40个)。
2)、提供类似于AWT的接口,方便业务人员的实装。
2、画面系统中的关键概念:
MainFrame:对应一个AWT的Frame,负责画面中最终描画,和AWT事件的分发。
View:逻辑画面,所有画面的基类。
Dialog:逻辑对话框。本来不需要重新实现Dialog,可是JVM实现的Dialog总是会影响后面画面的描画,并且效率奇低。只能自己做一个。
Panel:所有控件的容器。View中的控件管理都会委托给Panel。
Canvas:画布,一个Canvas对应一个BufferedImage,最终画面的显示,就是将这个BufferedImage显示出来。
Component:控件的基类。实现了Lable、TextField、Button、List、Panel、ScrollPanel。
3、设计思想
每个View对应一个Canvas,View中的每个控件的操作都会映射到这个Canvas中,提升画图的效率。一个控件状态的变更,不会导致整个view的重绘,而只会重绘这个控件。但是提供重绘这个View重绘的接口。
画面的迁移只是View的逻辑迁移,不会导致AWT的画面迁移。
支持Panel的嵌套,以方便共同画面模块的实现。
三、实现要点
1、控件系
类图
所有的控件都继承自Component,Component类实现了所有控件的共同属性,如前景背景,字体图片,可视等等。所有的控件都必须放在panel中,View中其实就是放了一个RootPanel。只有实现了Scrollable接口的控件才能放入ScrollPanel中,其实最好是Panel实现Scrollable,而现在的项目里没有用到所以就没有实现。
2、画面管理系
类图
系统中的可获得焦点控件都要实现Focusable接口,而View类包含一个Focusable的属性。一个画面只能有一个控件获得焦点,所以我把焦点监听放到了View中,以方便管理。而整个系统中只有TextField实现了Focusable。
系统Activable监视鼠标的压下事件,这可以使控件可以在控件拖动时实现更大的灵活性。比如滚动条,开始滚得时候可能在滚动条上,而托托鼠标可能就偏了,最为继续跟踪这个事件就通过Activable来保存这个控件。系统中只有滚动条实现了这个接口。
整个系统中只会有一个MainFrame的实例,它负责将View生成的图像描画到屏幕上,并将画面上的鼠标事件发送给View。由于系统中的画面不支持 键盘,所以不实现键盘事件。ViewManagerImp中包含了一个View和Dialog的引用,他们都描画都会被委托为MainFrame。Dialog的描画最终也会委托给View,以防止短时间内画 面的多次描画导致的闪屏。
View的初始属性的设置与迁移共同参数的生成都由ViewManager管理,而View对象的实际生成由ViewFactory完成,不过提供了一个根据类名通过反射生成的实例的默认实现。这里通过ViewManger和ViewFactory把对象的生成与属性的设置进行分离,以便于扩展。
四、改善点
1、将画布放入到每个控件中
在现有系统中,所以控件的描画都会委托为View中的Canvas,这虽然提升了性能,但是也使得控件的描画有许多限制。比如描画工程只能由控件自己完成,而不能由上层来控制。如List其实有两种状态,一种是直接放到Panel中,不需要变更大小。直接描画就可以;而当其放在ScrollPanel中时,则可以改变大小,由ScrollPanel来滚动。在现有系统中就需要List区分这两种行为。而如果是每个控件都有自己的Canvas,那么父容器就可以选择做出不同的行为,这个应该更合理,更自由。当然,也是需要一定的性能损耗的。
2、控件初始化的时机变更
现有系统的空间初始化是与AWT类似的,在addComponent时初始化,这就有很多时机的问题。现在想来还是SWT那种直接在构造函数中传父容器的方式更简单,方便。
3、MainFrame与View的关系
现在是view是MainFrame的属性,其实不需要,View应该有ViewManager完全控制。ViewManager会将view的描画委托为MainFrame。
4、为了提升画面更新的效率,考虑引入修改缓存的概念
现在的系统是任何一个控件的状态改变都会直接反映到屏幕上,如TextField的输入等。
当画面上有多个自主更新的控件时,并且不希望每个控件的更新都导致画面刷新,这时可以将图像的修改缓存,然后再一个特定时机一起刷新,当然这个时间间隔必须很小。