2014年9月3日 星期三

Virtual Keyboard 的顯示與隱藏

緣起

在 iOS app 裡面, 很多時機仍舊需要使用者輸入文字, 因此 iDevice (iPhone, iPad, iPod Touch) 上面的虛擬鍵盤就很重要。

然而,在 Delphi 上面,我們要構成一個完整的 app 畫面,需要許多的設計,包含美術設計與介面設計,美術設計會構築美觀的畫面,而介面設計跟程式設計的人則賦予這個畫面完整的功能。

如果您很習慣於使用 XCode 來製作 iOS app, 一定很常使用 UIImageView 或者 TapDetectingImageView 來取代一般的 UIButton, 因為 UIButton 常常在圖片的調整上面不精準,會讓整個畫面跟原來美術設計的構想差距很大。

所以,這個觀念在轉向到 Delphi 設計 app 的時候,也就會有一樣的擔憂,進而也使用 Delphi 的 TImage 來構築畫面,在 TImage 上面建立 onClick 的事件處理常式,把 TImage 當成 TButton 來用,畢竟這樣使用,在畫面的呈現上,要比先把一個 TButton 放到畫面上,再把圖片拉進到 TButton 裡面去要更為精準,也更為直接。

然而,在這種作法之下,居然產生了很大的副作用,所以有了這篇分享文章的誕生。

Virtual Keyboard 的顯示

圖一: 主要討論介面,模擬登入
我們接下來在整篇文章當中都會以圖一作為討論的介面,因為筆者已經把所有重點都濃縮在這個畫面當中了。

一般來說,只要是有需要使用者登入的介面,大多會有帳號、密碼的輸入框,就像上圖當中的 Account 跟 Password 欄位,當我們觸碰輸入框 (在本例是 TEdit) 的時候,虛擬鍵盤就會自動出現,如圖二所示。
圖二:虛擬鍵盤出現了
虛擬鍵盤旁邊有時候會有些 Done, Prev, Next 的功能按鈕,但介面上的設計就是要讓使用者自由自在的使用,所以我們在設計介面的時候,沒辦法規定使用者一定只能照著特定的操作步驟來用 app.

以圖二當中的登入畫面來看,如果密碼欄位旁邊有個『登入』按鈕,大多數的使用者一定會直接點選它,並期盼虛擬鍵盤會自己消失,然後完成登入的動作,切換到下一頁,幾乎不會有使用者先把虛擬鍵盤關掉,然後才去點選『登入』按鈕的,這個程序在 iPad 就更是如此了。

因此,在圖一一開始製作的時候,筆者就設定了三個按鈕在上面,分別是最上方的『By Button』,這是一個 TButton 元件,然後筆者再把 TImage 跟 TLabel 放進裡面去,如果還不知道這個步驟要如何達成,請參考筆者之前這篇文章

第二列則有兩個按鈕,都是 TImage 當中直接放了一個 TLabel 來製作的,因為有實作出 onClick 事件,所以點選它的時候,也可以跟點選 TButton 有幾乎完全相同的效果,當然,筆者一開始也是這樣以為的,只是,遇到了虛擬鍵盤的時候,就多了很多需要注意的地方,以下,且聽我為您娓娓道來。

使用 TButton

如果我們的『登入』按鈕是一個TButton, 那麼,只需實作它的 onClick 事件處理常式,其他什麼都不用管,點擊它,虛擬鍵盤就會消失,並且執行按鈕的 onClick 事件處理常式,如圖三所示。
圖三: 點選了By Button 按鈕,虛擬鍵盤消失,顯示 ShowMessage 的內容.



使用 TImage

接著,我們用 TImage 來試試看,同樣只實作它的 onClick 事件處理常式,然後點擊這個 TImage 看看,這是圖三當中下面那列按鈕的右方按鈕。
圖四:點擊了 TImage, 虛擬鍵盤卻沒有消失
這時候,TImage 的 onClick 事件處理常式程式碼如下:
procedure TForm1.Image2Click(Sender: TObject);
begin
   ShowMessage('IMage Clicked');
end;

單純的 ShowMessage, 沒辦法隱藏虛擬鍵盤, 如果這個動作還會把整個畫面釋放掉的話,引起的問題就會更多,甚至會造成 Access Violation, 原因是從 iOS 向上層層回傳的 SingleTap 事件沒有元件可以處理,所以造成了 Access Violation.

要解決這個問題,可以無副作用解決的方法有二:
1. 用 TButton 來包覆 TImage, 這樣就不會有上面的問題。
2. 在 TImage 的 onClick 事件處理常式當中,首先就把 Focus 轉移到其他可以取得 Focus 的元件上,例如途中的 CheckBox, 改過後的程式如下:
procedure TForm1.Image3Click(Sender: TObject);
begin
   self.CheckBox1.SetFocus;
   ShowMessage('Image clicked with transfer focus');
end;

而執行後的程式畫面則如圖五:
圖五: 把 Focus 移轉後的畫面

當然,鐵齒如我,也嘗試過了很多方法,例如把 TVirtualKeyboard 從系統中硬鏟出來,然後呼叫它的 HideVirtualKeyboard 方法:

首先宣告
FService: IFMXVirtualKeyboardService;

procedure TForm1.FormCreate(Sender: TObject);
begin
    if TPlatformServices.Current.SupportsPlatformService
        (IFMXVirtualKeyboardService, IInterface(FService)) then begin
    end;
end;

procedure TForm1.Image3Click(Sender: TObject);
begin
   FService.HideVirtualKeyboard;
end;

這樣在畫面上,看起來的確是會把虛擬鍵盤弄不見,但如果沒有把 Focus 先移到別的元件上頭,切換到下一個畫面以後,就會發現不管點什麼,還是會出現 Access Violation, 所以別鐵齒。

要強制虛擬鍵盤出現,就直接在該 Edit/Memo 元件上面設定 Focus, 如前面的程式碼範例中的 self.CheckBox1.SetFocus;

要強制虛擬鍵盤出現,就把 Focus 從編輯文字的元件上移除即可。

本篇文章的範例程式,可以在這裡下載。

2014年8月29日 星期五

FireMonkey v.s. VCL 之一 - 動態產生視覺元件的差異

在撰寫程式的過程中,對於畫面的製作,會隨著程式經驗與類型的積累,產生幾種演進:
所有畫面都事先在DesignTime,也就是設計階段,把畫面都先處理好
能在設計階段製作的,先在設計階段處理,不行的部分,在RunTime即時產生
所有畫面都在RunTime 產生

演進的狀況不外乎上述三種,但未必每個程式人員都有機會遭遇到全部三種狀況,也很難說那一種做法是功力最高的,因為所有的程式製作方法,都應該視當時的條件來決定製作的方式,這些條件包含了:
開發時程 - 有時候很多專案會被要求在極短的時間之內完成,沒時間,就沒機會使用全RunTime的做法完成。

對FootPrint (整個編譯結果的檔案大小)的要求- 如果遇到只能在非常局限的磁碟空間中安裝,完全RunTime 的做法或許是達成要求的唯一方法。

便於團隊對程式碼的長久維護- 此時就要考慮整個團隊的程式設計習慣,挑選適合的做法。

且不管最後選了哪一種,在RunTime建立畫面的需求都是可能會存在的,所以我們在此對完成這個需求的方法做些介紹。,也就是動態產生元件的方法,並就VCL跟 FireMonkey 兩種架構的差異進行比較。

我們以最簡單的,在 Form 上面建立 Button 的程序進行比較

VCL版:
procedure TForm1.btnAddButtonClick(Sender: TObject);
var
    btn: TButton;
    countByRow, idx: integer;
begin
    btn := TButton.Create(self);
    btn.Parent := self.Panel1;

    self.ButtonList.Add(btn);
    idx := self.ButtonList.IndexOf(btn);
    btn.Caption := 'btn' + IntToStr(idx);

    countByRow := (self.Panel1.Width div btn.Width);
    btn.Left := idx mod (countByRow) * btn.Width + 10;
    btn.Top := idx div (countByRow) * btn.Height + 10;
end;
上面這段程式可以在 VCL 建立一個 Button, 但在 FireMonkey 當中, 建立的方法就不一樣了:

FireMonkey版:
procedure TForm2.btnAddNewButtonClick(Sender: TObject);
var
    btn: TButton;
    countByRow, idx: integer;
begin
    btn := TButton.Create(self);
    self.scrollBox.AddObject(btn);

    self.ButtonList.Add(btn);
    idx := self.ButtonList.IndexOf(btn);
    btn.Text := 'btn' + IntToStr(idx);

    countByRow := Trunc(self.scrollBox.Width / btn.Width);
    btn.Position.X := idx mod (countByRow) * btn.Width + 10;
    btn.Position.Y := idx div (countByRow) * btn.Height + 10;
end;
在 VCL 當中,動態建立了一個新的按鈕以後,我們只需指定該按鈕的 Parent, 就可以把建立的按鈕放到 Parent 這個元件上面,當然,也需要先指定新按鈕的位置 (Left, Top) 與大小 (Width, Height).



而 FireMonkey 要做到相同的效果,則是看我們要把建立出來的按鈕放在哪個元件上面,就呼叫該元件的AddObject 方法,例如我們要把新建立的按鈕顯示在表單上面,就直接呼叫 scrollBox.AddObject(newButton);



帶一句題外話:從上面的兩個範例 procedure 裡面, 可以看出 FireMonkey 的座標與元件大小都可以有小數點, 但 VCL 不行。

要動態移除該元件的時候,也非常不同,VCL只需要呼叫該元件的 Free 方法即可:
procedure TForm1.btnRemoveBtnClick(Sender: TObject);
var
    btn: TButton;
begin
    if self.ButtonList.Count > 0 then begin
        btn := self.ButtonList.Last;

        if Assigned(btn) then begin
            self.ButtonList.Remove(btn);
            btn.Free;
        end;
    end else begin
        ShowMessage('No Button');
    end;
end;
FireMonkey 則不一樣:
procedure TForm2.btnRemoveClick(Sender: TObject);
var
    btn: TButton;
begin
    if self.ButtonList.Count > 0 then begin
        btn := self.ButtonList.Last;

        if Assigned(btn) then begin
            self.ButtonList.Remove(btn);
            self.scrollBox.RemoveObject(btn);
            btn.DisposeOf;
        end;
    end else begin
        ShowMessage('No Button');
    end;
end;
在 VCL 裡面,呼叫了 Free 方法以後,obj 所佔用的記憶體就會在 Windows 系統裡面被釋放出來,但在 FireMonkey 當中,由於要適應絕大多數系統上的特性,所以 Free 方法只會把該元件的 Reference Count 減一,在 Android 跟 iOS, Mac OS 裡面,一個元件要被系統釋放掉的話,必須要 Reference Count 等於 0, 才會真正把元件刪除掉。

所以 RemoveObject 被呼叫以後,obj 元件會在畫面上看不見了,但它還沒有真的被刪除,在 FireMonkey 當中,我們可以呼叫 obj 元件的 DisposeOf 方法,這個方法在Windows 平台上,會有等同於呼叫了 Free 方法的作用。

但在 iOS, Andorid, Mac OSX 上面, 則會強制把 Reference Count 遞減為 0, 真正強制的把佔用的記憶體釋放出來。

結語
許多已經很習慣於傳統 Delphi 或 傳統 Windows 應用程式設計的 Programmer, 會很習慣於把所有的畫面都放在同一個 Form 裡面。

在傳統的 Windows 或桌機型的應用程式環境當中,這是沒有問題的,但在行動裝置或者嵌入式裝置的應用程式當中,這是行不通的,因為在行動裝置當中,記憶體極其有限,有時只要使用超過了 40MB 的記憶體,應用程式就會被強制停止,也就是俗稱的閃退。

大家或許會覺得,40MB 很少,或者自己的應用程式不會用這麼多,但請大家記得一個數字,一張 2048x1536 的圖片,就可能佔用了約 10-15MB 的記憶體。

為什麼我提到 2048x1536 這個數字?

因為它正是 iPad Retina 螢幕的實際解析度,包含 iPad 3, 4, 5 (iPad Air), 以及 iPad Mini 2, 都是這個解析度,我們一旦有這個解析度大小的應用程式,只要兩張全螢幕的圖片,就已經處在危險邊緣。

如果還像以往的寫法,把所有的圖片、畫面都放在同一個 Form 裡面的話,只要換個兩頁,應用程式就會閃退,這種應用程式連上架的機會都沒有,所以,動態產生、動態刪除元件或畫面的需求將會越來越大,大家也一定要實際學會怎麼處理這種情形,不然前路絕對是坎坷不平的。

動態產生範例 (VCL & FireMonkey)

Delphi 用 for..in 迴圈釋放物件的注意事項 (XE5, XE6)

從 Delphi 2009 開始, 新增了不少原本 Object Pascal 沒有支援的語法, 包含了我還蠻常用的 for-each 迴圈. (在 Perl, obj-c 我都還蠻常用這個功能的)

為了搭配動態產生元件, 用 for-each 迴圈來處理釋放動態新增的物件, 在 Delphi XE5 分外重要,因為在 XE5 當中,我常用 TVertScrollBox 這個元件來處理需要動態新增、高度不確定的 Contents.

舉個大家最常見的例子來說,即時通訊軟體的對話內容,內容會有多少?誰也說不準,可能跟朋友A的對話內容有一千則,但跟不熟的路人甲則只有三則。

這時候,要用什麼元件來顯示這些對話內容呢? 從 XE2 開始,大多數的範例程式都是用 TListBox 或者 TListView 來載入這些資料,TListBox 跟 TListView 也很好,但這兩個元件的內容呈現比較制式,用來顯示通訊錄裡面的聯絡人非常適合,但要用來顯示對話內容的話,就不很合適了。

請回憶一下,現在大家都常用的通訊軟體,Line, What's app, iMessage 等等,都是以左邊顯示對方傳來的訊息,右邊顯示我方傳過去的訊息,底下會用個氣泡圖片來襯底,我們看一下 iMessage 的畫面:


要在 Delphi XE5, XE6 裡面製作完全相同的效果的話, 要怎麼做?

透過 TListView, 很抱歉, 我一時半刻創意不足, 想不出來, TListBox 倒是可以。

因為TListBox 的 Items 是 TListItem,建立TListItem 出來以後,可以用 AddObject 這個方法來把圖片 (TImage), 文字 (TLabel) 加到 TListItem 裡,所以也可以製作出像上圖的畫面,但如果要在上圖的畫面上顯示圖片呢?現在即時通訊軟體都流行貼圖,這種功能要怎麼在 TListBox 做出來呢?

抱歉,我又創意不足,一時技窮了。

這時候,大概也只有 TVertScrollBox 可以用來製作這樣的畫面效果了。

TVertScrollBox 跟 THorzScrollBox 都是從 TScrollBox 繼承而來的,從名稱來看,TVertScrollBox 是垂直方向的捲動視窗,而 THorzScrollBox 則是水平方向的捲動視窗,所以要製作上圖的畫面的話,就可以用 TVertScrollBox 來製作。

讓我寫一段簡單的 procedure 來示範怎麼把一個 TStrings 裡面的文字放進 TVertScrollBox 裡面去:

procedure addTextToScrollBox(info : TStringList);
var
   oneLine : string;
   currentY : Single;
   idx : integer;
   newLabel : TLabel;
begin
    currentY := 0.0;

    for idx := 0 to info.Count -1 do begin
         oneLine := info.Strings[idx];
         newLabel := TLabel.Create(self);
         newLabel.Position.X := 10;
         newLabel.Position.Y := currentY;
         newLabel.Text := oneLine;

         vScrollBox.AddObject(newLabel);

         currentY := currentY + 10.0 + newLabel.Height;  
    end;
end;

以上這段程式就可以把傳進去的 TStrings 的內容一行行顯示在名為 vScrollBox 的 TVertScrollBox 裡面去了,但這範例會有個小困擾,就是在對話內容要釋放,或者刷新的時候,需要把所有的資料全部清除,在 XE6 底下,可以用以下這個 procedure 來達成:

procedure clearScrollBox;
var
    obj : TFMXObject;
begin
    for obj in vScrollBox.Children do begin
         vScrollBox.removeObject(obj);
         obj.DisposeOf;
    end;
end;

在行動平台中,請記得除了透過 vScrollBox.removeObject(obj); 把 obj 從 vScrollBox 的可視範圍移除以外,還要呼叫 obj.DisposeOf; 這樣 obj 才會實際消失,不然的話,雖然obj 當時是看不見的,但卻也還存在記憶體當中沒有沒事放掉。

視覺效果上,我們會看到 vScrollBox 內容空無一物,但捲動軸卻不會消失,這就會讓使用者覺得很納悶了,所以記得,除了RemoveObj之外,還要呼叫 DisposeOf 把物件是放掉。

如果是 XE5 的話, TVertScrollBox 的 Children 就不只存放我們透過 addObject 放進去的物件,我實測的結果,會連同捲動軸也列名在 Children 裡面,所以得先判斷其 Class 是否是我們要釋放的,因此要加入 Delphi 的 RTTI 功能,改為:

procedure clearScrollBox;
var
    obj : TFMXObject;
begin
    for obj in vScrollBox.Children do begin
         if (obj is TLabel) then begin
             vScrollBox.removeObject(obj);
             obj.DisposeOf;
         end;
    end;
end;

不然也可能會把捲動軸弄消失,但這一點在 XE6 以後的版本應該不用再擔心了,真是太好了。

2014年7月12日 星期六

FireMonkey - 行動裝置上虛擬鍵盤的出現與隱藏

在 Win32 的應用程式開發中, VCL 架構當中, 我們常常會在畫面上有多個 TEdit 或 TMemo 的時候, 設定這些元件的 TabOrder, 用以決定每個文字編輯元件的順序, 待使用者輸入文字後,按下Tab鍵,便可切換到下一個文字輸入元件。

在行動裝置上卻沒有所謂以Tab鍵進行文字輸入元件切換的功能,要不是在虛擬鍵盤或手寫輸入控制元件上緣有上一個、下一個的按鍵進行切換,就會直接在鍵盤或手寫輸入框裏有換行、完成的按鍵,待使用者按下之後切換到下一個輸入的文字框去。

不僅如此,在行動裝置上輸入文字時,還會因為虛擬鍵盤的出現,需要把畫面整個向上挪移,才不會有文字框被虛擬鍵盤擋住,使用者輸入了什麼都看不見的窘況。

在 FireMonkey 的 TEdit,TMemo,就是扮演文字輸入框的角色,然而遍尋這兩個元件的所有Event,卻也找不著要如何達成前述切換文字輸入框的相關事件,更別提要像obj-c提供個像是 textFieldBeginEdit 這樣的Delegate method來偵測虛擬鍵盤的出現了。

雖然在TEdit裡面有onChange,onKeyUp,onKeyDown,onTyping這些事件,但這些事件卻無法接收到虛擬鍵盤的Return被觸發的事件,當然Windows系統上的Return可以測得,但行動裝置的不行,所以很令人煩躁。

然而,在FireMonkey 中,還是能夠達到前述功能的,只是要多花一些功夫,稍微多幾個步驟就是了:

KillFocusByReturn
這個屬性(property)是 TEdit 的新屬性,在設定接受到虛擬鍵盤的 Return 值時,是否要停止編輯,並讓目前的TEdit 失去Focus,這個屬性預設值是false,我們要把它改設定成true。

這樣一來,按下虛擬鍵盤的Return時,虛擬鍵盤就會自動縮回去了,也就是該文字框完成了編輯。

 

















































































TEdit 與鍵盤相關的新屬性
                        
TEdit 的事件


onEnter 事件
在每一個TEdit的事件表裡面,都有 onEnter 跟 onExit 這兩個事件,可以偵測到行動裝置上,該欄位是否開始/結束編輯了」

我們得自己計算一下每個TEdit 進入編輯模式時,該TEdit會不會被擋到?以iPad 為例,iPad的虛擬鍵盤(橫向)高度約為250,所以我會在橫向畫面最底下的TEdit 的 onEnter 事件當中設定一個畫面座標向上挪移250的動作:

procedure THeaderFooterForm.Edit1Enter(Sender: TObject);
begin
   shiftY := -150;
end;

設定了-150 的Y軸位移量之後,接著要偵測虛擬鍵盤出現與消失的事件:onVirtualKeyboardShow, onVirtualKeyboardHidden 這兩個事件,別急,看完再去寫 code。

Form 的虛擬鍵盤顯示/隱藏事件

onVirtualKeyboardShow, onVirtualKeyboardHidden 這兩個事件並不是歸屬給單一 TEdit 或TMemo 的哦,它們是歸屬於 TForm 的事件。

所以,我們接下來要先點選 TForm 元件,找到 onVirtualKeyboardShow, onVirtualKeyboardHidden 這兩個事件,雙擊它們,來製作出處理這兩個事件的事件處理常式。

如果Form 的畫面是由一個 align 設定為全螢幕(alClient)的TTabControl 元件所構成的,則在 onVirtualKeyboardShow 這個事件裡,我們要先把 TTabControl 元件的 align 屬性改設定為 alNone,否則修改其Position.Y 就不會有任何效果。

出現虛擬鍵盤
輸入完畢以後, 恢復原位














接著,把TTabControl Position.Y 設定為 ShiftY,就會看到畫面向上移動,露出正在輸入當中的 TEdit 或 TMemo 了。

最後,onVirtualKeyboardHidden 的時候,只要把 Position.Y 設定為 0,畫面就會恢復正常了。


心得分享:每一個文字輸入框的 ShiftY 都是獨立的,不用去管其他文字框的位移量是多少,彼此並沒有交互作用。


範例程式: 下載點

2014年6月30日 星期一

FireMonkey 桌面應用程式最小化

問題 - 視窗縮小時不會整個應用程式縮小

在製作 Delphi 或 VB 程式時, 常常會使用多個 Form, 在每個 Form 的畫面當中, 會賦予不同的功能, 但每個 Form 都會被當成獨立的畫面, 也就是 MainForm.

我們對 MainForm 的預期, 就是在按下右上方的縮小鍵時, 整個應用程式縮小到工作列上, 按了工作列上的按鈕時, 整個程式畫面還原回來原來的狀態.

但從過去 VCL 的時期到現在的 FireMonkey, 都並沒有這麼容易, 多個 Form 的時候, 只要不是按下 MainForm 的縮小按鈕, 視窗都會縮在桌面左下角。

視窗沒有縮到工作列上, 而是成為縮小視窗
以往這種 Form 的特性, 是為了製作 MDI Application, 也就是一個大的 Form 當中有多個文件會被開啟, 就像 Word, Powerpoint 那樣, 可以開啟多個文件, 讓每個文件有獨立的小視窗用來檢視文件。

以往在 VCL 架構的解決方法

在 VCL 架構的應用程式中, 解決這個問題的方法, 是在任何一個 Form 裡面建立一個 onMessage 的事件處理常式, 並在 FormCreate 事件中把它指派給 Application.OnMessage 事件:

procedure TForm1.HandleMessage(var Msg: TMsg; var Handled: Boolean); 
begin
   if (Msg.Message=WM_SYSCOMMAND) and (Msg.wParam=SC_MINIMIZE) then
     Application.Minimize;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   Application.onMessage := self.HandleMessage;
end;

當我們發現 SC_MiniMize 事件發生, 就直接執行 Application.Minimize, 整個應用程式就會縮到工作列上了, VCL 上面如此, FireMonkey 卻不然...... 時至今日, 網路上的各個論壇還在尋找著跟 VCL 架構當中的 Application.onMessage 對應的事件...... 然而, 我也找不到, 但是在 FireMonkey 當中, 有很多玩法跟 VCL 不相同, 花了一陣子搜尋 XE5 裡面的 RTL 原始碼, 找到了這個玩法, 有效, 簡易, 跟大家分享。

範例-製作兩個 Form 互相切換

先建立一個 FireMonkey 應用程式, 包含兩個 Form, Form1 跟 Form2, Form1 是 預設的MainForm, Form2 則是一般的 Form.

Form1, 放一個 button 切換到 Form2
Form2, 放一個 button 切換到 Form1













Form1 上面的 Button1Click 內容如下
procedure TForm1.Button1Click(Sender: TObject);
begin
   form2.Show;

   self.Hide;
end;

Form2 上面的 Button1Click 內容如下
procedure TForm2.Button1Click(Sender: TObject);
begin
   form1.Show;

   self.Hide;
end;

這是最常見的寫法, 然而切換到 Form2 的時候, 按下視窗上的縮小按鈕, 就會跟本文最上方的圖片所顯示的一樣:

這時候, 只要在 Form1 的 Button1Click 加上一行:
Application.MainForm := form2;

就可以把 Application.MainForm 從 form1 切換成 form2, 此時再按 form2 的縮小按鈕, 就不會再出現縮小的視窗了耶, 超神奇, 兩個 click 事件處理常式如下
修改後, Form1 上面的 Button1Click 內容如下:
procedure TForm1.Button1Click(Sender: TObject);
begin
   form2.Show;
   Application.MainForm := form2;
   self.Hide;
end;

修改後, Form2 上面的 Button1Click 內容如下
procedure TForm2.Button1Click(Sender: TObject);
begin
   form1.Show;
   Application.MainForm := form1;
   self.Hide;
end;

結語

FireMonkey 上面許多用法都很神奇, 由於所有元件都有 Container 的特性, 所以可以直接收納其他元件, Application.MainForm 也不是唯讀屬性, 所以可以在切換 form 的時候順帶把 Application.MainForm 做重新指派, 雖然很難想到這個用法, 不過它確確實實的有效, 程式碼也簡潔, 所以推薦給大家使用.

範例程式連結

2014年6月28日 星期六

FireMonkey 分享 - TImageButton 的製作

緣起

在以 VCL 撰寫桌面應用程式的時候, 常會遇到一個狀況, 就是需要讓某個按鈕用美術人員設計的圖片當成底圖, 而且該按鈕還要有四種狀態:

  1. Normal (一般狀態時)
  2. HighLighted (滑鼠游標進入按鍵區域時)
  3. Pressed (滑鼠按下時)
  4. Disabled (設定為不能點選時)
這樣的按鈕, 在過去 VCL 的時代, 有 TImageButton, TBitmapButton 等各種第三方元件可以使用, Delphi 內部也有一兩個內建的元件 (TBitButton? 不知道我有沒有記錯) 可以讓我們把圖片放上來, 然後設定一張圖片 (或多張) 給該按鈕不同屬性作為顯示圖片.

VCL 時代, 由於對圖片格式的支援, 在每一版的 Delphi 都不太一樣, Delphi 6 好像預設只有支援 Bitmap, 如果要使用 Jpeg 格式的圖片, 就得自己在 use 的區塊輸入 Jpeg 這個 unit 的名字.

如果要使用 Png 的話, 在 2005 年前後則有了 TPngObject 這個第三方工具可以使用, 但還是必須由程式人員自己去找到、引入才行.

所以我在 2005 年也自己寫了 TPngImageButton 這個 VCL 元件, 在 TButton 元件的屬性中加入了四種狀態的圖片, 可以在設計階段 (Design Time)把 Png格式的圖片設定好, 並存在 dfm 檔案裡面.

但當時只想著直接用圖片來解決一切, 並沒有想到有一天在處理多國語系的時候, 裡面還要多放個 TLabel, 好讓自己處理多國語系的介面文字顯示時能夠簡單一點, 這一切, FireMonkey 都已經處理好了.

元件容器的功能 (Container feature)

在 VCL 架構的應用程式裡面, 如果我們要讓兩個 VCL 元件結合在一起, 並且在 Design Time 的 Form 上面, 則其中一個必須具備包容其他 VCL 元件的功能,在 VCL 的元件當中, 並非全部都具備此一特性, 僅有以下幾個元件有元件容器的特性:

  • TPanel
  • TPageController (其中的各個分頁)
  • TGroupBox

很明顯的,TButton 並不屬於其中一員,所以,要在TButton裡面放上一張圖片,並在Design Time進行各項屬性的調整、設定,在 VCL 版本的 TButton 是無法達成的,真的需要這樣的Button。我們只好新建一個 VCL 元件,繼承 TButton,然後 implement 各項 TButton 的屬性與方法,然後再透過 dpk 與 bpl 把這個新的 VCL 元件安裝到 VCL 元件盤 (component plate), 這樣就能達成 VCL 的 TImageButton 了,但這篇分享文章的主題不在 VCL,所以要跟各位說聲抱歉,如果有朋友需要我的TPngButton的話,下次有機會再把該元件分享給大家。

FireMonkey 的所有視覺化元件元件都是 Container

元件容器的功能,在容納其他的元件,VCL 並非所有元件都具備此功能,但在FireMonkey 就完全改變了這一點,FireMonkey 的所有視覺化元件都具備 container 的功能。

換句話說,我們可以從Delphi 的IDE環境左上角的 Structure 畫面,任意把任一元件拖拉放到其他元件上,立刻就能把多個元件結合起來,成為一個新的元件,不需安裝,也不像 bpl 得在功能有所變動的時候就得重新編譯、安裝,當然,如果您想要把這個複合的元件建立成一個新的元件放到元件盤上面去,也是做得到的。

步驟一:拉一個 TButton 到 form 上面

從元件盤上找到 TButton (如果您對 Delphi 還不熟悉,請從 Delphi 畫面右下角找到元件盤,在搜尋框裏輸入 button, 就會顯示很多名稱當中有 button 這個字詞的元件, 第一個符合條件的就是 TButton),找到以後,把它拖拉到您的 form 上面任意的地方。

從元件盤進行關鍵字搜尋

不麻煩的話,幫您的元件取個容易看出其用途的名字吧,一堆 Button1,Button2的按鍵很容易讓您的程式失去可讀性,也增加了維護的難度。

步驟二:拉兩個 TImage 到 form 上面

從元件盤裏拉兩個 TImage 元件到您的 form 上面來。

請注意,名稱裡有 Image 這個字詞的元件還不少,我們只要 TImage,不要 TImageControl 哦,TImage 在 FireMonkey 的元件盤裡,被歸類在 Shapes 裡面,在 Common 跟 Advance 分頁裡面是找不到的。

從元件盤上面搜尋 TImage 元件

設定圖片
TImage元件拉好了以後,請從左下角的屬性檢視視窗(Object Inspector) 設定要顯示的圖片:
幫 Image 元件設定要顯示的圖片
點擊屬性檢視視窗裡面的 MultiResPicture 欄位,裡面會出現一個小按鍵,點擊它,就可以選擇要放進 TImage的圖片檔案了。

選擇要放在 Image 裡面的圖片檔案
FireMonkey 所支援的圖片格式很多,我們最常用的是 Jpeg 跟 Png 這兩種格式,請自行審酌您要用哪一種。

設定 Tag
Image 拉進來之後,要不要給他們命名?不重要,因為在這個範例裡面,我們用來判別圖片的依據是tag,而不是 Image 的name。

請把要設定為一般狀態的 Image 元件的 tag 欄位填為 1,要設定為被點擊 (Pressed) 狀態的 Image元件的 tag 欄位填為 2。tag 欄位一樣可以從左下角的屬性檢視視窗找到,所有元件的 tag 預設值都是 0. (請參照上圖的下半部)

Tag 是物件導向程式設計當中相當重要的一個概念,當同一個 Class 在 RunTime 被建立出多個實體 (Instance),這些個實體是無法事先命名的,此時,Tag 就可以用來分辨到底是哪一個實體被點擊或被觸發了事件,在這個案例裡面,也會得到練習。

圖片沒有填滿整個 Button?
如果您的圖片沒有填滿整個Button的區域,此時上下左右一定有些空白區塊。請把 Image 設定成拉伸填滿即可,這個屬性是ImageWrapMode,在屬性檢視視窗的最後一個,請將它設定為 iwStretch,預設是 iwFit,只會上下拉伸或左右拉伸。
請選擇 iwStretch, 讓圖片能變形填滿整個 Image 的區塊

步驟三:把 TImage 拉進 TButton 裡面

聽起來很玄吧? 要怎麼把一個元件拉到另一個元件裡面?

從 Delphi 2005 以後,Delphi 的 IDE畫面就是現在這個模樣,左上角的視窗叫做 structure,也就是結構。我們可以從這個結構視窗裡面任意拉動元件,在這裡拉動元件,會改變元件的從屬關係,我們在結構視窗裡面把剛剛的 Image1 拖放到剛剛的 Button 元件上,看看發生了什麼事?
結構視窗拖拉元件的畫面 (拖拉中)
Delphi 當掉了,沒有回應!!! 這是 Delphi 做這個動作時還蠻常發生的事,在結構視窗裡拖拉元件前請先記得存檔啊!

正常情況下,Image1 會變成 Button 的子元件,如同畫面上的樹狀結構所顯示的。接著請把 Image2 也拉進去 Button 裡面。

這時候,您可以看到Button的小小範圍裡面出現了兩張圖,這兩張圖還重疊呢。
先別急,就是要它們重疊。先把兩張圖的 align 屬性 都設定成 alClient,也就是讓它們都佔滿整個Button 的顯示範圍。

align 屬性,請從屬性檢視視窗中尋找,應該會是 Image 元件的第一個屬性,您點擊其右半部,就會出現下拉式選單讓您選擇,找到 alClient,點擊它即可完成設定。

把 Image 的 align 屬性設定成 alClient

迫不及待想執行看看嗎?就執行吧,這時候的Button只會顯示最上面那張圖片,會是哪一張?我也不知道,端看您拖拉Image到Button裡面的時候,哪一張圖片在比較前方,這無法用直覺操作來設定,需要透過滑鼠右鍵的功能選單來設定。別急,距離完成還有幾個步驟。

先從結構視窗找到 Image2,把它的 Visible 屬性設定成 false,此時在IDE當中並不會有任何改變,這些屬性的變化只會在RunTime 反映在介面上。您可以按 F9 執行看看。

到了這裡,我們的Button上面有圖片,但對於點擊還沒有畫面上的反應。

步驟四:設定 TButton 的 OnMouseDown 跟 OnMouseUp 這兩個 eventHandler

接著,我們要來寫程式了!

請點擊 Button,並從元件檢視視窗中選擇 events 分頁,這裡會列出所有可以直接處理的事件,例如 onClick,onDblClick 等等。

請找到 onMouseDown 這個事件,目前它在視窗右半部是空的,請雙擊它。這樣就能讓 Delphi IDE 幫我們產生一個 event handler (事件處理常式),畫面也會切回程式碼編輯視窗了。

在 onMouseDown 的時候,我們要讓有按壓效果的圖片顯示,隱藏其他所有的圖片,但位於 Button 內部的圖片最好別每個元件的 name 都寫在程式碼裡面,不然,如果有 100 個按鍵,豈不是要寫 100 個 eventHandler?! 那可要人命了。

所以我用一個迴圈來解決,程式碼如下:
procedure TForm1.btnCheckInMouseDown(Sender: TObject;
   Button: TMouseButton; Shift: TShiftState; X, Y: Single);
var
   enumObj: TFMXObject;
   clickedBtn: TButton;
begin
   clickedBtn := Sender as TButton;

   for enumObj in clickedBtn.Children do begin
      TImage(enumObj).Visible := (enumObj.Tag = 2);
   end;
end;
 
procedure TForm1.Button1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
var
   enumObj: TFMXObject;
   clickedBtn: TButton;
   selectedIdx: integer;
begin
   clickedBtn := Sender as TButton;

   for enumObj in clickedBtn.Children do begin
      TImage(enumObj).Visible := (enumObj.Tag = selectedIdx);
   end;
end; 
從上面的程式碼,可以看到,eventHandler 的參數是 Sender,型別是 TObject,這是 Delphi 的標準 eventHandler 宣告,在執行時,Sender就會是該事件被觸發的元件,例如我們有個最簡單的 onClick eventHandler,當它被呼叫時,Sender 參數就是被點擊的那個Button,也可以是被點擊的TLabel,或任何對該eventHandler進行了binding 的元件。

因此,雖然 Sender 的型別是TObject,但實際上可以轉型成 TButton,TLabel,當然也可以是 TImage。不過剛剛請您點選的是 Button 的 onMouseDown 事件,所以這裡的 Sender 是一個 TButton,不過也要請您留意,直接在 form 上面點元件的話,還是很可能點到放在 Button 裡面的 Image 哦,要小心,從結構視窗點擊比較準確。

第一行,我就把 Sender 轉型成 clickedButton,透過 as 語法,Sender 會被《當成》TButton,存放在 clickedButton 這個變數名稱中。

在 TFMXObject 裡面,都有個通用的屬性:Children,用來存放該元件所內含、收容的所有子元件,所以我們也能從這裡面找出剛剛拉進去的兩個 Image。

上頭的程式碼裡面,我用了for..in 迴圈,這是Delphi 2009 以後加到 Object Pascal 語言裡面的新語法,可以列舉某個 container 元件或資料結構裡面的所有內容物,進入到 for..in 之後,我們所取得的名為 enumObj 的元件,就是被收納在 clickedButton.Children 裡面的每個元件。

把元件一一找到了,接下來得分辨哪些是圖片,然後把按壓效果的圖片的Visible 屬性設成 true,其他的都設定成 false,這樣就能做出按鍵被壓下去的視覺效果了。

但是要先找出型別是 TImage 的元件,然後才設定其 Visible 屬性?還得多做一次判斷,這時候我們直接檢查 tag 就行了,前面提到過,預設的元件 tag 都是 0,只有我們拉進去的兩張圖片 tag 是 1跟2,所以超容易判斷,tag 等於 2 的元件, Visible 就是 true,其他的都是 false,夠簡單吧?

步驟五:完成一半了!來讓 image 不干擾 Button 的位置拖拉 (Design Time)

完成至此,Button在執行時期 (RunTime) 的效果大致完成了,但有時候介面設計的同仁或美術設計的同仁總是會天外飛來一筆,更改介面設計!

如果只是改張圖片,那算簡單的,如果位置、規則流程全變了,也是常有的事,但目前的Button還沒辦法直接用拖拉的方式調整,您試著拉拉看,現在應該只會拉到圖片,而且因為我們把圖片的align 屬性設定成 alClient 了,所以根本連圖片也拉不動了,如何是好?

在 FireMonkey 的元件當中有一個新的屬性,稱為 Locked,預設值是 false,也就是允許拖拉,我們現在把 Image的 Locked 屬性設定成 true,就可以把 Image 鎖定在現在的位置,可以讓我們在DesignTime 拖拉時,把拖拉的事件直接轉給父元件,這麼一來,就變成在拖拉 Button,而 Image 就變成了一張不會動彈的底圖了,很棒吧?!

設定 HitTest 跟 Locked.

別忘了,兩個 Image 元件的 Locked 屬性都設定為 true,這樣在 DesignTime 的拖拉動作,就可以直接拖拉 Button, 而不會誤拉到 Image 了.
正常狀態
點擊時狀態

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

步驟:讓底圖的點擊事件不干擾到 Button 的點擊事件

HitTest 這個屬性, 也是 FireMonkey 元件特有的, 這個屬性設定成 true 的時候, 該元件就會取得滑鼠點擊的事件, 我們現在不希望底圖有被點擊的機會, 以免干擾 Button 的所有事件, 所以要把兩個 Image HitTest 設定成 false,  這樣就可以把 Image 變成完全不會干擾 Button 事件的底圖了。

步驟:加個 TLabel,以不時之需

有時按鈕上的圖片只能是底圖的呈現, 如果按鈕上有需要顯示文字, 最好用 TLabel 來顯示, 這樣一來如果有多國文字要呈現, 才能用一套圖片, 以及準備好的多國文字直接完成多國語系的需求。

首先, 我們先重複前面幾個步驟:
  • 拉一個 TLabel 到 Form 上面
  • 把 TLabel 拉進 Button 元件裡面
  • 把 TLabel 拉到 Button 裡面適當的位置
  • 設定 TLabel 的 Tag 為 11
  • 修改 Button 的 onMouseDown 跟 onMouseUp
  • 設定 TLabel 的 Locked, HitTest 為 false
以下就是修改完成的 onMouseDown, onMouseUp 這兩個 eventHandler 的程式碼, 請注意, 這次加上了 Tag > 10 以上的元件也都要顯示喔, 至於是否要轉型成 TImage, 這段程式沒有影響, 因為並沒有直接存取到 enumObj 的屬性或方法, 只有對所有元件共通的 Visible 屬性進行修改, 所以不至於會造成 Access Violoation.

如果有要對其他屬性做修改的話, 記得要適當進行轉型後才行.

procedure TForm1.Button1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
var
   enumObj: TFMXObject;
   clickedBtn: TButton;
begin
   clickedBtn := Sender as TButton;

   for enumObj in clickedBtn.Children do begin
      TImage(enumObj).Visible := (enumObj.Tag = 2) or (enumObj.Tag > 10);
   end;
end;

procedure TForm1.Button1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
var
   enumObj: TFMXObject;
   clickedBtn: TButton;
   selectedIdx: integer;
begin
   clickedBtn := Sender as TButton;

   if clickedBtn.StaysPressed then selectedIdx := 2
   else selectedIdx := 1;
   for enumObj in clickedBtn.Children do begin
      TImage(enumObj).Visible := (enumObj.Tag = selectedIdx) or (enumObj.Tag > 10);
   end;
end;

完成這幾步以後,Label 就成為了 Button 裡面的一員,我們的 Button 裡面就可以顯示文字了.
 

步驟:完成了,如何在同一個 form 裡面複製這樣的 Button?

只要在結構視窗裡面, 先點選已經做好的 Button, 按下 Ctrl + C, 就像我們在 Windows 的其他任何的編輯器裡面進行複製一樣。

 接著點選我們要放置這個 Button 的元件, 例如在不同的 TabItem 裡面, 或者另一個 TRectangle 裡面, 點選好以後, 我們按下 Ctrl + V, 就像我們在 Windows 的其他任何編輯器裡面進行貼上的動作一樣

Button 就會被貼在該元件上面了, 這時候我們就可以編輯 Label 的文字內容, 拖拉 Button 的位置到適當的地方去, 跟一個原生的 TButton 元件一樣

總結

透過本篇的介紹,和大家分享了如何在 FireMonkey 架構下製作出內含有圖片、Label 的按鍵,在這個過程中,大家也一併了解、認識了幾個 Delphi 的重要概念:

  • FireMonkey 的所有視覺元件都是 Container, 具備包含其他元件的能力
  • FireMonkey 元件的 Locked 與 HitTest 這兩個屬性的特性與用途
  • Delphi 的 for..in 迴圈寫法
  • 元件當中的 Tag 屬性對於 Delphi 元件的重要性
  • Delphi 元件的 as 用法
  • 如何改變 FireMonkey 裡面的 TLabel 字體、大小,以及用RGB色碼改變 TLabel 的顏色

自我練習

本篇的介紹不一定適用於所有情況,有些時候Button 會因為美術人員的設定、企劃人員的規劃而有許多呈現上的變化,這時候大家就需要發揮創意對本篇的概念做一些微調,大致上可能有以下這幾種變化:

Label 的位置不一定正好在 Button 位置的正中間,需要跟畫面上的其他小圖示有相對位置的設定。
加上 MouseOver 的圖片效果時,要怎麼實作?
除了主要的圖片外,還有位置可能變動的小圖示,要怎麼處理?

請發揮您的想像力,想想上述這幾個狀況要如何修改程式或元件設定才能達成需求?

2014年6月26日 星期四

Delphi 浴火重生, 火猴的逆襲已經來臨

楔子

自 FireMonkey 推出以來, 漸漸燃起了從 2000 年以來這十年間, Delphi 程式人員對於 Delphi 的信心。從 2000 年 Anders Hejlsberg 琵琶別抱, 為微軟量身訂做了.NET 平台與 C# 程式語言之後, Delphi 的聲勢江河日下, 再加上 Borland 的策略失準, 以行銷為導向, 失卻了 2000年以前 Delphi 所自豪的穩定性與效能, 曾被稱為 VB Killer (VB 殺手) 的 Delphi, 在 2000-2010這個十年之間, 完全失去了主要程式開發工具的地位。

然而, 在歐洲各地的 Programmer 們並沒有受到 Delphi 崩壞的影響, 在俄羅斯、義大利、法國,Delphi 在程式開發的這個圈圈裡, 仍舊有許許多多的人在努力著, 從 2001 (年份我可能記得不清楚了) 的 Delphi 8, 到之後的 Delphi 2005, 2006, 2007, 2009, 2010, XE, XE2, XE3, XE4, XE5, 到 2014 年的 XE6, 各式各樣功能的第三方 VCL 元件不斷在全世界被創造、散佈,使用在各種場合與應用裡.

Tnt 元件

在 Delphi 2009 發佈之前, 所有 Delphi 的元件與 IDE 並沒有支援 Unicode 的字元, 也因此從 Delphi 5 開始, 就有兩三種內建的工具讓程式開發人員可以製作出 Resource 檔案, 用來儲存 Form 當中的各種多國語言的內容, 然而, Unicode 才是王道, 沒有 Unicode, 多國語系真的是個遙不可及的夢想。

Tnt系列元件, 是提供尚未支援 Unicode 的版本的 Delphi (Delphi 2009 以前的所有版本)對應的 VCL, 從 TLabel, TEdit, 到 BDE 的絕大多數元件, 都有對應的 Unicode 版本元件, 對應上來就是 TtntLabel, TtntEdit 等等。

在提供多國語系的應用程式上, 或者處理跨國企業的資料庫時, 都對程式人員提供了非常多的幫助, 感謝 Tnt 元件的開發者.


Indy 網路元件組

從 1999 年的 WinShoes, 到 2000 年開始的 Indy 7, Indy 8, 一直到目前搭載在 Delphi XE5 的 Indy 10.60, Indy 陪伴著 Delphi 開發人員走過了將近 20 個年頭, 從 Client, Server, Protocol, 各式各樣的功能, 讓開發人員處理網路相關的程式功能更不費力.

目前 Delphi XE5 當中的很多官方元件, 例如 DBX 系列元件的底層, 也都是以 Indy 作為其基礎, 因此如果我們遇到 DBX 系列元件發生了什麼問題, 也就可以從 Delphi 內附的 RTL 或 VCL 程式原始碼來抓出到底是那兒出了問題.


跨平台的努力-FireMonkey

從 Delphi 2009 把 Unicode 功能完完整整, 裡裡外外的融入了 Delphi 的 IDE, 編譯器以後, 我們現在已經可以在程式裡面用中文變數, 甚至各國文字作為變數, 要怎麼處理多國語系, 都不是問題.

但 2009 年的時空, 已經不是 Windows 桌面應用程式的天下, 就連網頁程式也已經是小學生就能提供的東西, 因此, Delphi 的下一版, 除了把 64 bit 編譯器做出來, 讓效能能夠更好, 接著就是朝向跨平台的目標前進.

在 1993-1995年間, JAVA 的概念與程式標準開始被 SUN 提出, 每個新的產品都有其行銷的口號, JAVA 當時的口號就是: Once Compile for All Platforms. (編譯一次, 所有平台都可用)

這個口號非常美好, 但大家可以在後來的桌面應用程式跟網頁程式當中得知, 這個口號又是另一個夢幻泡影. (如露亦如電, 應作如是觀 - 引述自金剛經)

JAVA 的 Once Compile for All Platforms, 是奠基在『所有硬體設備上面都會提供有 JAVA 虛擬機器 (本文後將簡稱為 JAVA VM)』的這個前提假設上, 然而, 並非每個硬體設備都有 JAVA VM, 且當時設計這個概念的人也沒想到 JAVA VM 的效能簡直是一場惡夢.

Delphi XE2 開始, 把這個概念作了一些些修正, 叫做 One Code for Multiple platforms (同樣一份程式碼, 能夠在多種平台上執行), 這個口號聽起來沒有很炫, 但卻實際多了.

從 Delphi XE2 開始, 沿用了 20 多年的 VCL 有了 32/64 bit 兩種版本, 另一組新的視覺元件庫: FireMonkey 也被推出, 透過 FireMonkey 元件組所撰寫的程式 (我們先討論一般桌面程式), 就可以直接編譯成 Win32, Win64, 以及 OSX (Mac 作業系統).

雖然要編譯三次, 在操作上多一些些步驟, 但編譯出來的程式, 在各個平台上都是原生程式碼, 不再透過像 JAVA 那樣高階的虛擬機器, 而能夠讓編譯出來的程式碼直接就是機器碼, 這效能的提升, 比透過直譯器達成的程式, 或者虛擬機器達成的程式, 都高出了不知道多少倍.

且 FireMonkey 所提供的介面, 在設計階段都是 Windows 的外觀, 但實際執行的時候, 在Windows 跟 Mac OSX 上面, 都完完全全是原生平台的程式外觀, 不用程式人員再花額外的功夫去處理佈景或圖片的問題了.

至此, 跨平台的功能頗有小成, 但請回想一下, 2009 年, 已經是 iOS 跟 Android 打的死去活來, Windows Phone 只能撿一些小屑屑, 市佔率幾乎達不到兩位數, 開發人員完全不會去想提供 Windows Phone 平台的應用程式.....

因此接下來的 XE3, XE4, XE5, XE6, Delphi 就花了更多的功夫在這兩個完全引領主流的作業系統上面, 到 XE5 算是有所成, 第一次能夠讓 FireMonkey Mobile 的同一份程式碼在 iOS 跟 Android 系統上面執行.


未來將何去何從?

這個題目太大, 即使問微軟說: 你們的 Visual Studio 接下來要提供什麼功能? 相信微軟的開發人員也會一下就楞住, 我相信 Delphi 跟 C++ Builder 也一樣.

目前 Visual Studio 2012 版, 提供了 C#, VB, VC 三種程式語言, 可以透過 .NET 開發網頁程式、Windows 應用程式, 以及透過一些第三方工具, 讓一部分程式能夠編譯成 iOS 能執行的 app (是透過像 JAVA 那樣的虛擬機器, 類似 Mono).

有了網頁, 桌面應用程式, 手機應用程式, 程式人員對 Delphi 的未來還有什麼期待?

未來是不斷在人們手中開創的, 2005 年的時空下, Windows Mobile 幾乎主宰整個智慧型手機市場, 靠的是 Compact .NET framework, 然而, 誰會知道 2007 年 iPod Touch 即將推出, 2008 年 iPhone 2G 與 App Store的推出會讓 XCode (Objective C) 在兩年內席捲全球? 2009 年 Android 系統跟 Eclipse 讓 JAVA 借屍還魂?

誰又知道 2014 年六月, Apple 會推出語法近似 JAVA 與 C#, 包含有完整物件導向概念, 原生相容於 XCode 所有 Framework 的新程式語言 Swift? (按筆者: 所有程式語言, 請從其概念來理解, 則只需要 Reference 文件就可以很快學會, 大約 1-2周吧)

未來, 永遠是軟體人員心中永遠的期許, 也是傷痛, 這兩個字代表著無限的學習, 永不停止.


對 FireMonkey 的不熟悉

FireMonkey 是一組全新的元件, 大多數一直使用 Delphi 的程式人員, 都有著陌生的感覺, 在台灣, 除了 Embarcadero 原廠的文件與代理商捷康努力的推廣, 請李維大師持續的在這塊領域上耕耘之外, 能夠說自己已經熟悉 FireMonkey 的人恐怕不多.

筆者從 Delphi XE5 使用至今, 剛剛有一些些熟悉, 未來也會把這些相關經驗在網路上分享出來, 期望透過交流與跟網路上的開發人員交互學習, 讓 Delphi 與 FireMonkey 能夠發揮出很好的功能, 讓 Delphi 的程式技巧能夠再次引領業界與學界的風氣, 在未來的某一天, 如果台灣能夠開始對軟體開發稍微正眼看待的時候, 讓 Delphi 與 C++ Builder 能夠成為程式人員前幾名的選擇.

Dennies
2014 年六月.