|
通过二次调用Display的getColor()方法,我们找到设备的默认背景色和前景色,每次把适当的常数传递给这个方法,先用COLOR_BACKGROUND(背景色),然后用COLOR_FOREGROUND(前景色)。不管我们选择使用默认颜色还是指定自己的颜色,OutlineItem.paint()都会用背景色填充一个矩形,然后切换成前景色画剩下的内容。
请注意:MIDP规范要求,在画图的时候,必须覆盖项目显示区域的每一个象素。有些实现可能会在调用paint() 之前清除项目覆盖的区域,但是其它一些实现可能不会。如果您没有先用背景色填充矩形,那么您就会冒着失去可移植性的风险。
然后OutlineItem以每一缩进级别8个象素,从左向右画圆,如果项目处在折叠状态,就填充圆。圆的宽度和高度由字体的高度决定,所以圆的大小会根据不同的字体和尺寸在设备上恰当地缩放。因为圆的尺寸永远不会超过字体高度,所以文本挨着缩进边际加上字体宽度之后向右偏移。
请注意:长的字符串可能会弄乱屏幕,因为OutlineItem没有把行的长度考虑在内。我很乐意把文本环绕的实现作为一个练习留给您。
剩下的抽象方法让CustomItem基类请求子类计算项目的合适的最小尺寸和期望尺寸。OutlineItem 的实现很简单:
<%@ control language="c#" autoeventwireup="true" codefile="webusercontrol2.ascx.cs" inherits="webusercontrol2" %>public int getMinContentHeight()
{
return FONT_HEIGHT;
}
public int getMinContentWidth()
{
return indent * INDENT_MARGIN + FONT_HEIGHT;
}
public int getPrefContentWidth( int height )
{
return indent * INDENT_MARGIN
+ FONT.stringWidth( text ) + FONT_HEIGHT;
}
public int getPrefContentHeight( int width )
{
return FONT_HEIGHT;
}
|
您可以把这些方法当作CustomItem所实现的最小尺寸和期望尺寸访问器的回调。除非您覆盖它们,否则您的项目的getMinimumWidth()方法将返回getMinContentWidth()方法的结果,而项目的getMinimumHeight()方法将返回getMinContentHeight()方法的结果。
项目的期望宽度和高度几乎是用同样的方式决定,区别在于应用程序可以修改期望尺寸。一旦调用项目的setPreferredWidth()方法或setPreferredHeight()方法,那么对应的尺寸就相当于被锁定了。获取期望尺寸的调用将总是返回锁定的值。在建立项目时,两个维度都被解锁,您可以通过把某一维的尺寸设置为-1来解除它的锁定。
只有当getPrefContentWidth()方法和getPrefContentHeight()方法各自的维度解除锁定的时候,才调用这二个方法。它们应当返回最佳尺寸,让项目内容最佳显示,行环绕最小,没有剪辑。
OutlineItem没有环绕,所以最小高度和期望高度都等于当前字体的高度。期望宽度就是当前文本的宽度,等于文本当前字体所占空间加上扩展指示器的空间加上当前缩进的空间。最小宽度就是指示器的空间加上缩进的空间。
窗体布局
要建立布局,窗体不仅需要每个项目的最小尺寸和期望尺寸,还需要每个项目的布局指令:一位的标志,用于指定对齐和断行。项目的布局指令组合成一个整数。如果您没有指定任何布局指定,那么会得到默认的兼容MIDP 1.0的布局,在这种布局里,项目按行摆放,一个接一个。要指定不同的布局,可以使用位运算符OR,把各个预定义的的布局指令组合成一个整数,把它传递给setLayout()方法。
Item类定义了布局指令常量:
|
水平对齐 |
垂直对齐 |
断行 |
|
LAYOUT_LEFT |
LAYOUT_TOP |
LAYOUT_NEWLINE_BEFORE |
|
LAYOUT_RIGHT |
LAYOUT_BOTTOM |
LAYOUT_NEWLINE_AFTER |
|
LAYOUT_CENTER |
LAYOUT_VCENTER |
|
| LAYOUT_SHRINK |
LAYOUT_VSHRINK |
|
|
LAYOUT_EXPAND |
LAYOUT_VEXPAND |
|
窗体的布局算法有点复杂。在类的文档里有非常详细的解释,我只是归纳一句:算法符合“springs-and-struts”模式,工作起来有点象是AWT的SpringLayout和GridBagLayout布局管理器之间的交叉。
对于水平和垂直维度来说,每个项目都有对齐方式以及缩小或放大到适合指定行空间的设置。通过查询换行是在项目之前还是在项目之后,还可以指定项目是在行首还是在行尾出现。
为了保证移植性,您指定的布局指令不应该比实际需要多。默认的对齐选项,在不同的实现和不同的语言之间,会有差异,一般在那些不是从左到右阅读的语言上会出现。您不必指定布局,但是如果指定布局,会给项目一个默认布局,可能会帮助项目在外观或行为上实现预期的一致性。
OutlineItem 在构造函数里用下面这个调用设置自己的布局指令:
<%@ control language="c#" autoeventwireup="true" codefile="webusercontrol2.ascx.cs" inherits="webusercontrol2" %>// 定义布局
setLayout( LAYOUT_EXPAND | LAYOUT_TOP | LAYOUT_NEWLINE_AFTER );
|
布局指令包括LAYOUT_EXPAND, LAYOUT_TOP,以及 LAYOUT_NEWLINE_AFTER。不需要水平对齐选项,因为项目会充满所有可用水平空间。因为没有指定LAYOUT_VSHRINK 和 LAYOUT_VEXPAND,窗体会用自己的期望高度设置项目的高度,并用顶端对齐方式在行的垂直空间里对齐项目。在窗体中的项目,会在它的位置后面得到一个断行,所以每个项目都出现在自己的行里。
因为Outliner的窗体只包含OutlineItems,所以这个布局指令组合会把每个项目放在自己的行里,每行的宽度与窗体宽度一样,高度为项目的期望高度。
游历窗体
迄今为止,我们一直侧重的是定制项目的外观。现在,我们要考虑一下它的行为——它对用户输入响应的感知。
MIDP窗体有自己的内置术语,叫做游历(traversal)。这与桌面应用程序中切换输入焦点的概念类似。不管是在桌面还是在移动环境里,在任何给定时刻,只有一个UI组件拥有焦点,这意味着所有的用户输入动作都被导向这个组件。
例如,如果文本字段拥有焦点,那么按下键盘就会造成在文本字段的插入点之后出现字符。在典型的桌面应用程序里,箭头键在文本字段内移动插入点,制表键则把焦点转移到下一个组件。移动设备可能没有完整键盘。实际上,它甚至可能没有四个方向箭头。如果移动设备有方向键,那么左、右键可能负责移动插入点,上、下键可能负责转移焦点为。如果只有二个方向键,那么上、下键可能承担双重责任:移动插入点,在插入点到达字段的开始或结束位置的时候,转移焦点。
因为设备的差异很大,所以MIDP为定制项目提供了一种机制,支持用一致的、可移植的方式进行游历。在CustomItem的一个方法里包含了这个机制:
<%@ control language="c#" autoeventwireup="true" codefile="webusercontrol2.ascx.cs" inherits="webusercontrol2" %>protected boolean traverse(
int dir,
int viewportWidth,
int viewportHeight,
int[] visRect_inout )
|
|