視圖、命令、資源文件及佈局的使用

<UIQ3開發白皮書1>應用程序框架的使用

UIQ3開發白皮書系列文檔翻譯自UIQ3官方開發文檔;
本文檔英文名稱:UIQ3_Whitepaper_02_View_Commands_ResourceFilesLayout.pdf
翻譯者:yzhv@IOICN
歡迎轉載,請註明出處作者.

視圖、命令、資源文件及佈局的使用
   

一、簡介
    在前一個白皮書中(參看 [1])討論了:如何使用應用程序框架,最後生成了一個具有空視圖的應用程序。 在本文中,繼續在程序中添加功能。但是首先我們先介紹一些基本的概念,通過閱讀本文,你可以:
• 更好地理解UI配置,
• 更好地理解 Build塊的概念。
• 更好地理解命令處理框架Command Processing Framework (CPF)的概念。
• 掌握如何從資源文件構造視圖內容。
• 掌握如何在視圖中處理命令,
• 掌握如何在不同的 UI配置中調整視圖.


  在前一白皮書中提到的:
• 切換模型的工作方式
• 如何在不同的程序之間切換
   將放在以後的白皮書中介紹。

二、UIQ 3 中的一些基本概念
2.1 簡介
     UIQ 3 引入了一些新的概念,本文檔重點介紹其中的三個,這些背景知識將在下一章中使用。

1. 第一個概念在前一白皮書中已經提到過 (參看 [1]):UI 配置.本文將更詳細地進行討論.
2. 第二個概念:命令處理框架 Command Processing Framework (CPF),該框架取代了以前的菜單系統,允許我們以比菜單系統更抽象的方式處理命令。
3. 第三個概念是管理佈局的一種,構建程序的一種方式。在 UIQ 2.x 中,因爲所有內容必須手工創建,因此程序的佈局非常難以處理,現在,我們有三種方式來佈局我們的程序:
(a) Listbox: 列表框是佈局相似信息的一種簡單方式。在 UIQ 2.x中 也可以使用列表框,但是使用起來非常困難,幾乎不可能創建自己的佈局。
(b) Layout manager:佈局管理器在其它的平臺上非常流行(參看 [6]),它允許在一個區域放置不同的控件,而且大多數情況下,取代了控件的手工佈局 。UIQ佈局管理器包括:
• 行佈局管理器
• 流佈局管理器
• 網格佈局管理器
•列布局管理器
(c) Building block: Build塊是佈局控件的一種新方式, Build塊結合了佈局管理器的強大功能,同時提供了列表框控件。

     本文檔不討論listbox和 layout manager,它們將在以後的白皮書中討論 (或者參看[2]). 我們只討論 building block。
4. 第4個概念我們不再詳細介紹了,因爲我們整個白皮書都是基於這種概念的:資源文件驅動的佈局, Resource-File-Driven
Layout.

2.2 UI 配置

2.2.1 簡介
    UI 配置的概念已經介紹過了(參看[1]). 當時,介紹了模擬器的在2種不同的配置情況:筆風格和軟鍵風格的 UI配置。在上一個白皮書中最後完成的程序在這2種配置下均可工作。在筆風格UI配置下,可以看到“後退”按鈕,如果運行在軟鍵風格UI配置下,該按鈕自動消失。對我們的程序來說,這種變化是透明的,在設計程序的時候就應該考慮這個問題,以便找到一個差異最小的設計方案。通常爲設備添加一個觸摸屏問題並不大,因此:推薦先針對軟鍵模式開發,然後在觸摸屏模式下進行優化,這也是本文要做的主要內容,因此,先配置模擬器運行在 軟鍵UI配置下,在命令行中輸入:

QUOTE:

cmd> uiqenv -ui softkey
那麼不同UI 配置的區別是什麼?在不同UI配置下運行UIQ 最終意味着什麼?接下來我們將要給出一個概括性的概念,你將看到它對程序員的影響。需要注意的是 只有特定的配置特性通過UI 配置處理,我將還會介紹一些不能被UI 配置處理的自定義區域.

2.2.2 一般性介紹
    UIQ 軟件平臺功能非常強大,可以通過很多高層的配置參數進行配置。這些參數目前包括:
Screen mode :屏幕模式是定義屏幕分辨率的方式.目前包括 4種已定義的屏幕模式(分辨率如下):
• 縱式(QVGA, 240 x 320 pixels)
• 橫式(QVGA, 320 x 240 pixels)
• 小型縱式(240 x 256 pixels)
• 小型橫式(256 x 240 pixels).

上述的分辨率僅僅是粗略的大小,實際屏幕分辨率取決於具體設備,可能有所變化。如果需要知道精確的分辨率,需要查詢顯示屏設備的廠商。
屏幕朝向(orientation):表示是否使用正常或反向模式(屏幕旋轉180 度)。通常會以正常模式來開發應用,因爲屏幕的朝向對您的開發不會有多大影響。
觸摸屏:顯示該應用程序能否被優化來充分利用數字轉換器。並非所有的應用程序都可受益於此類優化(例如動作類遊戲),這取決於硬件是否支持。下文會解釋,應用程序將要採用的主要交互風格是決定是否爲觸摸屏進行優化的更佳依據。
交互風格:目前有2中不同的交互風格
• 筆風格(pen-style),爲手寫筆進行優化(筆風格用戶界面並不意味着交互必須使用手寫筆進行);
• 軟鍵風格(softkey-style),爲單手操作進行優化。
這些UI 參數結合起來被稱爲UI 配置,並可通過唯一地ID辨認。頭文件Qikon.hrh 含有一些預設的UI 配置,例如:KQikSoftkeyStylePortrait。將來還可能添加其他配置參數。該文件還說明了這些ID如何使用。

2.2.3 UI 配置可以提供的內容
      上面介紹的UI 配置參數僅僅是目前我們用到的,其他的配置用做將來的擴展.UIQ 3 手機不一定要在單一的UI 配置中運行,例如,
Sony Ericsson P990 可運行在下述六種屏幕模式裏:
• KQikSoftkeyStyleLandscape
• KQikSoftkeyStylePortrait
• KQikSoftkeyStyleLandscape180
• KQikSoftkeyStyleSmallLandscape
• KQikSoftkeyStyleSmallPortrait
• KQikSoftkeyStyleSmallLandscape180.

未命名.JPG
然而,在同一時間,只能夠有一個配置可被激活。例如不可同時運行縱向與橫向模式。


2.2.4 如何開發
      前面已經提到:在任何時候UI配置只能同時運行一種.不同UI 配置是可以切換的。該切換可由硬件事件啓動(例如:P990 手機上的“翻蓋打開”或“翻蓋關閉”),也可通過應用程序控制(例如:瀏覽器從縱向模式到橫向模式切換)。通常,切換到橫向模式意味着應用程序將在全屏模式下運行而不顯示標題欄、狀態欄等常見的修飾欄,這些內容我們在以後介紹。儘管可以根據特定的UI 配置來進行優化,但一般而言,應用程序的界面排版、指令清單和具體行爲與UI配置是獨立的。例如,“翻蓋打開”時的縱向模式所顯示的選項可能會比“翻蓋關閉”時的縱向模式的選項要多;而橫向模式裏組件的佈局可以和縱向模式裏的不同等等。是否需要優化,主要取決於應用程序所提供的功能。

     注意,您不需要爲所有的UI 配置編寫代碼。您的應用程序可能會正常運行在一個並非專門爲其設計的UI 配置的設備上。例如,應用程序運行在一個僅支持筆風格UI 配置的設備上時,要求切換到軟鍵風格的UI 配置,這一轉換是不可能的。但如果程序僅僅定義了軟鍵風格配置,在其初始化時,仍會使用軟鍵風格配置的版本,但是將會實際運行在筆風格配置裏。這一類初始化,即應用初始化的UI 配置與其運行的設備的不同,被稱之爲“降級路徑”。應用程序框架將爲應用初始化尋找最接近的、最恰當的UI 配置。降級路徑大大簡化了您的任務,因爲您不需要爲所有的UI 配置定義應用程序的界面排版。這也是前面提到的屏幕朝向對大多數應用程序沒什麼影響的原因,因爲應用程序在正向和反向朝向中運行時,通常都採取相同的外觀。
    降級路徑亦使程序可以在未來的UI 配置上運行。
    在接下來的內容裏,我們將介紹這些行爲如何實現.

2.2.5 其它自定義區域
    注意:並非所有的自定義可以通過UI配置來處理,UI 配置只指定高層參數,手機廠家具有風格化UIQ3 的高度自由。請勿做以下假設:
• 屏幕組件(例如狀態欄)的具體外觀和位置;
• 指令分佈——請遵照指南;
• 硬件按鈕的映射(例如,假設具有非觸摸屏UI 配置的手機應至少提供四個方向的導航及確認按鈕);
• 缺省字體大小。
   在某部手機上,狀態欄位於屏幕頂端,在另外一部,可能在底部,而在第三部裏,可能在屏幕的邊上。


2.3 指令處理框架Command Processing Framework (CPF)

2.3.1 簡介
      如前所述,UIQ允許自定義擴展,軟件在設備上需要能夠變化,因此,需要一些適當的支持.例如,不必總是需要菜單欄,軟件不能假設擁有這些元素,以及直接處理它們. 在沒有觸摸屏的設備上,軟件的按鈕顯然是不太有用,因此,需要有一種方法將"按鈕"轉換爲其它的元素. 所以.軟件不能真地決定指令在哪裏執行,因爲這取決於UI配置,是否擁有菜單欄,按鈕,軟鍵,或者可能是硬鍵.我們必須把指令處理和指令顯示分離開來,這通過指令處理框架(CPF)來完成 (參看 [10]),系統來決定顯示指令的合理位置.(在特定的設備上提供哪些按鈕,有一些特定的要求,例如:沒有觸摸屏 UI配置的設備需要提供至少 4中切換方式,一個確認按鈕)
.

      程序只須定義它提供什麼指令,這些指令如何顯示由 CPF來決定.如果程序運行在軟鍵配置下,這些指令應該總能顯示.因此,這些指令必須能夠反映正確的狀態 (可見,暗化,選中等).所以,當打開菜單面板時,這些指令不可能動態初始化.DynInitMenuBarL 和DynInitMenuPaneL因此不能使用.另外,應該注意新的系統與 UIQ2.x中使用的舊的菜單系統之間的兼容性.這樣才能方便移植.重要的特性下面介紹,更多信息參看[2].
     篇幅原因,此處僅提供command processing framework的簡要介紹, CPF提供的優點包括:
1. 分佈式指令生成
2. 分佈式的抽象指令提供
3. 分佈式指令處理

2.3.2 設計概要

指令模型:UIQ 通常擁有視圖結構,每個視圖擁有自己的一系列指令,當進行視圖切換時,指令集進行相應的切換.當彈出對話框時,指令集也進行適當的切換,因此,每個視圖,對話框,或者彈出對象都擁有自己的指令模型.只有在正確指令模型裏面的指令纔會被顯示.
分佈式指令生成及處理: 一般,指令模型由幾個控件組成.例如,一個視圖可以擁有一個具有文本的編輯框,用戶應該提供一些指令處理文本,如:“剪切”, “複製”, 和 “粘貼”.這些指令除了可以被視圖管理,也可以由控件自己直接處理.  CPF 允許指令模型由多個控件組成.每個組成構建自己的指令列表.
上述內容形成了指令管理器 command manager (CQikCommandManager), 組成了所有功能的入口點(參看下圖).指令管理器 只有一個能夠被程序進行全局訪問.(控件棧上的任何控件都有自己的指令模型,只有擁有焦點的激活控件能夠被使用).這使得
FEP能夠在當前視圖中添加自己的指令.

未命名.JPG
       需要說明的是CQikCommand 不是一個抽象接口,而是一個具體的類.從資源文件創建的指令總是從這種類型.,也可以重載這個類以提供其他功能.指令甚至可以自己實現MQikCommandHandler接口,然後自己處理.
組成部件: 這裏介紹指令模式的參與者,以及相應UIQ接口:

    客戶:生成指令並將指令添加到框架的實體.客戶負責設置接收者,也就是每個指令的處理器. 在 UIQ 3 框架中客戶是一個 CCoeControl對象,並且實現 MQikCommandModelOwner接口.
    接收器:執行與指令相關的動作.接收器實現MQikCommandHandler接口.
    激發器:請求相應的指令發出執行請求.例如,軟鍵欄和程序的標題欄就是激發器.激發器實現 MQikCommandOperator 接口.


   注意:不一定要指定指令處理器,這種處理方法與 UIQ 2.x軟件非常相似,所有的指令在 AppUi種處理.
指令操作器:有兩種不同的指令操作器:默認的指令操作器由應用程序框架安裝, 自定義的操作器可以在特定的情況下安裝.後者允許以自定義的方式處理指令.自定義的處理器要求確保它能夠在所有的UI 配置(甚至是未知的)上正常工作, 這使得它們的實現更巧妙,因此,推薦:避免使用自定義的操作器.
注意: 指令操作器不必擁有可見的界面.甚至某些硬鍵也是如此處理,例如 "後退"或"確認"鍵. 其它的鍵通過OfferKeyEventL() 函數處理,不使用 CPF 操作器處理.


2.4 Building Block (BB)
     building block是UIQ 3引入的新概念,用於加快在視圖中的佈局創建.創建視圖時,可以選擇想要使用的 building block.每個 building block 擁有一些空隙,在每個空隙裏,可以放置任何控件.每個 building block有一個設置,指定其屬性如: 頁邊,對齊及大小.簡單點說:選擇building block,使用控件填充空隙,將 building blocks放進視圖,你的佈局就完成了.

Building block的示例如下圖:

未命名.JPG
Building block 也是普通的控件,不過沒有可見的界面及狀態信息,因爲它們僅僅是其它控件的容器,僅僅負責這些控件的放置.因此,如果你想要替換視圖的佈局,可以使用新的building block替換當前的 building block.

      在這種佈局切換過程中,可能發生下面3種情況:
1. 新佈局要求的控件已經是舊佈局的一部分:這種情況下,該控件應當重用,並且保持其狀態信息.
2. 新佈局不需要舊佈局中的控件:這種情況下,該控件不再繼續使用,我們可以隱藏它或者刪除它.

3. 新佈局要求使用一箇舊佈局中沒有的控件:這種情況下需要構造控件,如果控件已經構造但是不可見,那麼設置它可見.


    使用 building block時,即可以使用系統的building block ,也可以自己創建.推薦儘可能使用系統的,優點如下:
• 所有使用系統building blocks 能夠獲得一致的外觀
• 所有的 building blocks 的行爲經過了合理定義,使得可以估計可用的字符串空間 (對於特定的設備),這可以節約轉換和本地化的時間,因爲可以估計字符串被截斷的情況.
    如果系統的 building block不能滿足需要,可以創建自定義的 building block.在示例程序將使用系統building blocks. 如何創建自定義 building block 參看[2].
    前面說過 UIQ 3也引入了佈局管理其,但是它們很少使用.大多數情況,僅用於佈局不同的 building blocks (通常是行方式).大多數系統組件在內部使用佈局管理器,但是沒有給開發者開放.如果你想要在程序中使用,請參看[7].


三、視圖佈局的構造

3.1 簡介
     本章將重點通過擴展前一白皮書中實現的程序,介紹如何從資源文件構造視圖. 上次,我們通過代碼直接構造所有的內容,雖然沒有包含任何控件,但是工作的非常好.本章目標是:完全從資源文件構造視圖(包括所有控件及佈局).雖然可以直接從代碼中直接創建,但是需要程序員做更多工作,包括:
• 控件構造
• 控件的擁有關係
• 焦點處理
• 佈局處理
• 視圖 繪製處理
• 屏幕模式切換
    資源結構允許用戶極大的自由,因此剛開始會覺得比較複雜,因爲它們可以分解成較小的子結構,這可能會導致更復雜,但是分割使得我們更容易理解如何創建視圖,參看下圖:展現了資源文件中的不同結構,以及它們之間的關係.



未命名.JPG
     跟我們在 UI配置節看到的以至,視圖外觀根據運行環境的 UI配置的改變而不同.剛開始,我們只爲單個的 UI 配置定義一個視圖:軟鍵風格.這不代表它不能在其它的 UI配置下運行,僅代表該視圖 是針對軟鍵風格優化的.其它的 UI 配置,我們沒有精確定義,應用程序框架自動選取合適的風格.第一步,我們將創建一個空的,然後逐步加入更復雜的內容.

3.2 目錄結構
       按慣例先說明需要添假和修改的文件的目錄結構,大多數修改主要是針對資源文件,另外還要添加一些bitmap和一個 hrh文件,如下圖:


UIQ3_Whitepaper_02_View_Commands_ResourceFilesLayout.jpg

3.3 基本資源文件的修改
3.3.1 簡介
     先來做一些準備工作.首先,需要定義一個我們視圖所要使用的資源文件結構 (QIK_VIEW_CONFIGURATIONS) ,它定義了視圖的佈局以及指令,指令在下一章討論,現在留空.到後面我們會看到: 不在視圖佈局中定義控件會帶來的便利性, 控件定義在 QIK_CONTROL_COLLECTION結構中.現在我們先提供它,儘管還不使用.定義完這2個結構(雖然只是空的)後,從資源文件構造視圖. 我們應該能夠通過修改資源文件,創建視圖佈局的剩餘部分.

3.3.2 視圖結構的定義

     資源文件結構應該至少包括下列結構,QHelloWorld.rss 資源文件的內容:

QUOTE:

// QHelloWorld.rss
#include <Qikon.hrh>
#include <Qikon.rh>
RESOURCE QIK_VIEW_CONFIGURATIONS r_qhelloworld_view_config
{
      configurations =
     {
          QIK_VIEW_CONFIGURATION
         {
                   ui_config_mode = KQikSoftkeyStylePortrait;
                   view = 0;
                   command_list = 0;
         }
     };
}


     前面說過,我們將在軟鍵風格的 UI配置下繼續演示示例,因此只爲KQikSoftkeyStylePortrait定義了一個視圖.  然後定義一個控件集合的結構,如下:

QUOTE:

// QHelloWorld.rss
RESOURCE QIK_CONTROL_COLLECTION r_qhelloworld_control_collection
{
      items =
     {
     };
}

      顯然,大部分結構還是空的,但是 已經足夠我們修改視圖的構造代碼來使用它們.

3.3.3 第一次從資源文件構造

      視圖的構造代碼原來是:

QUOTE:

// QHelloWorldView.cpp
void CQHelloWorldView::ViewConstructL()
{
        CQikCommandManager& cmdManager = CQikCommandManager::Static();
        cmdManager.CreateCommandListL(*this);
}
現在修改爲:

QUOTE:

// QHelloWorldView.cpp
#include <QHelloWorld.rsg>
void CQHelloWorldView::ViewConstructL()
{
         ViewConstructFromResourceL(R_QHELLOWORLD_VIEW_CONFIG,
         R_QHELLOWORLD_CONTROL_COLLECTION);
}

     當然,需要在自動創建的資源文件中包含rsg文件,以便能夠找到我們的資源結構,當從資源文件創建視圖時,視圖保證爲我們創建合適的指令列表,所以我們不再需要 CreateCommandListL()函數了.執行如下命令重新編譯程序:

QUOTE:

cmd> abld build winscw udeb
因爲還沒有定義佈局,因此現在還看不到界面上的任何差異,接下來我們將介紹這些內容:創建佈局.

3.4 創建佈局
3.4.1 簡介
      現在可以決定我們的視圖裏可以包含什麼了.首先我們要添加一個 building block,其中包含一個控件.在創始它們之前,需要先定義視圖結構.

3.4.2 定義視圖佈局結構
        視圖結構非常簡單,因爲我們將使用大部分默認值 QIK_VIEW 結構定義如下:

QUOTE:

// QHelloWorld.rss
RESOURCE QIK_VIEW r_qhelloworld_view
{
     pages = r_qhelloworld_viewpages;
}
此處聯接到 QIK_VIEW_PAGES 結構:

QUOTE:

// QHelloWorld.rss
RESOURCE QIK_VIEW_PAGES r_qhelloworld_viewpages
{
     pages =
    {
            QIK_VIEW_PAGE
            {
                  page_content = 0;
            }
    };
}


     下一節我們將定義 page_content此處留空(定義爲0).再重新編譯之前,需要更新 QIK_VIEW_CONFIGURATION 結構,聯接到 QIK_VIEW ,結果如下:

QUOTE:

// QHelloWorld.rss
RESOURCE QIK_VIEW_CONFIGURATIONS r_qhelloworld_view_config
{
     configurations =
      {
            QIK_VIEW_CONFIGURATION
              {
                     ui_config_mode = KQikSoftkeyStylePortrait;
                     view = r_qhelloworld_view;
                     command_list = 0;
             }
       };
}

       爲避免重新編譯不必要的內容,我們需要將新的結構 添加到資源文件的末尾,如QIK_VIEW_CONFIGURATIONS 和 QIK_CONTROL_COLLECTION 結構在文件中的位置不變.
       然後重新編譯程序,執行:

QUOTE:

cmd> abld resource winscw udeb
爲確保修改是正確的,可以在模擬器中運行程序測試,不過不要期望看到可見的變化.

3.4.3 創建 building block


       現在可以給程序添加可見的修改了. 從系統 building blocks (參看[28])裏選擇使用 IconCaptionedOnelineBuildingBlock, 這個 building block 帶有一個小圖標,一個標題,一個任意控件. 這個 building block通過枚舉值 EQikCtIconCaptionedOnelineBuildingBlock標識,定義在 QikStockControls.hrh文件中.
      UIQ提供的大多數控件又叫做 stock 控件,通過工廠函數創建.在 UIQ 2.x 中,這個原則適用於從資源文件創建對話框.在此,該原則用於構造視圖,包括 building blocks.
      首先定義一個  QIK_XXX_CONTAINER_SETTINGS 結構.有兩種選擇:
QIK_CONTAINER_SETTINGS 是默認的容器,它不添加任何滾動欄. 如果在整個頁面裏只想包含一個控件,它就非常方便.任何滾動條可以由容器內的控件提供,這類容器的典型的例子就是列表框.如果控件比較多,可能會被放到可見區域之外,導致不可訪問.
QIK_SCROLLABLE_CONTAINER_SETTINGS 適合於不止一個控件的情況,如果控件擁有自己的滾動條,那麼就不應當使用該容器,因爲你可能會面臨2個滾動條, (一個是容器的,另一個是控件自己的l).

       起初,我們只有一個控件,但是後面可能會使用更多的控件,因此我們選用 QIK_SCROLLABLE_CONTAINER_SETTINGS 結構 (而不是使用缺省值). 在此結構裏面,需要分別定義容器的元素.有多個QIK_CONTAINER_ITEM_XXX 資源結構可以使用,允許直接使用結構或者 間接聯接. 可用的結構包括:
• QIK_CONTAINER_ITEM
• QIK_CONTAINER_ITEM_CI_LI
• QIK_CONTAINER_ITEM_CD_LI
• QIK_CONTAINER_ITEM_LD
• QIK_CONTAINER_ITEM_CI_LD
• QIK_CONTAINER_ITEM_CD_LD
• QIK_CONTAINER_ITEM_NESTED_CONTAINER
• QIK_CONTAINER_ITEM_NESTED_CONTAINER_CI_LI
     這些結構的區別請參看SDK文檔,或者參看 Qikon.hrh 文件中的結構定義.在示例中,我們直接內含定義我們的控件,但是不想自定義佈局的數據,因此我們選擇 QIK_CONTAINER_ITEM_CD_LI 結構,而留空佈局的數據. 第一步,我們不定義任何控件,除了 building block.因爲  building block 不包含任何可見內容,因此做了修改之後仍不能看到變化:

QUOTE:

// QHelloWorld.rss
#include <QikStockControls.hrh>
RESOURCE QIK_SCROLLABLE_CONTAINER_SETTINGS r_qhelloworld_page_content
{
     controls =
    {
          QIK_CONTAINER_ITEM_CD_LI
         {
              type = EQikCtIconCaptionedOnelineBuildingBlock;
              control =
              {
                      QIK_SYSTEM_BUILDING_BLOCK
                      {
                            content =
                            {
                            };
                      }
               };
         }
    };
}

      不要忘記更新 QIK_VIEW_PAGE 結構,指定我們使用一個滾動容器EQikCtScrollableContainer),聯接到其定義(QIK_SCROLLABLE_CONTAINER),它定義了滾動容器的行爲,並且指定了使用的頁面內容:

QUOTE:

// QHelloWorld.rss
RESOURCE QIK_VIEW_PAGES r_qhelloworld_viewpages
{
     pages =
    {
          QIK_VIEW_PAGE
         {
               container_type = EQikCtScrollableContainer;
               container = r_qhelloworld_container;
               page_content = r_qhelloworld_page_content;
         }
    };
}


// we are happy with the default behavīor of the scrollable container
RESOURCE QIK_SCROLLABLE_CONTAINER r_qhelloworld_container
{
}



3.4.4 創建標題
      現在可以創建控件,創建後就可以看到程序的變化
. 我們的 building block 可以容納三個不同的控件,首先定義一個標題,需要填充 building block的合適的間隙; 因此創建 QIK_SLOT_CONTENT_XXX. 定義的時候,有幾個變化需要注意,因爲只定義標題,所以最合適的就是
QIK_SLOT_CONTENT 版本:

QUOTE:

// QHelloWorld.rss
RESOURCE QIK_SCROLLABLE_CONTAINER_SETTINGS r_qhelloworld_page_content
{
        controls =
        {
                  QIK_CONTAINER_ITEM_CD_LI
                  {
                        type = EQikCtIconCaptionedOnelineBuildingBlock;
                        control =
                        {
                                   QIK_SYSTEM_BUILDING_BLOCK
                                  {
                                           content =
                                          {
                                                  QIK_SLOT_CONTENT
                                                  {
                                                         slot_id = EQikItemSlot1;
                                                          caption = "Hello World";
                                                   }
                                           };
                                  }
                         };
                   }
         };
}


3.4.5 創建位圖
      現在添加圖標. 首先要創建一個mbm文件, 與第一個白皮書中介紹的方法一樣.首先在mmp文件中定義mbm文件:

QUOTE:

// QHelloWorld.mmp
START BITMAP qhelloworld.mbm
TARGETPATH /private/E1000001
HEADER
SOURCEPATH ../data/image
SOURCE c16 GoldStar.bmp
SOURCE 8 GoldStar_mask.bmp
END


      bitmap文件存儲在私有的數據緩衝目錄裏.這可以避免任何名字衝突,可以避免別人誤用.我們創建自己的mbm文件,但是不添加到已有的mbm文件中,儘管那樣做是可行的.首先使用更新的mmp文件創建新的mbm文件,確保編譯正確,然後創建資源,執行下列命令:

QUOTE:

cmd> abld makefile winscw
cmd> abld resource winscw udeb
現在可以使用圖標了,首先將圖標的 mbg文件包含到資源文件,通過創建 CEikImage 控件,並放入building block的EQikIconSlot1 間隙中 . 因爲要內含創建控件,因此使用 QIK_SLOT_CONTENT_DIRECT,現在 building block的定義如下:

QUOTE:

// QHelloWorld.rss
#include <QHelloWorld.mbg>
QIK_SYSTEM_BUILDING_BLOCK
{
       content =
      {
            QIK_SLOT_CONTENT
            {...},
            QIK_SLOT_CONTENT_DIRECT
            {
                 slot_id = EQikIconSlot1;
                 type = EEikCtImage;
                 control = QIK_IMAGE
                 {
                       content = QIK_CONTENT_MBM
                       {
                               bmpfile = //private//E1000001//QHelloWorld.mbm;
                               bmpid = EMbmQhelloworldGoldstar;
                               bmpmask = EMbmQhelloworldGoldstar_mask;
                       };
                  };
            }
      };
}
       重新編譯資源文件,啓動模擬器,現在只能看到佈局的第一行,第二行的控件還沒有添加(在下一節中介紹).如下圖:


UIQ3_Whitepaper_02_View_Commands_ResourceFilesLayout.jpg
       因爲繪製矩形的代碼沒有刪除,因此截圖中仍然可以看到,通過對比,也可以看到UIQ 3 和UIQ 2.x的基本差別.
       添加控件的方法非常簡單,複製已有的圖標或標題,作爲第2個控件,不過不能交互,因爲用戶不會和標題進行交互.因此,下面我們要介紹允許交互的控件的添加,例如編輯窗口.

3.4.6 創建控件
      下面將要爲 building block添加一個edwin (editor window), 僅僅需要添加一個QIK_SLOT_CONTENT_DIRECT, 而且是一個edwin.

QUOTE:

// QHelloWorld.rss
QIK_SYSTEM_BUILDING_BLOCK
{
      content =
      {
            QIK_SLOT_CONTENT
           {
                slot_id = EQikItemSlot1;
                caption = "Hello World";
           },
          QIK_SLOT_CONTENT_DIRECT
          {
               slot_id = EQikIconSlot1;

               type = EEikCtImage;
               control = QIK_IMAGE
               {
                     content = QIK_CONTENT_MBM
                     {
                         bmpfile = //private//E1000001//QHelloWorld.mbm;
                         bmpid = EMbmQhelloworldGoldstar;
                         bmpmask = EMbmQhelloworldGoldstar_mask;
                     };
               };
          },
         QIK_SLOT_CONTENT_DIRECT
         {
              slot_id = EQikItemSlot2;
              type = EEikCtEdwin;
              control = EDWIN
             {
             };
         }
    };
}

    重新編譯後執行,就可以看到結果:
1. building block被高亮顯示 (如下圖a),高亮部分包含了2行,這也是爲什麼不使用兩個簡單的 IconOnelineBuildingBlocks 的原因,雖然外觀是一樣的,但是行爲不同,因爲那樣高亮部分只包含一行. 高亮行爲是自動添加的,現在可以與 building block交互,或者與控件交互.

2. 有一行虛線,看上去象是編輯窗口,但是 它不是,因爲那裏沒有顯示允許用戶輸入的光標.(a).
3. 即使沒有光標,文本輸入也是可能的,因爲右側有一個三角,說明系統可以識別手寫(a).
4. 自動擁有一個軟鍵 — 唯一的一個,因爲我們並沒有添加(a).
5. 可以通過下列方式,激活 building block:
• 選擇適當的軟鍵 (確認鍵)
• 輸入一些文本,與輸入系統獨立 (例如:鍵盤,手寫識別,預測文本輸入, ...)
• 點擊 building block
     可以點擊building block可能會比較令人意外, 即使我們正在運行的 UI配置沒有包含觸摸屏.就象前面提到的(參看 2.2節) , UI配置中的“touch”參數 並不代表沒有數字轉換器. 顯然模擬器上有一個 活動的數字轉換器,因此可以直接與屏幕直接交互(b).
6.一旦激活 building block 就可以與它交互了.現在我們真正看到了 edwin. 可以輸入文本,並且可以移動光標,也可以關閉它(c).

7. 可以看到一個自動彈出的building block標題 (c).
8. 如果 edwin裏沒有文本,軟鍵就非常簡單,唯一能做的就是就是關閉 edwin.也可以通過點擊浮動欄 的外面來關閉 (c).
9. 輸入文本後,自動產生了更多的指令(d).
10. 關閉浮動欄後,文本出現在building block中,替換了原來的虛線(e).
11. 如果輸入過多的文本,末尾將會被截斷.現在應該明白爲什麼這個 building block 叫做 OnelineBuildingBlock. 因爲它只能顯示一行文本. TwolineBuildingBlock 允許換行一次,然後才截斷. (f).



未命名.JPG

3.4.7 其它的佈局
      其它控件的創建方法類似.建議使用不同的 building blocks體會一下.例如:

• 複製已有的building block
• 從複製結果中刪除圖標
• 將building block 類型從 EQikCtIconCaptionedOnelineBuildingBlock 改爲 EQikCtCaptionedHalflineBuildingBlock
• 爲building block 添加一個標誌以便擁有一個分割條.
修改後的內容如下:

QUOTE:

// QHelloWorld.rss
RESOURCE QIK_SCROLLABLE_CONTAINER_SETTINGS r_qhelloworld_page_content
{
        controls =
        {
             QIK_CONTAINER_ITEM_CD_LI
             {...},
             QIK_CONTAINER_ITEM_CD_LI
             {
                  type = EQikCtCaptionedHalflineBuildingBlock;
                  control =
                  {
                         QIK_SYSTEM_BUILDING_BLOCK
                         {
                                  flags = EQikBuildingBlockDividerBelow;
                                  content =
                                 {
                                        QIK_SLOT_CONTENT
                                       {
                                             slot_id = EQikItemSlot1;
                                             caption = "Hello World!";
                                       },
                                       QIK_SLOT_CONTENT_DIRECT
                                       {
                                             slot_id = EQikItemSlot2;
                                             type = EEikCtEdwin;
                                             control = EDWIN
                                            {
                                            };
                                        }
                                 };
                           }
                    };
             }
     };
}


      結果如下圖,我們不需要擔心焦點處理,它已經自動實現了. 第二個 building block與第一個有明顯的區別.示例說明: 在一個視圖中使用混合building blocks時,應當保持視圖清晰,並容易使用.在以後的白皮書中,我們將會詳細介紹什麼時候應該/不應該使用混合的 building blocks.

UIQ3_Whitepaper_02_View_Commands_ResourceFilesLayout.jpg

     現在第二個 building block 已經顯示了,並且仍然可以看到紅色的矩形.顯然,控件只重繪了它需要繪製的部分.並且高亮是半透明的,因此我們可以看見矩形.
     在UIQ 2.x 中控件繪製時,通常用背景色清除矩形.不應該再繼續這樣實現,因爲背景可能不是單色.另外,這使得不同的控件很好地混合,就象示例那樣.一般,控件僅需要刪除 ClearRect() 函數 .應用程序框架保證重繪正確處理,並且更新背景.

3.5 使用新的佈局
     除了上節已經介紹的控件創建,交互之外,控件指令的添加也是自動處理的.但是,我們怎麼在程序中訪問從資源文件中創建的控件呢?我們需要考慮兩種情況:
1. 獲取控件以便設置或獲取一些值
2. 控件發生改變時獲取通知消息(以便能夠檢查發生了什麼改變)

3.5.1 訪問控件
      前面沒有討論控件的標識,如果我們有多個同類型的控件,該怎麼辦? 因此,需要給每個控件一個識別符.在源代碼中,使用相同的標識符.爲了保證正確使用, 可以創建一個通用的頭文件,即
hrh文件.在示例中創建文件 QHelloWorld.hrh,裏面包含了一些用於標識控件的枚舉值.我們並對所有的控件定義標識,只標識我們需要的;我們將只使用如下unique_handle 來標識兩個 edwinith.

QUOTE:

// QHelloWorld.hrh
enum TQHelloWorldControls
{
EFirstEdwin = 1,
ESecondEdwin
};
然後就可以使用這些枚舉值來標識控件:

QUOTE:

// QHelloWorld.rss
#include "QHelloWorld.hrh"
QIK_SLOT_CONTENT_DIRECT
{
      unique_handle = EFirstEdwin;
      slot_id = EQikItemSlot2;
      type = EEikCtEdwin;
      control = EDWIN
      {
      };
}
[...]
QIK_SLOT_CONTENT_DIRECT
{
        unique_handle = ESecondEdwin;
        slot_id = EQikItemSlot2;
        type = EEikCtEdwin;
       control = EDWIN
       {
       };
}


      通常在激活視圖的時候顯示視圖內容,因此 ViewActivatedL()函數是最適合查找控件的位置.對 LocateControlByUniqueHandle()的調用將遍歷視圖的控件,然後找到我們需要的控件:

QUOTE:

// QHelloWorldView.cpp
#include <EikEdwin.h>
#include "QHelloWorld.hrh"
void CQHelloWorldView::ViewActivatedL(const TVwsViewId&, TUid, const TDesC8&)
{
        _LIT(KFirstEdwinText, "First");
        _LIT(KSecondEdwinText, "Second");
        CEikEdwin* firstEdwin = LocateControlByUniqueHandle<CEikEdwin>(EFirstEdwin);
        firstEdwin->SetTextL(&KFirstEdwinText);
       CEikEdwin* secondEdwin = LocateControlByUniqueHandle<CEikEdwin>(ESecondEdwin);
       secondEdwin->SetTextL(&KSecondEdwinText);
}


      上述代碼查找兩個edwin控件,然後爲它們設置文本.然而代碼中有2個問題需要小心.因爲,我們不能保證控件確實存在.可能程序員想修改佈局,不想再繼續使用第二個edwin. 因此上述代碼需要重寫,以檢查 LocateControlByUniqueHandle() 的返回值,以便只處理確實存在的控件.另一個問題是每次激活視圖時都爲它設置文本,因此,我們所做的修改可能會丟失,例如,離開程序,然後又切換回來.
      另一種情況是,當需要保存控件內容時,怎麼訪問控件,適合的位置是 SaveL() 函數,這個函數每次在需要保存視圖內容十,由應用程序框架調用.例如:

QUOTE:

// QHelloWorldView.cpp
void CQHelloWorldView::SaveL()
{
      CEikEdwin* firstEdwin = LocateControlByUniqueHandle<CEikEdwin>(EFirstEdwin);
      HBufC* buffer = firstEdwin->GetTextInHBufL();
      User::InfoPrint(*buffer);
      delete buffer;
}


3.5.2 獲得修改通知消息

      每次控件內容發生改變時,控件向它的 MCoeControlObserver發送事件消息.該事件然後被傳送到視圖,因此需要做的就是重載視圖的 HandleControlEventL() 函數.通常唯一比較感興趣的是 EEventStateChanged,通常使用視圖的默認行爲就足夠了,因此確保給它傳遞所有的事件.

QUOTE:

// QHelloWorldView.cpp
void CQHelloWorldView::HandleControlEventL(CCoeControl* aControl, TCoeEvent aEventType)
{
     _LIT(KInfo,"Control changed");
     if (aEventType == EEventStateChanged)
    {
         iEikonEnv->InfoMsg(KInfo());
     }
      CQikViewBase::HandleControlEventL(aControl, aEventType);
}
      大多數情況,控件發生變化後,不需要立刻知道.因此推薦使用上面介紹的 SaveL()函數.在 UIQ 2.x 中,常常實現HandleControlEventL()函數,因爲焦點處理是手工處理的.現在,UIQ3的焦點處理是自動的,焦點策略由佈局管理器決定.如果沒有安裝佈局管理器,焦點移動到給定方向的最近一個控件上. (參看[9] )
在這個函數裏,可以使用定義的唯一識別符 ,判斷是哪個控件發生了變化.

QUOTE:

switch(aControl->UniqueHandle())
{
    case EFirstEdwin:
    // Do something with the first edwin
    break;
    case ESecondEdwin:
    // Do something with the second edwin
    break;
   default:
   // all other controls (not necessarily identifiable)
   break;
}


四 、視圖指令
4.1 簡介
      本節爲視圖添加一些指令.前面已經看到一些自動添加的指令,因此我們只需要關注其它的指令.資源文件結構的圖中已經告訴我們應該在哪裏添加指令,我們首先來介紹第一個,其它的以後介紹.本章,我們將討論:
1. 添加指令

2. 初始化指令,
3. 處理指令
4. 改變指令狀態


4.2 目錄結構


UIQ3_Whitepaper_02_View_Commands_ResourceFilesLayout.jpg


4.3 創建指令
      QikCommand.rh頭文件定義了指令列表和指令的資源結構. 因此我們只需使用它們創建指令列表,並添加到視圖結構即可.首先,添加一個關閉指令,用於在測試環境下檢查內存泄露
.關閉指令的標準 ID EEikCmdExit定義在uikon.hrh文件中,同時需要設置狀態標誌爲 EQikCmdFlagDebugOnly,以便該指令僅在debug編譯版本中可見 .
      爲指令定義文本時,注意文本不能太長,因爲指令可能出現在軟鍵上,空間非常有限.在示例中,我們使用 “Close (debug)”文本,但是,在軟鍵上,顯然太長了,因此另外定義一個短的文本 “Close”,在軟鍵上出現時,將使用短文本.

QUOTE:

// QHelloWorld.rss
#include <QikCommand.rh>
#include <uikon.hrh>
RESOURCE QIK_VIEW_CONFIGURATIONS r_qhelloworld_view_config
{
   configurations =
  {
       QIK_VIEW_CONFIGURATION
      {
             ui_config_mode = KQikSoftkeyStylePortrait;
             view = r_qhelloworld_view;
             command_list = r_qhelloworld_commands;
       }
   };
}


RESOURCE QIK_COMMAND_LIST r_qhelloworld_commands
{
        items =
        {
                QIK_COMMAND
               {
                      id = EEikCmdExit;
                      type = EQikCommandTypeScreen;
                      // Indicate that this command will only be visible in debug
                      stateFlags = EQikCmdFlagDebugOnly;
                       text = "Close (debug)";
                     shortText = "Close";
               }
        };
}


       應用程序框架知道如何處理指令,因此,不需要添加任何處理代碼. 因爲我們創建的所有的代碼歸應用程序框架擁有,而且我們不希望出現內存泄露,因此程序應當能夠無異常地關閉.如果創建了控件,但是沒有刪除它,程序就不能無異常地關閉,而是產生內存泄露異常,如下圖:

UIQ3_Whitepaper_02_View_Commands_ResourceFilesLayout.jpg

       現在添加其它指令,這些指令將由我們的程序處理,因此需要唯一地標識.指令標識不能與系統已定義的衝突(如前面提到的關閉指令標識),因此我們使用 0x1000 作爲我們的第一個 ID.

QUOTE:

// QHelloWorld.hrh
enum TQHelloWorldCommands
{
EQHelloWorldCommand1 = 0x1000,
EQHelloWorldCommand2,
EQHelloWorldCommand3
};

       添加的新指令中,有的用於描述可用的特徵 (參看[37, 2]).第二個指令用於介紹 cpfFlags,該標誌會影響到指令的位置.

QUOTE:

// QHelloWorld.rss
RESOURCE QIK_COMMAND_LIST r_qhelloworld_commands
{
     items =
    {
           QIK_COMMAND
          {
                 id = EEikCmdExit;
                 type = EQikCommandTypeScreen;
                 // Indicate that this command will only be visible in debug
                 stateFlags = EQikCmdFlagDebugOnly;
                 text = "Close (debug)";
                 shortText = "Close";
          },
          QIK_COMMAND
          {
                 id = EQHelloWorldCommand1;
                 type = EQikCommandTypeScreen;
                 text = "Command1";
                 shortText = "Cmd1";
          }
          QIK_COMMAND
          {
                id = EQHelloWorldCommand2;
                type = EQikCommandTypeScreen;
                text = "Command2";
                stateFlags = EQikCmdFlagInvisible;
                cpfFlags = EQikCpfFlagDuplicateInMenuPane |
                EQikCpfFlagPreferToBePlacedInButtonbar;
                shortText = "Cmd2";
          },
          QIK_COMMAND
          {
                id = EQHelloWorldCommand3;
                type = EQikCommandTypeScreen;

                text = "Command3";
                shortText = "Cmd3";
                stateFlags = EQikCmdFlagCheckBox;
                icon = r_qhelloworld_command_icon;
          }
     };
}

RESOURCE QIK_CONTENT_MBM r_qhelloworld_command_icon
{
        bmpfile = //private//E1000001//QHelloWorld.mbm;
        bmpid = EMbmQhelloworldGoldstar;
   &
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章