2008年12月1日 星期一

記得要在程式中,處理鍵盤開啟或關閉的動作

Handle the open/close keyboard usage for your Android applications

之前寫了一個 Android 版的世界時鐘 (world clock) 程式,自認為應該沒什麼大問題了,就丟上網讓使用者開始下載。嗯,下載數是不少,心喜之餘,還是有幾個人反應,無法完成註冊的問題。

由於,當時 G1 手機沒有到手,只能用模擬機測,測了半天,一直無法重現使用者提到的問題。只好再檢視註冊相關的程式碼,一樣,看了不下十遍,還是沒看到什麼問題。終於,有天想到使用者要註冊時,應該要輸入使用者名稱及註冊碼,而實機不像我們在模擬機上,直接用 PC 的鍵盤輸入,他應該要打開鍵盤輸入才行。會不會,是這個問題的關係?

首先,第一個是做的是,要如何在模擬機上,模擬打開鍵盤這個動作?告訴你,你在網路上一定找不到這樣的資訊。今天就分享給你,這可是我嘗試了許久,才發現的秘訣。

告訴你,答案就是按下 PC 鍵盤上的 KEYPAD_7 or KEYPAD_9 鍵。疑,這不就是 如何以程式的方式,旋轉 Android 螢幕 文章中,提到的旋轉模擬機螢幕方向 (直立 <-> 水平) 的按鍵嗎?的確是,所有的文件都只說明,這是用來旋轉螢幕的按鍵,根本沒人提到,這也是模擬鍵盤打開或關閉的按鍵。

還來我才知道,在實機上,系統並不會自動隨著機器的旋轉,而自動旋轉螢幕方向。只有當你打開鍵盤時,系統才會自動將螢幕轉成水平。當你收起鍵盤時,才又將螢幕轉成垂直方向。

那這開啟或關閉鍵盤 (正確地,應該說旋轉螢幕方向) 的動作,為什麼會造成程式的問題呢?

原來,這螢幕的旋轉,是靠重新起動你的 activity (不是整個應用程式喔) 而達成的。蝦米,這是什麼意思?我發現,當螢幕準備要開始旋轉時,系統會先殺掉你現在的 activity,接著呼叫底層的顯示系統,旋轉螢幕的方向,最後才又重新啟動你的 activity。從使用者看來,只是螢幕轉個方向而已,什麼事也沒發生。但從程式面來看,這可是已經經歷過一次生死輪迴了。

我寫個簡單的程式,證明給你看。

首先,當你執行這個程式後,你會看到底下這樣的結果。

onCreate(null)
onStart()
onPostCreate()
onResume()
onPostResume()

可是當你按下 KEYPAD_7 ,將螢幕轉成水平的方向,你會看到底下這樣的結果。

onSaveInstanceState()
onPause()
onStop()
onDestroy()
onCreate(Bundle[{android:viewHierarchyState=
  Bundle[{android:views=
  android.util.SparseArray@43370a48}], key=123}])
onStart()
onRestoreInstanceState()
onPostCreate()
onResume()
onPostResume()

看到了嗎? onDestroy() 證明系統殺了你的 activity, onCreate() 則是又重新啟動你的 activity。

我原先無法讓使用者無法完成註冊的問題,就是有些 local variables 的值,因為這重起 activity 的動作,而不見了。

這個經驗,讓我學到了,千萬要有程式隨時會被系統殺掉的準備

有經驗的你,可能會發現為什麼 activity 上 EditText 內的文字,不會隨著旋轉螢幕的動作,而重設。翻翻 EditText 的原始碼,你會發現其實 EditText 也有特別針對這樣的行為來處理。所以說,如果你有寫自己的 custom widget 時,別忘了也要做類似的處理。不過, widget 的處理動作,和 activity 完全不同。有機會的話,再談這部份。

後記,在寫這篇時,又去 Google 了 一下,看到 Rotational Forces…On Your Android App 中,也有詳細提到關於旋轉螢幕的問題處理。值得參考。

8 則留言:

Michael 提到...

Hi, Sam, 好久不見,
其實Android introduction中就有說過
Android為了避免程式hang住的情況,
所以會有time out的機制,
這是為了避免某些大意的作者,
不過也造成了一點其他人的麻煩 XD

samlu 提到...

不好意思,我認識好幾位 Michael,請問您是哪位?

的確是,在 Activity Lifecycle 中,有提到記憶體不夠時,會殺掉你的程式。不過,連旋轉螢幕都會殺程式,倒是始料未及的。

Michael 提到...

我是以前NTD的Michael~
現在也正在摸Android @@
不過我的方向目前是想朝3D的方向
沒工作以後可以學的東西突然變多了
現在倒是有學不完的困擾 XD

samlu 提到...

3D? 聽起來不錯,是用 OpenGL 的東西嗎?寄信給我,可以一起聊聊 Android 的東西。

Indiana 提到...

老師您好,我查詢了一些網站和論壇,都找不到相關資訊,所以又來打擾了... 我想要在user在EditText裡輸入字串時,做即時檢查以過濾特殊符號:

edit.setOnKeyListener(new EditText.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// 檢查輸入字串...
return false;
}
});

用實體鍵盤輸入時,的確可以接收到訊息;但是在虛擬鍵盤上輸入時,沒辦法接收到訊息?請問要透過什麼方式可以擷取虛擬鍵盤keyin的訊息呢?謝謝指教!

samlu 提到...

Indiana,
要過濾輸入的文字,試試用 EditText.addTextChangedListener()

Indiana 提到...

謝謝您提供EditText.addTextChangedListener()這個方法...

這個方法如果是單一個EditText元件應該是沒什麼問題... 但我寫在自製的ListView時,把它寫在MyAdapter的getView()裡,結果發現他會add多次?好像是每次getView,就會add一次...

所以我把它寫在if(convertView == null){//這裡面},但卻沒辦法準確抓到ListView裡元件的index,只能抓到目前這個畫面的convertview的index...(這樣一搞也比較清楚一些convertview的概念了...)

我希望做成這種效果:
http://www.javaworld.com.tw/jute/post/view?bid=26&id=262311&sty=1#262311

試了各種如:ListActivity, setOnItemClickListener, onListItemClick...等相關方法,也好像都不能成功...

這問題卡好幾天了,查了一堆資料也沒多大進展...
繼續努力中!

Indiana 提到...

終於解決這個問題了...
我把EditText改成Button後,問題變得簡單得多!
謝謝老師提供這個提示,讓我能往對的方向尋求答案!

張貼留言