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 中,也有詳細提到關於旋轉螢幕的問題處理。值得參考。

10 則留言:

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後,問題變得簡單得多!
謝謝老師提供這個提示,讓我能往對的方向尋求答案!

โชติ ธรีะวิท 提到...

A perfect one ,I am using most of them .. Thankyou so much gclub

คนสวย2019 提到...

ฉีดเผาผลาญไขมัน




สิ่งใหม่





ฉีดลดแฟตที่ปลอดภัยรวมทั้ง







มีคุณภาพ



ที่สุดในโลกที่ศิลปิน



ฮอลลีวู้ดใช้ ยืนยัน









คำตอบ







กับการฉีดลดแฟตที่ดีเยี่ยมที่สุด









ได้ผล



เยอะที่สุด





ในโลกจากอังกฤษ



ยุโรป อเมริกาแล้วก็







กาหลีตัวยาที่ทรงพลัง





ที่สุดในตอนนี้




ฉีดสลายไขมัน
meso fat

張貼留言