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 從編輯文字的元件上移除即可。

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