使用MIDP列表组件

作者:Eric Giguere

版本1.0

2001年6月22日

MIDP 将它的用户界面组件分为两组类:高层 API 和底层 API。使用高层 API 你可以使用一组平台独立的抽象类定义你的用户界面,并让
MIDP 实现确定用户界面的外观和操作。底层
API 是你获得完全的画图区域控制并处理原始输入事件。底层
API 在使用底层用户界面 API 中介绍。

本文讨论列表组件,高层 API 的一个组成部分。所有 MIDP 用户界面组件在 javax.microedition.lcdui 包中定义。

列表组件显示一组可选择的字符串。一组可选图案也可以和每个字符串替换使用。实现显示了在一些列表中的字符串—显示的准确格式并没有定义—并提供了一个途径,是用户能够从列表中的一个项目转换到另一个项目,根据需要选择或不选择某个项目。浏览和选择的处理流程是相互独立的。换句话说,用户可以浏览一个特定的项目,但是并不确定选择与不选择。这是指移动输入指针。

List类是Screen类的扩展,Screen类是高层 API 中所有顶层窗口的基本类。在同一时间段内,只有一个Screen是可见的。一个Screen有一个可选的标题。由于Screen类是Displayable类的扩展,命令对象也可以与List组件相关联。List类也实现Choice界面,它定义了一些方法,这些方法是List组件与高层API的其它组件所共有的:ChoiceGroup组件。

List组件支持三种类型的项目选择。第一种是List.EXCLUSIVE,是用户准确的从列表中选择一个项目。如果用户在选择一个项目时,另一个项目已经被选择了,先前的一个项目将被自动取消选择。从概念上看,列表就像一组Radio Buttons,实现实际上就是用这种方式显示的。第二种是List.MULTIPLE,是用户同时选择零个或多个项目。就好像列表是由一组Checkbox组成的。最后一种是List.IMPLICIT,象是一种排它方式,但是选择一个项目的动作同时也激发一个事件,就像用户激活了一个命令(下面详细介绍)。

List类定义两个构造器。两个构造器都使用屏幕的名称和一个列表方式(List.EXCLUSIVE,List.MULTIPLE,List.IMPLICIT之中的一个)。第二个构造器也带有一个字符串数组和一个图像数组,用以初始化List。尽管如此,初始化可以分别进行。例如,这是构造项目列表的一种方式:

List l = new List( "Choose fruit:", List.EXCLUSIVE );
l.append( "orange", null );
l.append( "apple", null );
l.append( "pear", null );

另一种方式是使用数组形式的构造器:

String[] fruitNames = { "orange", "apple", "pear" };
 
List l = new List( "Choose fruit:", List.EXCLUSIVE,
                   fruitNames, null );

注意:图像完全是可选项。即使你指定图像,实现也可能忽略它们。这样,无论什么时候一个图像或一组图像被请求时,传递Null比较安全。

在显示一个List组件之前,你会希望为它注册一个命令监听器:

List l = ....;
CommandListener listener = ....; // often "this"
 
l.setCommandListener( listener );

命令监听器是任何一个实现CommandListener接口的类的实例。通常MIDlet的Main类是一个监听器。同时只有一个监听器可以被注册。一个采用EXCLUSIVE或MULTIPLE方式的列表必须至少注册一个Command对象,否则没有事件会被发送到命令监听器。这是因为在用户操作列表时EXCLUSIVE和MULTIPLE方式不会激发任何事件。

一般情况下,为采用IMPLICIT方式的列表,象采用EXCLUSIVE和MULTIPLE方式的列表一样,添加命令是一种好主意。在用户从一个IMPLICIT列表种选择项目时,列表使用定义为List.SELECT_COMMAND的特殊命令对象通知它的命令监听器。这个对象在新的项目被选择的时候被作为第一个参数传递到commandAction方法。换句话说,你可以使用如下代码检查implicit选择:

public void commandAction( Command c, Displayable d ){
    if( c == List.SELECT_COMMAND ){
        // implicit selection...
    } else if( ..... ){
        ..... // etc. etc.
    }
}

请确认任何一个响应SELECT_COMMAND的操作对于用户来说是直观的。例如,显示一个电子邮件的列表的List组件可以通过显示选定的电子邮件信息的文本来响应SELECT_COMMAND。其它邮件管理操作是通过相应的注册的命令对象来访问的。

列表的内容,就是列表显示的文本和图像,可以在任何时候修改。列表为此定义了append,delete,insert和set方法。一个常见的需求,例如,删除列表的一个内容。你可以这样容易的实现:

public static void deleteListContents( List l ){
    int n = l.size();
    while( n-- > 0 ){
        l.delete( n );
    }
}

size方法返回目前在列表中存储的项目的数量。

如果你发现你重复的进行这种操作,你可能希望为你的特性为列表增加参数,就像这样:

public class ExtendedList extends List {
    public ExtendedList( String title, int mode ){
        super( title, mode );
    }
    
    public ExtendedList( String title, int mode, 
                         String[] itemText, Image[] 
                                         itemImages ){
        super( title, mode, itemText, itemImages );
    }
    
    public void deleteAll(){
        int n = size();
        while( n-- > 0 ){
            delete( n );
        }
    }
} 

在一个列表被构造后,你可以在任何时候获得选择的项目的索引。在EXCLUSIVE或IMPLICIT方式,使用getSelectedIndex,它返回选择的项目的索引(从0开始):

    List l = ....; // some list
    int  which = l.getSelectedIndex();

使用MULTIPLE方式的列表的getSelectedIndex返回-1。因为在任何的指定时间可以返回多于一个的项目。使用getSelectedFlags将每个选择的项目的状态返回到你指定的数组种:

    List      l = ....; // some list
    boolean[] selected = new boolean[ l.size() ];
    
    l.getSelectedFlags( selected );
    for( int i = 0; i < selected.length; ++i ){
        if( selected[i] ){
            system.out.println( "Item " + i + " is 
                                        selected!" );
        }
    }

你也可以随时检查每一个选定的项目的状态,是通过调用isSelected来实现。你可以通过调用setSelectedFlags或setSelectedIndex来设置它的状态。例如,如何在MULTIPLE方式列表种所定选择的项目:

public void toggleItems( List l ){
    boolean[] selected = new boolean[ l.size() ];
    l.getSelectedFlags( selected );
    for( int i = 0; i < selected.length; ++i ){
        selected[i] = !selected[i];
    }
    l.setselectedflags( selected );
}

另一个锁定选择的项目的方法是分别设置每一个项目为选择状态:

public void toggleItems( List l ){
    int n = l.size();
    for( int i = 0; i < n; ++i ){
        l.setselectedindex( i, !l.isselected( i ) );
    }
}

后一种方法将导致列表的刷新,无论怎样,它可以避免调用setSelectedFlags。

列表中的每一个项目可以有一个关联的图像。一个图像是Image类的一个实例,通常通过Image.createImage得到,并在将图像在MIDlet包的JAR文件中的路径传递给它。例如:

Image checked = null;
Image unchecked = null;
 
try {
    checked = Image.createImage( "/images/check.png" );
    unchecked = Image.createImage( 
                            "/images/unchecked.png" );
}
catch( java.io.IOException e ){
}

你也可以通过使用Image类的offscreen缓存器功能来动态创建图像。无论怎样,列表或其它高层用户界面组件中使用的图像必须是不可变的,就是说,它是一个不能修改的图像。更多关于如何动态生成图像的信息,参见Image类文档。

一旦你有了一个或多个图像,你可以通过构造器或作为参数传递个append,insert或set方法,来把它分配给列表的项目。你在构造器中分配图像是传递一个与定义的项目数组同等长度的图像数去给构造器。这里是一个通过append方法分配图像的例子:

List l = ....; // some list
 
try {
    l.append( "orange", Image.createImage( 
                          "/orange.png" ) );
    l.append( "apple", Image.createImage( 
                           "/apple.png" ) );
    l.append( "pear", Image.createImage( 
                            "/pear.png" ) );
}
catch( IOException e ){
}

尽量使用小的图像,不要超过10-16 pixels。采用同样大小的图像,以确保文本页面的正确。而且不要依赖于图像,确认项目的文本即使在没有图像的情况下,同样是容易理解的。

让我们以一个简单的使用List组件的例子来结束这个tip。

import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
 
public class ListDemo extends MIDlet {
 
    private Display              display;
    private int                  mode = List.IMPLICIT;
 
    private Command exitCommand = new Command( "Exit",
                                   Command.SCREEN, 2 );
    private Command selectCommand = new Command( "Select",
                                       Command.OK, 1 );
    private Command nextCommand = new Command( "Next",
                                   Command.SCREEN, 2 );
    public ListDemo(){
    }
 
    protected void destroyApp( boolean unconditional )
                   throws MIDletStateChangeException {
        exitMIDlet();
    }
 
    protected void pauseApp(){
    }
 
    protected void startApp() throws 
    MIDletStateChangeException {
        if( display == null ){ // first time called...
            initMIDlet();
        }
    }
 
    private void initMIDlet(){
        display = Display.getDisplay( this );
        display.setCurrent( new SampleList( mode ) );
    }
 
    public void exitMIDlet(){
        notifyDestroyed();
    }
 
    public static final String[] items = {
        "First", "Second", "Third", "Fourth"
    };
 
    class SampleList extends List implements 
                           CommandListener {
 
        private int mode;
 
        SampleList( int mode ){
            super( "", mode, items, null );
            addCommand( exitCommand );
            addCommand( selectCommand );
            addCommand( nextCommand );
            setCommandListener( this );
 
            switch( mode ){
                case IMPLICIT:
                    setTitle( "Implicit" );
                    break;
                case EXCLUSIVE:
                    setTitle( "Exclusive" );
                    break;
                case MULTIPLE:
                    setTitle( "Multiple" );
                    break;
            }
 
            this.mode = mode;
        }
 
        public void commandAction( Command c, 
                             Displayable d ){
            if( c == exitCommand ){
                exitMIDlet();
            } else if( c == selectCommand ){
                showSelection( false );
            } else if( c == SELECT_COMMAND ){
                showSelection( true );
            } else if( c == nextCommand ){
                if( mode == List.IMPLICIT ){
                    mode = List.EXCLUSIVE;
                } else if( mode == List.EXCLUSIVE ){
                    mode = List.MULTIPLE;
                } else {
                    mode = List.IMPLICIT;
                }
    
                display.setCurrent( new SampleList( 
                                             mode ) );
            }
        }
 
        private void showSelection( boolean implicit ){
            Alert alert = new Alert( 
                       implicit ? "Implicit Selection"
                               : "Explicit Selection" );
            StringBuffer buf = new StringBuffer();
 
            if( mode == MULTIPLE ){
                boolean[] selected = new boolean[ size() ];
                getSelectedFlags( selected );
 
                for( int i = 0; i < selected.length; ++i ){
                    if( selected[i] ){
                        if( buf.length() == 0 ){
                            buf.append( 
                             "You selected: " );
                        } else {
                            buf.append( ", " );
                        }
 
                        buf.append( getstring( i ) );
                    }
                }
 
                if( buf.length() == 0 ){
                    buf.append( "No items are 
                                 selected." );
                }
            } else {
                buf.append( "You selected " );
                buf.append( getstring( 
                       getselectedindex() ) );
            }
 
            alert.setstring( buf.tostring() );
            alert.settimeout( alert.forever );
 
            display.setcurrent( alert, 
                       display.getcurrent() );
        }
    }
}