BlackBerry 应用程序开发者指南 第一卷:基础--第3章 创建用户接口(UI)


作者:Confach 发表于April 23,2006 15:02 pm
版权信息:可以任意转载, 转载时请务必以超链接形式标明文章原始出处 和作者信息.
http://www.cnblogs.com/confach/articles/382744.html


 

3

3 创建用户接口(UI

UI API

显示UI组件。

管理UI组件

创建客户定制的UI组件

操作图片

使用图像对象画图

监听UI对象的改变

UI API

当你为BlackBerry设备编写应用程序时,使用下面2UI API的一组:

  • MIDP UI API (javax.microedition.lcdui)
  • BlackBerry UI API (net.rim.device.api.ui)

如果你正在编写一个在任何MIDP兼容设备上运行的应用程序,请使用MIDP UI API.如果你正在编写专门运行在BlackBerry设备上的应用程序,那就使用BlackBerry UI API吧。BlackBerry API提供了访问BlackBerry设备的特定特性的功能,并且也允许更成熟的UI布局(layout)和交互。

:不要在同一个程序里既使用MIDP UI API,又使用BlackBerry UI API,否则会抛出异常。在应用程序中,UI框架支持一中类型的UI对象。

显示UI组件

显示屏幕(Screen)

UI 的主要结构是Screen。一个应用程序一次只能显示一个屏幕。
:不要使用Screen对象来输入文本。Screen对象没有明确实现此功能,它需要复杂的输入方法,例如国际化的键盘和7100系列的设备。为实现无缝得集成不同输入方法,扩展Field或者其任一子类。参看53页“创建定制的域”得到更多信息。

显示栈(Stack

Screen对象在一个一组有序的Screen显示栈里得到维护。在栈顶的Screen对象是显示给用户的活动Screen。当应用程序显示一个Screen时,它将这个Screen压入到栈顶。当关闭一个Screen,将这个Screen从栈里移出,然后显示栈里的下一个Screen,如果必要会重绘它。

:每个Screen在栈里只出现一次。如果同一个Screen压入到栈不止一次,VM会抛出一个运行时异常。当用户完成和Screen交互,应用程序必须将Screen从栈里移出,以致内存不必再用。不要在同一时间里使用多个Screen,因为每个Screen使用独立的线程。

Screen的类型

在多数情况下,创建一个Screen最有效的方法是创建一个扩展Screen或其任一子类,FullScreenMainScreen的类。

描述

Screen

使用Screen类定义一个管理器布局Screen上的UI组件,并且使用在超类Field定义的常数的样式(Style)定义一明确的Screen

FullScreen

缺省的,一个FullScreen包含单个垂直[1]的域管理器(Field Manager)。使用一个FullScreen提供了一个空的Screen,在这个空的Screen上,你可以增加UI组件到这个标准的垂直布局里。如果需要另外类型的布局,例如水平的或对角的,使用一个Screen类,并且在里面增加一个管理器。

MainScreen

MainScreen类提供常见的标准BlackBerry应用程序常见特性。对你的应用程序的第一个Screen,使用一个MainScreen对象来保持和其他BlackBerry应用程序的统一。MainScreen提供一下的UI组件:

l         Screen标题的缺省位置,标题后的一个SeperatorField

l         一个包含在VerticalManager里的滚动的主界面。

l         有一个Close菜单项的菜单。

l         当用户点击Close菜单项或者按Escape键时缺省的关闭操作。

响应用户交互

BlackBerry API提供一个和Java标准版本类似的事件监听框架。特殊的,2个监听接口使程序接收和响应用户交互:TrackWheelListenerKeyboardListnenerScreen类和其子类都实现了这些方法。

提供screen导航(navigation

BlackBerry应用程序为用户提供一个菜单来完成操作。避免使用按钮(Button)或其他占据Screen空间的UI组件。

:按滑轮访问菜单。

当创建一个FullScreenScreen,在构造子里指明DEFAULT_MENUDEFAULT_CLOSE参数来提供缺省的导航。

FullScreen fullScreen = new FullScreen(DEFAULT_MENU | DEFAULT_CLOSE);

参数

描述

DEFAULT_MENU

这个参数增加一个缺省的菜单,它包含了不同的菜单项,这依赖域用户的上下文环境。例如,如果一个EditField获得焦点,将显示CutCopyPaste菜单项。所有已选择的域提供SelectCancel Selection菜单项。

DEFAULT_CLOSE

这个参数增加一个缺省行为的Close菜单项到菜单,当用户点击Close菜单项或者按Escapes按钮,如果Screen上的任何东西改变,一个确认的对话框将会出现。如果这个Screen是栈里的唯一一个Screen,应用程序将关闭。

当创建一个MainScreen时,缺省的导航会自动提供。

增加菜单项

创建MenuItem对象。

private MenuItem viewItem = new MenuItem("View Message", 100, 10) {

     public void run() {

        Dialog.inform("This is today’s message");

        }

     };

MenuItem构造子接受下面的3个参数:

参数

描述

text

菜单项的名称

ordinal

菜单项的顺序;一个越大的值表明了这个菜单项越靠近菜单的底部。

priority

接收缺省焦点的菜单项优先级

run()定义了当用户点击菜单项发生的操作的实现。如果你没有使用本地资源,重写toString()方法来指定菜单项的名字。

为了在应用程序加入上下文菜单给field,调用getLeafFieldWithFocus(),并且调用getContextMenu(),其返回值决定哪一个Field接收makeMenu()里的客户化菜单项。为了得到更多信息,参看60页的“创建客户化的上下文菜单”。

当增加你自己的菜单项时,显式的定义一个Close菜单项。

为了增加菜单项到Screen里,重写Screen.makeMenu()方法:

protected void makeMenu(Menu menu, int instance) {

    menu.add(viewItem);

    menu.add(closeItem);

}

如果你扩展Screen或其任一子类,那么当用户点击滑轮时,缺省的TrackwheelListener实现调用makeMenu()。

如果你没有扩展Screen,那么实现TrackwheelListener。特殊地,trackwheelClick()的实现创建一个新的菜单,增加菜单项以及在Screen上显示菜单。

public boolean trackwheelClick(int status, int time) {

    Menu appMenu = new Menu();

    makeMenu(appMenu, 0); // Add menu items.

    appMenu.show(); // Display the menu on screen.

    return true;

}

:为了创建菜单项提供附加的功能,请扩展MenuItem类。为了得到更多信息,参看60页“创建客户化的上下文菜单”。

显示对话框

PopupScreen类通过使用它的子类,DialogStatus,来提供创建对话框和状态Screen的特性。Popup screen不会压入到显示栈中,为了显示一个popup screen,调用Dialog.ask(int)Status.show().

为了控制对话框的布局,使用DialogFieldManager对象,为了得到更多的信息,参看50页的为一个PopupScreeen指定布局

为了显示一个对话框,使用下面的一个参数来调用Dialog.ask():

参数

描述

D_OK

显示一个字符串,并且提示用户点击OK

D_SAVE

实现一个字符串,并且提示用户点击SaveDiscard,或者CancelEscape取消

D_DELETE

显示一个字符串,并且提示用户点击Delete或者CancelEscape撤销

D_YES_NO

显示一个字符串,并且提示用户点击YesNo

 

int response = Dialog.ask(Dialog.D_SAVE);

if (Dialog.SAVE == response || Dialog.CANCEL == response)

    return false;

if ( Dialog.DISCARD == response )

    _item.deleteItem(_itemIndex);

为了指定一个对话框的缺省的响应,使用一个接受defaultChoice作为参数的Dialog.ask()版本。

int response = Dialog.ask(Dialog.D_YES_NO, "Are you sure?", Dialog.NO);

显示状态消息

调用Status.Show()显示一个状态消息。缺省的,状态屏幕保留其屏幕 2秒钟。

Status.show("Status screen message");

参看API参考获取Status.Show()的版本信息,它使你可以指定额外的参数,例如不同的图标或者保持状态对话框可见的时间长短。你可以创建模态的状态对话框(需要用户取消它们),也可以创建计时的状态对话框(在指定的时间后自动取消)。

显示域(Field

所有UI组件以包含在管理器里的成矩形的field的形式表现。Field的大小取决于它的布局需求。管理器为它们包含的field提供滚动(条)。

BlackBerry JDEnet.rim.device.api.ui.component包里提供一个预创建接口控件和组件的库。多数情况下,你可以使用这些对象构建UI应用程序。

为了创建指定的field控件(如包含多个元素的文本field),扩展Field类或者其任意子类来创建你自己定制的类型。为得到更多信息,参看53页的创建定制的field

:参看API参考获取更多关于指定field类的有效、支持的格式的信息。如果使用一个不支持的格式实例化一个Field,将抛出一个IllegalArgumentException异常。

Bitmap Field

一个BitmapField包含了位图。当使用Graphics对象绘图时使用BitmapField。为了修改一个field的内容,调用BitmapField的绘图方法。为得到更多信息,参看73页的使用graphics对象绘图”.

Bitmap myBitmap = Bitmap.getPredefinedBitmap(Bitmap.INFORMATION);

BitmapField myBitmapField = new BitmapField(myBitmap.getPredefinedBitmap(myBitmap));

 ...

mainScreen.add(myBitmapField);

4种预定义的位图:

  • Bitmap.INFORMATION
  • Bitmap.QUESTION
  • Bitmap.EXCLAMATION
  • Bitmap.HOURGLASS

 为了使用原始的.gif.png作为位图,调用getBitmapResource().

注:一个二进制资源的大小,如一个.png文件,不能超过63000字节。

private static final Bitmap myBitmap =

    Bitmap.getBitmapResource("customBitmap.gif");

...

BitmapField bitmapField = new BitmapField(myBitmap);

mainScreen.add(bitmapField);

Button Field

ButtonField包含了用户选择来完成操作的按钮。使用ButtonField可以创建超出菜单的扩展交互的界面。

ButtonField mySubmitButton = new ButtonField("Submit");

ButtonField myResetButton = new ButtonField("Reset");

mainScreen.add(mySubmitButton);

mainScreen.add(myResetButton);

为了给button增加功能,扩展ButtonField并且覆写trackwheelClick()方法,以让它能完成一个操作来代替调用菜单。当用户点击button后为了接受消息,使用一个FieldChangeListener对象。为得到更多信息,参看78页的监听UI对象的改变”. 

Choice Field

Choice field类似于下拉列表。这里有2choice field:包含整数的和包含可以转化为字符串的对象。

你也可以显示一组选项作为check box或者radio button。为了得到更多信息,参看45页的option field

为了从ChoiceField里选择一个值,用户可以完成下面的操作:

  • 点击field,并且按Space键。
  • 按住Alt键,滚动滑轮。
  • 打开菜单,选择Change Option

描述

NumericChoiceField

NumericChoiceField是一个包含了一组数字的ChoiceFieldNumericChoiceField实例典型的用在较少的数字范围内(到20个之多)。

NumericChoiceField myNumericChoice =

    new NumericChoiceField( "Select a number: ",

           1, 20, 10);

mainScreen.add(myNumericChoice);

:对于大数量的数字,使用GaugeField,为得到更多信息,参看47页的“Gauge Field)。

ObjectChoiceField

ObjectChoiceField是一个包含了对象的ChoiceField。这个Field的所有对象必须实现Object.toStirng()以提供他们自己的字符串表现形式。

Option Field

OptionField允许用户从列表种选择条目。为允许用户从选择列表中选择多个条目,使用CheckBoxField。为允许用户从选择列表中仅选择一个条目,使用RadioButtonField

描述

CheckBoxField

每个CheckBoxField是一个独立的对象,与其他的可选框无关。

CheckboxField myCheckbox =

    new CheckboxField("First checkbox", true);

CheckboxField myCheckbox2 =

    new CheckboxField("Second checkbox", false);

...

mainScreen.add(myCheckbox);

mainScreen.add(myCheckbox2);

RadioButtonField

多个RadioButtonField对象组合在一个RadioButtonGroup中,这样用户一次只能选择一个选项。

RadioButtonGroup rbGroup = new RadioButtonGroup();

RadioButtonField rbField = new RadioButtonField("First field");

RadioButtonField rbField2 = new RadioButtonField("Second field");

...

rbGroup.add(rbField);

rbGroup.add(rbField2);

...

mainScreen.add(rbField);

mainScreen.add(rbField2);

Date Field

在你的应用程序中,一个DateField显示当前的日期和时间。

类型

描述

DATE

显示年月日

DATE_TIME

显示年月日,时分秒

TIME

显示时分秒

当创建一个DateField时,调用System.currentTimeMillis()得到当前时间。

DateField dateField =

    new DateField("Date: ", System.currentTimeMillis(),

           DateField.DATE_TIME);

mainScreen.add(dateField);

Date Field缺省为可编辑的。为了创建一个用户不能编辑的Date Field,在其构造子中指定Field.READONLY参数。

将为可编辑的Date Field提供一个缺省的Change Options菜单项。

Edit Field

一个EditField允许用户在此Field里输入文本。AutoTextEditField, EditField, PasswordEditField 都扩展了BasicEditField.

注:net.rim.device.api.ui.component.TextField类,扩展了Field类,并且是抽象的。实例化它的子类,例如RichTextField EditField,就是创建一个显示文本或允许用户输入文本的UI Field

你可以应用下面的过滤项(filter)到Edit Field中。

过滤项

描述

DEFAULT_MAXCHARS

限制field中字符的个数。对于Edit Field,字符的最大个数缺省为15

FILTER_DEFAULT

这个是缺省的文本输入过滤项。当构造子需要此过滤项,但是你又不想应用任何特定的过滤项时,那么使用它。

FILTER_EMAIL

仅允许有效的internet消息地址字符(例如,用户可以仅输入一个@标记).它会自动将文本格式化为internet消息地址格式(例如,当用户第一次按Space键时,一个@符号会出现,用户接着按Space键,每次.’s也随之出现。

FILTER_HEXADECIMAL

仅允许数字和AF的字母。

FILTER_INTEGER

仅允许数字和负号“-“。

FILTER_LOWERCASE

将字符转化为小写

FILTER_NUMERIC

仅允许输入数字

FILTER_PHONE

仅允许输入有效电话号码字符,数字,连接号(-),加号和减号,左括号和右括号,以及”x”.

FILTER_PIN_ADDRESS

仅接受在PIN地址上输入的有效字符。

FILTER_UPPERCASE

将字母转化为大写。

FILTER_URL

仅允许有效的URL字符。它也自动格式化field。(当用户按Space键时它将插入一个节点).

JUMP_FOCUS_AT_END

改变field的行为,以致当field获得焦点,并且用户试图滚动时,焦点移动到field的末端(代替移动到下一个field)。

NO_NEWLINE

忽略文本中的换行和回车,例如用户从其他地方拷贝和粘贴的文本。

 

描述

RichTextField

RichTextField类创建一个只读的Field,它可以体格式化为各种不同的字体以及样式,RichTextField虽然不可以编辑,但是可以获取焦点。

mainScreen.add(new RichTextField("RichTextField"));

BasicEditField

BasicEditFieldEditField, PasswordEditField的基类。

BasicEditField是一个可编辑的文本Field,它没有缺省的格式,但是却可以接受过滤。

BasicEditField bf =

    new BasicEditField(

"BasicEditField: ",

       "", 10,           EditField.FILTER_UPPERCASE);

mainScreen.add(bf);

EditField

EditField是一个可以编辑的文本Field,它扩展了BasicEditFieldEditField允许用户当问特殊的字符。例如,用户按住A键,并且滚动滑轮来选择各种样式的A字符以及Æ字符。EditField类接受样式,但是有些样式会让EditField失去其功能(例如EditField.FILTER_PHONE.

mainScreen.add(new EditField("EditField: ", "", 10, EditField.FILTER_DEFAULT));

PasswordEditField

PasswordEditField扩展了BasicEditField,提供下面的功能:

l         让用户输入显示为星号(*)。

l         自动文本(AutoText)(或其他自动格式化)不会应用。

l         剪切或拷贝不支持

 

下面的样例使用了一个构造子,允许你为PasswordEditField提供了一个缺省的初始化值。
mainScreen.add(new PasswordEditField("PasswordEditField: ", ""));

AutoTextEditField

AutoTextEditField应用了AutoText引擎指定的格式。在本Field中输入的任何文本都会根据BlackBerry设备上的AutoText数据库说明来进行格式化。

某些过滤让一些AutoText输入没有效果。例如,FILTTER_LOWERCASE render一个包含大写无效的AutoText输入。

mainScreen.add(new AutoTextEditField("AutoTextEditField: ", ""));

Gauge Field

Gauge允许你创建数值的可视表现。GaugeField显示一个进度条或允许用户选择数字。你可以使用一个Label作为它的前缀,并显示gauge的当前值。例如,组合一个GaugeField和一个NumericChoiceField来创建一个用户制作的数字选择的图形化表现。

为了创建一个交互的GaugeField,使用Field.FOCUSABLEField.EDITABLE样式实例化field

GaugeField staticGauge = new GaugeField("1: ", 1, 100, 20, GaugeField.NO_TEXT);

GaugeField percentGauge = new GaugeField("Percent: ", 1, 100, 29, GaugeField.PERCENT)

GaugeField interactiveGauge = new GaugeField("Gauge: ", 1, 100, 60, Field.FOCUSABLE | Field.EDITABLE);

...

mainScreen.add(staticGauge);

mainScreen.add(percentGauge);

mainScreen.add(interactiveGauge);

Label(标签)和Separator(分隔)Field

一个LabelField允许你增加文本标签到屏幕中。LabelField是可读的。缺省的,它不能获得焦点。大部分应用程序在它们的第一个屏幕上使用LabelField来显示一个静态的标题。

一个SeparatorField是一个静态的水平线,它跨越屏幕的宽度。使用SeparatorField将屏幕上的相关内容和菜单分组。

MainScreen缺省的在标题后显示一个分割线。

LabelField title = new LabelField(

       "UI Component Sample",

       LabelField.ELLIPSIS));

mainScreen.setTitle(title)

List Field

List允许你创建子项的目录,通过此用户可以滚动并选择单个或多个条目。BlackBerry地址簿就是List对象的一个例子。

你不可以直接将内容加入到field条目中。你的ListFieldListFieldCallbackTreeFieldTreeFieldCallback的实现会绘图field

描述

ListField

ListField包含了数行可选条目。为了显示ListField的内容,为了列表设置ListFieldCallback。为了得到更多信息,参看66页的“创建一个回调对象”.

String fieldOne = new String("Mark Guo");

String fieldTwo = new String("Amy Krul");

...

ListField myList = new ListField();

ListCallback myCallback = new ListCallback();

myList.setCallback(myCallback);

myCallback.add(myList, fieldOne);

myCallback.add(myList, fieldTwo);

...

mainScreen.add(myList);

 

:为了使用户选择列表中的多个条目,指定ListField作为MULTI_SELECT.ListFieldCallback.add()加入列表元素到向量中,并且调用List.insert()决定适当的位置。

ObjectListField

ObjectListField是一个包含以对象作为子项的list field。所有包含在list里的对象必须实现Object.toString(),以提供他们自己的字符串表现。在接口上,ObjectListField以和一个标准的ListField一样提供。

 

Tree Field

TreeField包含父节点和子节点,并且显示一个折叠夹或它们(例如文档或信息折叠夹)之间的树关系。所有节点都是缺省可见的。为了指明一个折叠夹是否可以折叠,调用TreeField 对象的setExpand()方法。

图标显示在包含有子节点的每个节点边上以明确节点是打开的还是折叠的。

String fieldOne = new String("Main folder");

...

TreeCallback myCallback = new TreeCallback();

TreeField myTree = new TreeField(myCallback, Field.FOCUSABLE);

int node1 = myTree.addChildNode(0, fieldOne);

int node2 = myTree.addChildNode(0, fieldTwo);

int node3 = myTree.addChildNode(node2, fieldThree);

int node4 = myTree.addChildNode(node3, fieldFour);

...

int node10 = myTree.addChildNode(node1, fieldTen);

myTree.setExpanded(node4, false);

...

mainScreen.add(myTree);

TreeFieldCallback的实现加入field到树中。为了获得更多关于回调的信息,参看66页的“创建一个回调对象”.

private class TreeCallback implements TreeFieldCallback {

    public void drawTreeItem(TreeField _tree, Graphics g,

           int node, int y, int width, int indent) {

       String text = (String)_tree.getCookie(node);

       g.drawText(text, indent, y);

    }

}

管理UI组件

管理布局

使用BlackBerry API布局管理器来安排屏幕上的组件。

下面四个类扩展了Manager类,以提供预定义的布局管理器:

  • VerticalFieldManager
  • HorizontalFieldManager
  • FlowFieldManager
  • DialogFieldManager

MainScreenFullScreen缺省的都使用了一个VerticalFieldManager。仅为这些类定义一个布局管理器实例提供了不同的布局。

:为了创建一个定制的布局管理器,请扩展Manager。为了得到更多信息,参看63页的“创建定制的布局管理器”。

为一个指定的Screen实例定义布局管理器,完成下面的操作:

  • 实例化合适的Manager子类。
  • 加入UI组件到布局管理器中。
  • 加入布局管理器到屏幕中。

VerticalFieldManager vfm = new VerticalFieldManager(Manager.VERTICAL_SCROLL);

vfm.add(bitmapField);

vfm.add(bitmapField2);

...

mainScreen.add(vfm)

Manager类定义了多个系统样式的常数,这些系统样式定义了如滚动和对齐的行为。当创建布局管理器时,使用这些样式作为参数。为得到更多信息,参看API参考net.rim.device.api.ui.Manager

垂直组织field

VerticalFieldManager垂直地组织field。所有field在一新地线(line)上开始。为了可以垂直滚动,提供Manager.VERTICAL_SCROLL参数。

VerticalFieldManager vfm = new VerticalFieldManager(Manager.VERTICAL_SCROLL);

vfManager.add(bitmapField);

vfManager.add(bitmapField2);

...

mainScreen.add(vfManager);

缺省地,BitmapField对象在VerticalFieldManager中是左对齐的。

水平组织field

HorizonFieldManager水平组织field。为了可以水平滚动,提供Manager.HORIZONTAL_SCROLL样式。如果没有包含HORIZONTAL_SCROLL参数,field水平排列他们自己,可能会超出屏幕宽度,但是用户不能滚动到超出屏幕右边的内容。

BlackBerry设备没有显示水平滚动指示器或滚动条。

HorizontalFieldManager hfm = new HorizontalFieldManager(Manager.HORIZONTAL_SCROLL);

水平垂直组织Field

FlowFieldManager先水平组织field,然后再垂直组织。先水平组织Field,直到没有足够空间放另外一个field,然后管理器在下一行上水平的安排它们。首页屏幕(Home Screen)就是一个FlowFieldManager的例子。

FlowFieldManager flManager = new FlowFieldManager(Manager.FIELD_HCENTER);

指定一个PopupScreen的布局

DialogFieldManager指定了PopupScreen对象的布局。它管理了一个图标,一个消息,以及一列定制的field的布局。图标和消息相互靠近的出现在布局上方,定制的field出现在消息的下方。这个布局是PopupScreen对象的标准布局。为了创建定制的对话框,扩展DialogFieldManager

BitmapField bitmapField = new BitmapField(Bitmap.getBitmapResource("x.gif"));

RichTextField message = new RichTextField("Dialog manager message", Field.NON_FOCUSABLE);

LabelField dialogChoice = new LabelField("Choice one", Field.FOCUSABLE);

...

DialogFieldManager dialogManager = new DialogFieldManager();

dialogManager.setMessage(message);

dialogManager.setIcon(bitmapField);

dialogManager.addCustomField(dialogChoice);

管理UI交互

一个时间只有一个线程(通常是事件调配线程)可以得到UI的访问权。通过下列方式,背后(Background)线程也可从主事件处理或UI绘制代码的外部访问UI

  • 获取并保持事件锁。
  • 使用invokeLater()或invokeAndWait()在事件调配线程上运行。

获取并保持事件锁

当它处理一个消息时,事件调配者在事件线程上设置一个事件锁。在没有打断事件调配者处理的情况下,背后线程(也就是,非事件调配线程)在短事件内通过获取这个锁可以访问UI

为了得到事件锁,调用Application.getEventLock()。和这个对象同步,序列化访问UI。在短期内保持这个锁,因为锁会暂停事件调配者。一个应用程序应该永远不要在EventLock对象上调用notify()wait()

class MyTimerTask extends TimerTask {

    public void run() {

       synchronized(Application.getEventLock()) {

           _label.setText("new text " + System.currentTimeMillis());

           }

    }

}

在事件调配线程上运行

如果保持事件锁不合适,创建一个实现Runnable接口的类。在事件调配者上通过下面的3种方法之一调用它的run()方法:

  • 调用invokeAndWait(Runnable),以致在事件调配线程上立即调用run()。这个调用会阻塞直到run()完成为止。
  • 调用invokeLater(Runnable),以致在所有等候的事件处理后,在事件调配线程上调用run()
  • 调用invokeLater(Runnablelongboolean)以致在某一指定时间后,事件调配线程上调用run()。在这里,在将Runnable加入到事件队列之前,时间指定了等待时间的长短。如果repeattrue,每隔time毫秒后,Runnable加入到事件队列中,

管理前台事件

系统调用Application.activate()将应用程序带到前台。

大多数的应用程序不需要重写activate()。应用程序应该完成应用程序构造子的任何初始化,包括任何必需的UiApplicaiton.pushScreen()调用.因为对同一个应用程序,activate()能够调用多次,因此在在这个方法中,应用程序不应该完成一次初始化。

当带到前台时,应用程序可以覆写activate()方法完成其他的附加处理。如果覆写了activate(),在方法的定义里调用super.activate(),以致应用程序能正确得重绘。

管理绘图区域

使用XYRect对象

Graphics对象代表了应用程序可用的整个绘图表面。为了界定这个区域,将它分为多个XYRect对象。XYRect在图形上下文(graphics context)的顶端创建一个矩形区域、一个XYRect对象有2XYPoint对象组成。第一个XYPoint对象代表了XYRect左上方的坐标,第二个XYPoint对象代表了右下方的坐标。每个XYPoint代表了一个由XY坐标构成的屏幕的坐标。

XYPoint topLeft = new XYPoint(10, 10);

XYPoint bottomRight = new XYPoint(50, 50);

XYRect rectangle = new XYRect(topLeft, bottomRight);

Rectangle对象将XYRect对象的上下文绘制区域界定为(1010)与(5050)之间的区域。

为了开始对XYRect对象进行绘图调用,调用pushContext()pushRegion();

当开始用pushContext()进行绘图调用时,指定区域原点不要调整绘图偏移(Drawing offset)。

graphics.pushContext(rectangle, 0, 0);

graphics.fillRect(10, 10, 30, 30);

graphics.drawRect(15, 15, 30, 30);

graphics.popContext();

当你首先调用pushRegion()来调用绘图方法时,区域源(Region Origin)需调整绘图偏移,左上方的XYPoint对象代表了区域源。所有绘图都通过这个数来偏移。

在下面的例子中,pushContext()XYRect对象的10个象素位放到右边,10个放在下方。

区域源调整了绘图偏移(XYPoint topLeft = new XYPoint(10, 10)).

graphics.pushRegion(rectangle);

graphics.fillRect(10, 10, 30, 30);

graphics.drawRect(15, 15, 30, 30);

graphics.popRegion();

旋转(Invert)个区域

旋转一个Graphics对象上的一个区域,它保留像素,只是转化像素值的位(也就是0变为11变为0)。大多数field使用旋转来表示焦点,尽管这样,你可以为定制的field创建你自己的焦点行为。

为了旋转Graphics对象的任何一个区域,提供坐标或者旋转一个指定的XYRect对象。指定Graphics对象的一个区域,并且压入栈中。在调用pushContext()pushRegion()后,提供Graphics对象的一个区域来旋转。

graphics.pushContext(rectangle);

graphics.invert(rectangle); // invert the entire XYRect object

graphics.popContext();

转化(Translate)一个区域

为了将一个Graphics上下文上的区域移动到另外一个地方。调用invoke()

XYRect rectangle = new XYRect(1, 1, 100, 100);

XYPoint newLocation = new XYPoint(20, 20);

rectangle.translate(newLocation);

XYRect将点(11)转化为(2020,)。转化后,XYRect的底部扩展了过去图形上下文的范围,并且重合了。

创建客户定制的UI组件

你仅能将定制的上下文菜单项和布局增加到一个定制的field中。

创建定制的field

为覆写field的缺省行为,创建一个定制的field

:不要使用Screen对象来输入文本。Screen对象没有明确的实现此功能,它需要复杂的输入方法,例如国际化的键盘和7100系列的设备。为了实现不同输入方法的无缝集成,扩展Field或者其任一子类。参看53页“创建定制的域”得到更多信息。

DrawStyle接口的实现允许在定制的field上绘制样式。为获得更多信息,参看73页的“创建一个与标准BlackBerry UI一致的接口”。

客户定制的field应该实现所有相关的系统样式。例如,USE_ALL_WIDTHUSE_ALL_HEIGHT适用于许多field

扩展Field

扩展Field类和任一其子类,指定定制Field的特征。

public class CustomButtonField

  extends Field implements DrawStyle {

    public static final int RECTANGLE = 1;

    public static final int TRIANGLE = 2;

    public static final int OCTAGON = 3;

    private String _label;

    private int _shape;

    private Font _font;

    private int _labelHeight;

    private int _labelWidth;

}

定义按钮的标签,图形,以及样式

你的构造子的实现定义了按钮的标签,图形,以及样式。

public CustomButtonField(String label) {

    this(label, RECTANGLE, 0);

}

 

public CustomButtonField(String label, int shape) {

    this(label, shape, 0);

    }

 

public CustomButtonField(String label, long style) {

    this(label, RECTANGLE, style);

}

 

public CustomButtonField(String label, int shape, long style) {

    super(style);

    _label = label;

    _shape = shape;

    _font = getFont();

    _labelHeight = _font.getHeight();

    _labelWidth = font.getWidth();

}

指定field中对象的安排

任何扩展Field的类必须实现layout().Field管理器调用了layout()方法来决定field应该如何根据可用的控件安排它的内容。

protected void layout(int width, int height) {

     _font = getFont();

     _labelHeight = _font.getHeight();

     _labelWidth = _font.getAdvance(_label);

     width = Math.min( width, getPreferredWidth() );

     height = Math.min( height, getPreferredHeight() );

     setExtent( width, height );


 

定义需要的宽度

注:在大多数情况下,通过覆写getPreferredWidth(),确保合适的布局出现在定制的布局管理器里。

getPreferredWidth()的实现计算出定制Field的宽度,这个定制Field是基于标签Field的相对尺寸的。使用相对尺寸来确保标签不会超出标签的尺寸。

public int getPreferredWidth() {

    switch(_shape) {

    case TRIANGLE:

       if (_labelWidth < _labelHeight) {

           return _labelHeight << 2;

           }

       else

       {

           return _labelWidth << 1;

       }

    case OCTAGON:

       if (_labelWidth < _labelHeight) {

           return _labelHeight + 4;

           }

       else {

           return _labelWidth + 8;

       }

    case RECTANGLE:

    default:

       return _labelWidth + 8;

    }

}

 

定义需要的高度

:在大多数情况下,通过覆写getPreferredHeight(),确保合适的布局出现在定制的布局管理器里。

 

getPreferredHeight()的实现计算出定制Field的高度,这个定制Field是基于标签Field的相对尺寸的。它确保了标签不会超出field的尺寸。

public int getPreferredHeight()

{

    switch(_shape){

     case TRIANGLE:

       if (_labelWidth < _labelHeight){

          return _labelHeight << 1;

            }

           else {

             return _labelWidth;

             }

       case RECTANGLE:

           return _labelHeight + 4;

      case OCTAGON:

           return getPreferredWidth();

           }

       return 0;

    }

 

定义定制field的外观

paint()的实现定义了BlackBerry设备屏幕上的定制Field的外观,不管什么时候Field的域标记为无效,Field管理器都调用paint()来重绘Field

技巧:验证paint()是否是有效率的,因为不管什么时候field发生变化,UI框架调用paint()方法。对于大数量的field,使用Graphics.getClippingRect()并在可见的区域里绘图来保存绘制时间。

protected void paint(Graphics graphics) {

    int textX, textY, textWidth;

    int w = getWidth();

    switch(_shape) {

        case TRIANGLE:

           int h = (w>>1);

           int m = (w>>1)-1;

           graphics.drawLine(0, h-1, m, 0);

           graphics.drawLine(m, 0, w-1, h-1);

           graphics.drawLine(0, h-1, w-1, h-1);

           textWidth = Math.min(_labelWidth,h);

           textX = (w - textWidth) >> 1;

           textY = h >> 1;         

           break;

       case OCTAGON:

           int x = 5*w/17;

           int x2 = w-x-1;

           int x3 = w-1;       

           raphics.drawLine(0, x, 0, x2);

           graphics.drawLine(x3, x, x3, x2);

           graphics.drawLine(x, 0, x2, 0);

           graphics.drawLine(x, x3, x2, x3);

           graphics.drawLine(0, x, x, 0);

           graphics.drawLine(0, x2, x, x3);

           graphics.drawLine(x2, x3, x3, x2);

           textWidth = Math.min(_labelWidth, w - 6);

           textX = (w-textWidth) >> 1;

           textY = (w-_labelHeight) >> 1;

           break;

       case RECTANGLE:

       default:

           graphics.drawRect(0, 0, w, getHeight());

           textX = 4;

           textY = 2;

           textWidth = w - 6;

           break;

       }

    graphics.drawText(_label, textX, textY, (int)( getStyle() & DrawStyle.ELLIPSIS | DrawStyle.HALIGN_MASK ), textWidth );

    }

 

处理焦点事件

为了支持焦点事件,使用Field.FOCUSABLE样式以及实现Field.moveFocus().如果你想你的Field接收焦点,覆写Field.isFocusable()返回true

Field获得焦点时,UI框架调用onFocus(),当Field失去焦点时,调用unFocus().如果你的field对于这些事件需要特定的行为,覆写这些方法。框架调用moveFocus()来处理field的焦点移动事件。它对应trackwheelRoll事件,覆写drawFocus()。 

实现setget方法

Fieldgetset方法的实现,增加了Field的能力。

:所有getset方法应该在field加入到一个Screen的前后工作。例如,如果现在屏幕上的field合适的调用了invalidate()updateLayout()setLabel(),应该使用一个新值来修改其显示。

public String getLabel() {

    return _label;

    }

public int getShape() {

    return _shape;

    }

public void setLabel(String label) {

    _label = label;

    _labelWidth = _font.getAdvance(_label);

    updateLayout();

    }

public void setShape(int shape) {

    _shape = shape;

    updateLayout();

    }

 

代码实例

CustomButtonField.java创建了具有多个图形的button field


实例: CustomButtonField.java

/**

* CustomButtonField.java

* Copyright (C) 2001-2005 Research In Motion Limited. All rights reserved.

*/

 

package com.rim.samples.docs.custombuttons;

import net.rim.device.api.ui.*;

import net.rim.device.api.system.*;

 

/**

* CustomButtonField is a class that creates button fields of various

* shapes. This sample demonstrates how to create custom UI fields.

*/

public class CustomButtonField extends Field implements DrawStyle {

    public static final int RECTANGLE = 1;

    public static final int TRIANGLE = 2;

    public static final int OCTAGON = 3;

    private String _label;

    private int _shape;

    private Font _font;

    private int _labelHeight;

    private int _labelWidth;

   

    /* Constructs a button with specified label, and the default style and shape. */

    public CustomButtonField(String label) {

       this(label, RECTANGLE, 0);

    }

   

    /* Constructs a button with specified label and shape, and the default style. */

    public CustomButtonField(String label, int shape) {

       this(label, shape, 0);

       }

   

    /* Constructs a button with specified label and style, and the default shape. */

    public CustomButtonField(String label, long style) {

       this(label, RECTANGLE, style);

       }

   

    /* Constructs a button with specified label, shape, and style */

    public CustomButtonField(String label, int shape, long style) {

       super(style);

       _label = label;

       _shape = shape;

       _font = getFont();

       _labelHeight = _font.getHeight();

       _labelWidth = _font.getAdvance(_label);

       }

   

   

    /* Method that draws the focus indicator for this button and

     * * inverts the inside region of the shape.

     *

     * */

    protected void drawFocus(Graphics graphics, boolean on) {

       switch(_shape) {

       case TRIANGLE:

           int w = getWidth();

           int h = w >> 1;

           for (int i=h-1; i>=2; --i) {

              graphics.invert(i, h - i, w - (i << 1), 1);

              }

           break;

       case RECTANGLE:

              graphics.invert(1, 1, getWidth() - 2, getHeight() - 2);

              break;

       case OCTAGON:

           int x3 = getWidth();

           int x = 5 * x3 / 17;

           int x2 = x3 - x;

           x3 = x3 - 1;

           x2 = x2 - 1;

           graphics.invert(1, x, getWidth() - 2, x2 - x + 1);

           for (int i=1; i<x; ++i) {

              graphics.invert(1+i, x-i,getWidth() - ((i+1)<<1), 1);

              graphics.invert(1+i, x2+i,getWidth() - ((i+1)<<1), 1);

           }

           break;

           }

       }

   

    /* Returns the label. */

    public String getLabel() {

       return _label;

       }

   

    /* Returns the shape. */

    public int getShape() {

       return _shape;

       }

   

    /* Sets the label. */

    public void setLabel(String label) {

       _label = label;

       _labelWidth = _font.getAdvance(_label);

       updateLayout();

       }

   

    /* Sets the shape. */

    public void setShape(int shape) {

       _shape = shape;

       updateLayout();

       }

   

    /* Retrieves the preferred width of the button. */

    public int getPreferredWidth() {

       switch(_shape) {

       case TRIANGLE:

           if (_labelWidth < _labelHeight) {

              return _labelHeight << 2;

              }

           else {

              return _labelWidth << 1;

              }

       case OCTAGON:

           if (_labelWidth < _labelHeight) {

              return _labelHeight + 4;

              }

           else {

              return _labelWidth + 8;

              }

      

       case RECTANGLE: default:

           return _labelWidth + 8;

       }

       }

   

   

    /* Retrieves the preferred height of the button. */

    public int getPreferredHeight() {

       switch(_shape) {

       case TRIANGLE:

           if (_labelWidth < _labelHeight) {

              return _labelHeight << 1;

              }

           else {

              return _labelWidth;

              }

      

       case RECTANGLE:

           return _labelHeight + 4;

       case OCTAGON:

           return getPreferredWidth();

           }

       return 0;

       }

   

    /* Lays out this button’s contents.

     * This field’s manager invokes this method during the layout

     * process to instruct this field to arrange its contents, given an

     *  amount of available space.

    **/

    protected void layout(int width, int height) {

       //     Update the cached font in case it has been changed.

       _font = getFont();

       _labelHeight = _font.getHeight();

       _labelWidth = _font.getAdvance(_label);

//     Calculate width.

       width = Math.min( width, getPreferredWidth() );

//     Calculate height.

       height = Math.min( height, getPreferredHeight() );

//     Set dimensions.

       setExtent( width, height );

       }

       /*

       * Redraws this button. The field’s manager invokes this method during the

       * repainting process to instruct this field to repaint itself.

       */

       protected void paint(Graphics graphics) {

       int textX, textY, textWidth;

       int w = getWidth();

       switch(_shape) {

       case TRIANGLE:

           int h = (w>>1);

           int m = (w>>1)-1;

           graphics.drawLine(0, h-1, m, 0);

           graphics.drawLine(m, 0, w-1, h-1);

           graphics.drawLine(0, h-1, w-1, h-1);

           textWidth = Math.min(_labelWidth,h);

           textX = (w - textWidth) >> 1;

           textY = h >> 1;

           break;

       case OCTAGON:

           int x = 5*w/17;

           int x2 = w-x-1;

           int x3 = w-1;

           graphics.drawLine(0, x, 0, x2);

           graphics.drawLine(x3, x, x3, x2);

           graphics.drawLine(x, 0, x2, 0);

           graphics.drawLine(x, x3, x2, x3);

           graphics.drawLine(0, x, x, 0);

           graphics.drawLine(0, x2, x, x3);

           graphics.drawLine(x2, 0, x3, x);

           graphics.drawLine(x2, x3, x3, x2);

           textWidth = Math.min(_labelWidth, w - 6);

           textX = (w-textWidth) >> 1;

           textY = (w-_labelHeight) >> 1;

           break;

       case RECTANGLE: default:

           graphics.drawRect(0, 0, w, getHeight());

           textX = 4;

           textY = 2;

           textWidth = w - 6;

           break;

           }

       graphics.drawText(_label, textX, textY, (int)( getStyle() & DrawStyle.ELLIPSIS | DrawStyle.HALIGN_MASK ),textWidth );

       }

       }

 


创建定制的上下文菜单项

Field类里,创建定制的上下文菜单项。为得到更多关于实现的菜单的信息,参看39页的“显示屏幕”.

private MenuItem myContextMenuItemA = new MenuItem( _resources, MENUITEM_ONE, 200000, 10)

    {

       public void run() {

           onMyMenuItemA();

           }

    };

   

    private MenuItem myContextMenuItemB = new MenuItem( _resources, MENUITEM_ONE, 200000, 10)

    {

       public void run() {

           onMyMenuItemB();

       }

   };

 

 

提供一个上下文菜单

在主应用程序类里,覆写makeContextMenu()方法提供一个上下文菜单。

protected void makeContextMenu(ContextMenu contextMenu) {

    contextMenu.addItem(myContextMenuItemA);

    contextMenu.addItem(myContextMenuItemB);

   }

 

创建应用程序菜单

在主应用程序类里,覆写makeMenu()方法创建应用程序菜单,并且无论合十,当特定的field获取焦点时,更新上下文菜单。

protected void makeMenu(Menu menu) {

    Field focus = UiApplication.getUiApplication().getActiveScreen()

.getLeafFieldWithFocus();

    if (focus != null) {

       ContextMenu contextMenu = focus.getContextMenu();

       if (!contextMenu.isEmpty()) {

           menu.add(contextMenu);

           menu.addSeparator();

           }

       }

    }

 

代码实例


实例: ContextMenuSample.java

/**

* ContextMenuSample.java

* Copyright (C) 2001-2005 Research In Motion Limited. All rights reserved.

*/

package com.rim.samples.docs.contextmenus;

import net.rim.device.api.i18n.*;

import net.rim.device.api.ui.*;

import net.rim.device.api.ui.component.*;

import net.rim.device.api.ui.container.*;

import net.rim.device.api.system.*;

import com.rim.samples.docs.baseapp.*;

 

public class ContextMenuSample

extends BaseApp implements ContextMenuSampleResource {

    private MyContextField myContextField;

    private static ResourceBundle _resources

    = ResourceBundle.getBundle(

           ContextMenuSampleResource.BUNDLE_ID,

           ContextMenuSampleResource.BUNDLE_NAME);

   

    public static void main(String[] args) {

       ContextMenuSample app = new ContextMenuSample();

       app.enterEventDispatcher();

       }

   

    // Inner class to define a new field.

    private class MyContextField extends RichTextField {

       private MenuItem myContextMenuItemA = new MenuItem(

              _resources, MENUITEM_ONE, 200000, 10) {

           public void run() {

              onMyMenuItemA();

              }

           };

          

    private MenuItem myContextMenuItemB = new MenuItem(

           _resources, MENUITEM_TWO, 200000, 10) {

       public void run() {

           onMyMenuItemB();

           }

       };

      

    private void onMyMenuItemA() {

       // Perform an action when user selects menu item.

       }

   

    private void onMyMenuItemB() {

       // Perform an action when user selects menu item.

        }

   

    protected void makeContextMenu(ContextMenu contextMenu) {

       contextMenu.addItem(myContextMenuItemA);

       contextMenu.addItem(myContextMenuItemB);

       }

   

    MyContextField(String text) {

       super(text);

       }

    }

   

    protected void makeMenu(Menu menu) {

       super.makeMenu(menu, 0); // Implemented by BaseApp.

       }

   

    public ContextMenuSample() {

       MainScreen mainScreen = new MainScreen();

       MyContextField myContextField = new MyContextField(“Field label: “);

       mainScreen.add(myContextField);

       mainScreen.addKeyListener(this);

       mainScreen.addTrackwheelListener(this);

       pushScreen(mainScreen);

       }

   

    public void onExit() {

       // Perform action when application closes.

       }

}

创建定制的布局管理器

Manager对象管理UI组件的位置以及决定屏幕上的field如何安排。

创建一个定制的布局管理器

扩展Manager类或其任一子类

class DiagonalManager extends Manager {

    public DiagonalManager(long style){

    super(style);

    }

    ...

}

返回一个优先的Field宽度

覆写getPreferredWidth(),以致它能为管理器返回一个优先的Field宽度。

getPreferredWidth()的实现可以返回不同的值,取决于布局管理器的目的。例如,如果管理器扩展了HorizontalFieldManager,getPreferredWidth()返回所有field宽度的总和。如果扩展了VerticalFieldManagergetPreferredWidth()返回最宽field的宽度。

public int getPreferredWidth() {

    int width = 0;

    int numberOfFields = getFieldCount();

    for (int i=0; i<numberOfFields; ++i) {

       width += getField(i).getPreferredWidth();

       }

    return width;

    }

TextFieldManger使用了指派给他们的整个宽度。为组织2个或更多的水平上的对象,分别覆写它们各自的getPreferredWidth()方法。为了组织多个水平上的TextField,覆写layout().

 

返回一个优先Field高度

覆写getPreferredHeight(),以致它能为管理器返回一个优先的Field高度。

public int getPreferredHeight() {

    int height = 0;

    int numberOfFields = getFieldCount();

    for (int i=0; i<numberOfFields; ++i) {

       height += getField(i).getPreferredHeight();

    }

    return height;

    }

 

指定子Field如何安排

subLayout()方法指定了管理器如何在屏幕上组织field。它得到管理器中field的个数,然后为子field设置合适的位置以及布局。

 

layout()调用了subLayout()方法,subLayout()方法通过调用每个管理器包含的fieldsetPositionChild ()以及LayoutChild(),控制每个子field如何加到屏幕上。

protected void sublayout(int width, int height) {

    int x = 0;

    int y = 0;

    Field field;

    int numberOfFields = getFieldCount();

    for (int i=0; i<numberOfFields; ++i) {

       field = getField(i);

       layoutChild(field, width, height);

       setPositionChild(field, x, y);

       field.setPosition(x,y);

       x += field.getPreferredWidth();

       y += field.getPreferredHeight();

       }

    setExtent(width,height);

    }

:为设置field需要的大小,在subLayout()方法里调用setExtend()。如果你不调用setExtend(),则不会绘制每个field,并且也不抛出一个异常。

 

处理焦点

当用户滚动滑轮时,为了指定field该如何得到焦点,覆写nextFocus()方法。direction参数描述了焦点移动的方向(一般来说,当滑轮向下滚动,焦点向下并且向右方向。当滑轮向上滚动,焦点向上并且向左)

protected int nextFocus(int direction, boolean alt) {

    int index = this.getFieldWithFocusIndex();

    if(alt) {

       if(direction > 0) {

           //  action to perform if trackwheel is rolled up

           }

       else {

           //  action to perform if trackwheel is rolled down

          }

    }

    if (index == this.getFieldWithFocusIndex())

       return super.nextFocus(direction, alt);

    else

       return index;

    }

为了将焦点转移到下一个field,而以管理器的顺序,这个field不是下一个field,那么覆写nextFocus().例如,如果你想为你的管理器实现Page-upPage-down的功能,那么nextFocus()就有用了。

 

当可见区域改变时重绘field

缺省的,定制的管理器在不考虑剪辑区域下调用paint()重绘所有field。如果这导致了不必要的重绘,当可见区域改变时,仅subpaint()的实现重绘所有field

 

代码实例


: DiagonalManager.java

/**

* DiagonalManager.java

* Copyright (C) 2001-2005 Research In Motion Limited. All rights reserved.

*/

package com.rim.samples.docs.custommenu;

import net.rim.device.api.system.*;

import net.rim.device.api.ui.container.*;

import net.rim.device.api.ui.*;

import net.rim.device.api.ui.component.*;

 

class DiagonalManager extends Manager {

    public DiagonalManager(long style) {

       super(style);

       }

   

    public int getPreferredWidth() {

       int width = 0;

       int numberOfFields = getFieldCount();

       for (int i=0; i<numberOfFields; ++i) {

           width += getField(i).getPreferredWidth();

           }

       return width;

       }

   

    public int getPreferredHeight() {

       int height = 0;

       int numberOfFields = getFieldCount();

       for (int i=0; i<numberOfFields; ++i) {

           height += getField(i).getPreferredHeight();

           }

       return height;

       }

   

    protected void sublayout(int width, int height) {

       int x = 0;

       int y = 0;

       Field field;

       int numberOfFields = getFieldCount();

       for (int i=0; i<numberOfFields; ++i) {

           field = getField(i);

           layoutChild( field, width, height );

           setPositionChild(field, x, y);

           x += field.getPreferredWidth();

           y += field.getPreferredHeight();

           }

       setExtent(width,height);

       }

   

    protected int nextFocus(int direction, boolean alt) {

       int index = this.getFieldWithFocusIndex();

       if(alt)

       {

           if(direction > 0)

           {

              //  Action to perform if trackwheel is rolled up.

           }

           else

           {

              //  Action to perform if trackwheel is rolled down.

           }

       }

      

       if (index == this.getFieldWithFocusIndex())

          return super.nextFocus(direction, alt);

       else

          return index;

       }

    }

创建列表

一个ListField包含了多列可选项。为了使用户可以选择列表中多项,声明列表为MULTI_SELECT.

 

创建一个回调对象

ListFieldCallback对象为列表控制所有重绘任务。每次要求Field显示列表中的一个条目。必要的方法也会在回调对象中调用。

 

ListFieldCallback接口的实现创建了一个回调对象。系统调用这个接口的方法绘制列表的行,获得一个指定的列表元素,或决定列表的宽度。

private class ListCallback implements ListFieldCallback {

    // The listElements vector contain the entries in the list.

    private Vector listElements = new Vector();

    ...

}

 

允许Field重绘一行

drawListRow()的实现允许Field重绘一行。传递到drawListRow()的图形上下文代表整个列表。相应地,drawText()必须指明绘制哪一行。

public void drawListRow(ListField list, Graphics g, int index, int y, int w) {

    String text = (String)listElements.elementAt(index);

    g.drawText(text, 0, y, 0, w);

}

允许Field从列表中得到一个条目(Entry

get()的实现允许field从列表中得到一个条目。本方法返回一个包含在有明确索引行中的对象。

public Object get(ListField list, int index)

{

    return listElements.elementAt(index);

}

 

为列表返回一个优先的宽度

getPreferredWidth()的实现为列表返回一个优先的宽度。在下面的实现中,getPreferredWidth()返回整个屏幕的绘制宽度。

 

getPreferredWidth()的实现返回一个不同的值,这依赖field管理器的类型。例如,如果管理器扩展了HorizontalFieldManager,getPreferredWidth()返回所有field宽度的总和。如果扩展了VerticalFieldManagergetPreferredWidth()返回最宽field的宽度。

public int getPreferredWidth(ListField list) {

    return Graphics.getScreenWidth();

}

 

指派回调以及加入条目到列表中

创建列表对象,并且将