返回列表 回复 发帖
所需阅读权限 1

[分享]Brew&J2ME:在差别中联合(3)

[UploadFile=7_30.zip.info][这个帖子最后由cnangel在 2004/05/26 12:39pm 第 3 次编辑]

作者:Radu Braniste
这是本系列的第二篇文章,主要面向那些更习惯使用J2ME的手机开发者,或者那些对更轻便和更有效的代码生成感兴趣的BREW开发者。在上一篇文章中《BREW&J2ME:在差别中联合(一)》《BREW&J2ME:在差别中联合(二)》,我们给出了一个主要受Java GUI模型启示的基本框架,它减轻了开发者采用BREW编写高级别接口代码的难度。这部分,我们将扩展和重分解这个框架,以便正确处理现实生活中的问题。
目录

案例
其他说明
包装
下载样品程序

我们以多控制屏幕这个基本范例为例,来详细讨论框架的内部机制。

案例

我们直接以双屏的应用程序为例:
  1. class BJApp : public IMidlet
  2. {
  3.   typedef ListImpl<BJApp> List;
  4.   typedef DateCtlImpl<BJApp> DateCtl;
  5.   typedef List Command;
  6. private:
  7.   void BuildDateCtl(int year)
  8.   {
  9.     DateCtl* l =
  10.        (DateCtl*) getRegistered("DATE_CTL");
  11.     if (!l)
  12.     {
  13.       l = DateCtl::createDateCtl("DATE_CTL",
  14.                                  this);
  15.       if (!l) return ;
  16.       Command* c = BuildDateCtlCommand(1);
  17.       if (!c) return ;
  18.       l->setScreen(8,20,110,60);
  19.       l->installEventHandler(dateCtlHdl);
  20.       l->installEventHandler(dateCtlHdl,
  21.                              TAB);
  22.       l->setTitle("Select a Date");
  23.       l->addDisplayable(c);
  24.       l->addDisplayable(l);
  25.     }
  26.     l->setDate(year,12,31);
  27.     l->setCurrent();
  28.   }
  29.   bool dateCtlHdl( DateCtl* l)
  30.   {
  31.     l->setCurrentAt(0);
  32.     return true;
  33.   }
  34.   Command* BuildDateCtlCommand(int ix)
  35.   {
  36.     Command* c =
  37.       (Command*) getRegistered("DATE_CTL_COMMAND");
  38.     if (c)
  39.       return c;
  40.     c = Command::createCommand("DATE_CTL_COMMAND",
  41.                                this);
  42.     if (!c) return 0;
  43.     c->installEventHandler(dateCtlCmdHdl);
  44.     c->installEventHandler(dateCtlCmdTabHdl,
  45.                               TAB);
  46.     WString a[] = {"Nowhere","Back"};
  47.     c->append(a,SIZE_OF(a));
  48.     c->setSelection(ix);
  49.     return c;
  50.   }
  51.   bool dateCtlCmdTabHdl( Command* l)
  52.   {
  53.     setActive("DATE_CTL");
  54.     return true;
  55.   }
  56.   bool dateCtlCmdHdl( Command* l)
  57.   {
  58.     int pos = l->getSelection();
  59.     if (pos == INDEX_OUT_OF_BOUNDS)
  60.       return false;
  61.     if (pos == 1)
  62.     {
  63.       Command* c =
  64.         (Command*)getRegistered("DATE_COMMAND");
  65.       if (!c) return false;
  66.       c->setSelection(0);
  67.       BuildInputList(0, "INPUT_LIST_0");
  68.     }
  69.     return true;
  70.   }
  71.   void BuildInputList(int focus,
  72.                       const String& lst)
  73.   {
  74.     List* l = (List*) getRegistered(lst);
  75.     if (!l)
  76.     {
  77.       l = List::createList(lst, this);
  78.       if (!l) return;
  79.       Command* c = BuildDateCommand(0);
  80.       if (!c) return;
  81.       l->installEventHandler(inputListHdl);
  82.       l->installEventHandler(inputListTabHdl,
  83.                              TAB);
  84.       l->setTitle("Start");
  85.       l->setFullScreen(120);
  86.       l->addDisplayable(c);
  87.       l->addDisplayable(l);
  88.     }
  89.     l->setCurrentAt(focus);
  90.   }
  91.   bool inputListHdl( List* l)
  92.   {
  93.     int pos = l->getSelection();
  94.     if (pos == INDEX_OUT_OF_BOUNDS)
  95.       return false;
  96.     Command* c =
  97.       (Command*)getRegistered("DATE_COMMAND");
  98.     if (!c)
  99.       return false;
  100.     if (c->getSelection() == 0)
  101.       BuildDateCtl(2003);
  102.     else if (c->getSelection() == 1)
  103.       BuildDateCtl(2004);
  104.     else
  105.       BuildDateCtl(9999);
  106.     return true;
  107.   }
  108.   bool inputListTabHdl( List* l)
  109.   {
  110.     l->setCurrentAt(0);
  111.     return true;
  112.   }
  113.   Command* BuildDateCommand(int ix)
  114.   {
  115.     Command* c =
  116.        (Command*) getRegistered("DATE_COMMAND");
  117.     if (c)
  118.       return c;
  119.     c = Command::createCommand("DATE_COMMAND",
  120.                                this);
  121.     if (!c) return 0;
  122.     c->installEventHandler(dateCmdTabHdl);
  123.     c->installEventHandler(dateCmdTabHdl,
  124.                            TAB);
  125.     WString a[] = {"2003","2004" };
  126.     c->append(a,SIZE_OF(a));
  127.     c->setSelection(ix);
  128.     return c;
  129.   }
  130.   bool dateCmdTabHdl( Command* l)
  131.   {
  132.     int pos = l->getSelection();
  133.     return dateManipulation(pos);
  134.   }
  135.   bool dateManipulation(int pos)
  136.   {
  137.     switch (pos)
  138.     {
  139.       case 0:
  140.       {
  141.         String s("INPUT_LIST_0");
  142.         List* k = (List*) getRegistered(s);
  143.         if (k && !k->size())
  144.         {
  145.           WString a[] = {"Conference","DevDay"};
  146.           k->append(a,SIZE_OF(a));
  147.         }
  148.         BuildInputList(1,s);
  149.         break;
  150.       }
  151.       case 1:
  152.       {
  153.         String s("INPUT_LIST_1");
  154.         BuildInputList(1,s);
  155.         List* k = (List*) getRegistered(s);
  156.         if (k && !k->size())
  157.         {
  158.           WString a[] = {"Training","Vacation"};
  159.           k->append(a,SIZE_OF(a));
  160.         }
  161.         BuildInputList(1,s);
  162.         break;
  163.       }
  164.       default:
  165.         return false;
  166.     }
  167.     return true;
  168.   }
  169. public:
  170.   virtual bool onStart()
  171.   {
  172.     setColor(CLR_SYS_ITEM_SEL , 0,0, 255);
  173.     setColor(CLR_USER_TEXT , 0,0, 255);
  174.     BuildInputList(0, "INPUT_LIST_0");
  175.     return true;
  176.   }
  177. };
复制代码
本例中的所有小器件都登记为EVT_CTL_TAB 和 EVT_COMMAND事件,并可作为单独的器件进行登记和按名检索。所有的UI逻辑可以方便的在事件处理器内部实现。

应用从onStart()内开始,它首先创建一个列表(INPUT_LIST_0) 和一个命令 (DATE_COMMAND)。

INPUT_LIST_0 两个小器件都有。EVT_CTL_TAB 切换焦点,EVT_COMMAND实现某些与数据相关的逻辑并通过BuildDateCtl将屏幕切换到DATE_CTL 。

DATE_COMMAND 通过同一个处理器(dateCmdTabHdl)处理这两个事件。在选择的基础上,创建了一个新的列表(INPUT_LIST_1),它与DATE_COMMAND 和给定焦点,或者可移回到INPUT_LIST_0的焦点,相关联。

BuildDateCtl 使用简单的UI逻辑创建了DATE_CTL 和DATE_CTL_COMMAND之间的关联——"back" 命令可将应用带回到INPUT_LIST_0 状态。

其他说明

我们的应用处理三个GUI小器件:列表、命令和DateCtl。实际上,列表和命令是同一个基本类型ListImpl的变种,但却用于不同目的。所有的应用都继承于IMidlet ,他们都优先于onStart()、onStop()、 onResume()、和onSuspend()回调。IMidlet现在展示一个未测试过的setColor() 函数。

有一个新的机制可用于实现小器件的持续性。onStart()在BuildInputList内部创建了一个名为"INPUT_LIST_0"的列表。小器件现在可以按名检索(在以前的实现中,只能通过指针检索),这样就不必在类的范围内明确的声明他们:
  1. List* l = (List*) getRegistered(lst);
复制代码
小器件总是可以通过使用"Named Constructor Idiom"[2],采用createXXX(const String& name, T* t) 或者createXXX(T* t)的格式来创建。第一种格式允许按名检索,并可在例中扩展性的使用。请注意方法名的变化:现在createXXX()取代了getXXX()。

事件处理是一般化的。有一个新方法可取代setCbk()——无效的installEventHandler(F f, LIST_EVT evt)。基本上,这个方法对于每个事件都登记一个回调,就像在LIST_EVT 定义中定义的一样:
  1. typedef  enum  {TAB,CMD, EVT_SIZE} LIST_EVT;
  2. // EVT_SIZE is a guard value
  3. //currently only CMD & TAB supported
  4. //CMD = EVT_COMMAND
  5. //TAB = EVT_CTL_TAB
  6. f 就是一个应用程序的方法。
复制代码
实现细节:事件处理作为一个单独的服务封装:
  1. template <class T, class F>
  2. class EvtHandler;
复制代码
命令(DATE_COMMAND) 在BuildDateCommand()内创建。命令是软件密匙包装,从BREW 的方面来看,它也是列表的小型规范。

现在进入最令人感兴趣的部分:装配小器件到更丰富的显示中。我们的UI层与J2ME有点类似——开头是Displayables,接着是从Displayables继承而来的屏幕(这是层的新的添加物),最后是继承于屏幕的命令、列表和其他小器件。从BREW 的观点来看,IDisplayable相当于IControl。屏幕增加了容器功能——每个屏幕都能够包含任何其他的Controls (IDisplayable), 包括他自己。
  1. class Screen: public IDisplayable
  2. {
  3. public:
  4.   void addDisplayable(IDisplayable* d);
  5.   void setDisplayableAt(IDisplayable* d, UINT pos);
  6.   void setCurrent(bool isRedrawing = true);
  7.   void setCurrentAt(UINT mainDisplayable, bool isRedrawing = true);
  8.   virtual ~Screen()
  9.   {}
  10. protected:
  11.   Screen(IMidlet* m):IDisplayable(m->getDisplayableRegistry()),
  12.                                   m_(m)
  13.   {}
  14. private:
  15.   void setCurrentImpl(IDisplayable* displayable,
  16.                       bool isRedrawing );
  17.   void redrawDisplayables();
  18. private:
  19.   DISPLAYABLES displayables_;
  20.   IMidlet* m_;
  21. };
复制代码
小器件在屏幕的开头实现,这使得配置非常灵活。例如:命令可以包含列表,诸如此类。下列代码更好的解释了这个概念:
  1. List*    l = List::createList("lst", this);
  2. Command* c = Command::createCommand("cmd", this);
  3.   l->addDisplayable(c);
  4.   l->addDisplayable(l);
  5.   l->setCurrentAt(0);
复制代码
列表 l 包含命令 c 和它自身(l),最后他们都呈送到屏幕上,而且c 被激活(setCurrentAt(0) )。setCurrentAt(index) 使用了一个基于零的指针符号,但是基于字符串(名字)的实现同样有可能——有一点点性能降低。快捷方式(setCurrent())是可行的—— 直接在容器内设置焦点。

正如上文提到的一样,指针可以自动生成,它在相当程度上简化了项的操作。指针操作封装在一个指定的类Indexes中,它可被所有有权限的组织(主要是小器件)使用:
  1. class Indexes
  2. {
  3. public:
  4.   void append(int id);
  5.   int size() const;
  6.   template <class P>
  7.   int getIDImpl(int ix);
  8. private:
  9.   BrewVector<int> indxs_;
  10. };
复制代码
另一个有用的机制也被添加到IMidlet中。如前所述,setCurrent() 和 setCurrentAt()有双重功能:他们都可以刷新屏幕,并且可在综合视图内设置激活的小器件。在内,setCurrent()和setCurrentAt() 使用IMidlet::setActive(),它唯一的用途就是保存到激活的IDisplayable 的引用。

当不必刷新屏幕时就可以使用IMidlet::setActive(),例如:
  1. bool dateCtlCmdTabHdl( Command* l)
  2.   {
  3.     setActive("DATE_CTL");
  4.     return true;
  5.   }
复制代码
在DateIml.h内实现了一个DateCtl (PickCtl是它的较小变种)。

包装

这个框架很容易扩展——只要添加必要的新控制和新事件即可。它比原来的BREW API要容易使用,因为它与J2ME模型相似,它提供了自动存储管理。所有前面描述的框架(见Generic Connection Framework和Cooperative Multitasking) 都很容易装配到这个框架里面。

上面是下载样品程序

原文地址:http://www.developer.com/ws/j2me/article.php/3116331

参考文献:

1.BREW and J2METM—A Complete Wireless Solution for Operators Committed to Java—Qualcomm http://www.qualcomm.com/brew/images/about/pdf/brew_j2me.pdf

2.Design Patterns: Elements of Reusable Object-Oriented Software—ErichGamma, RichardHelm, RalphJohnson, and John Vlissides, Addison-Wesley, 1994

3.String in BREW Revisited—a BrewString Generalization—http://www.developer.com/ws/brew/article.php/2229051

4.Emulating C++ Exception Handling—Gregory Colvin, C/C++ Users Journal, December 1994

5.For Brew Developers, There Are New Kids in Town: IThread & IRscPool, Part 2—http://www.developer.com/ws/brew/article.php/3105131

关于作者

Radu Braniste 是Epicad的技术主任。联系他请发邮件到: rbraniste@epicad.com

                     我是一个呼吸着现在的空气而生活在过去的人
               这样的注定孤独,孤独的身处闹市却犹如置身于荒漠
                                     我已习惯了孤独,爱上孤独
                                 他让我看清了自我,还原了自我
                             让我再静静的沉思中得到快乐和满足
                                   再孤独的世界里我一遍又一遍
                                   不厌其烦的改写着自己的过去
                                             延伸到现在与未来
                                       然而那只是泡沫般的美梦
                                 产生的时刻又伴随着破灭的到来
                         在灰飞烟灭的瞬间我看到的是过程的美丽
                                      而不是结果的悲哀。。。
返回列表