網頁

2010年6月23日 星期三

關於介面與執行序Only the original thread that created a view hierarchy can touch its views.

Android 是一個多功多執行的作業系統,雖然可以多執行緒
但是遵循著mvc模式 ,所以當非UI的執行序,嘗試去改變ui介面的程式就會出現錯誤而關閉程式
例如

寫了一個遊戲,遊戲的核心是一個執行緒,定期需要計算遊戲的內容,且需要定時的更新畫面,然而跟跟新畫面,需要呼叫 invalidate 這個方法,偏偏這個方法使會變更介面,所以android只好把你強迫關閉程式。告訴你Only the original thread that created a view hierarchy can touch its views

最簡單的方法就是不要使用 invalidate 改用 postInvalidate();

但只是剛好invalidate 有 postInvalidate(); 可以幫助更新畫面,但是如果要更變tTextBox內容,或是 setContentView(view); 那可就沒那麼幸運了。

事實上這樣龜毛的規則並不是Android訂製的,c#的開發使用者也是有著相同的困腦,
但在c# 會使用委派來將 要改變介面的事情 透過委派 交給 UI的執行序去執行。

然而在Android 使用了比較值覺得寫法
runOnUiThread(new Runnable(){
public void run() {
//要改變介面的程式寫在這裡
}});

這樣android就會裡面的程式碼,改由ui的執行序來執行,而不會出錯了,
所以類推本來寫postInvalidate(); 可以改寫成

runOnUiThread(new Runnable(){
public void run() {
invalidate
}});



通常錯誤如下:
06-23 15:47:40.123: ERROR/AndroidRuntime(393): Uncaught handler: thread Thread-10 exiting due to uncaught exception
06-23 15:47:40.135: ERROR/AndroidRuntime(393): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

2010年6月19日 星期六

Android 多點觸控

Android 的多點觸控以現在的api來看 最多3點觸控
於程式中加入
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
即可抓取觸控的座標以及事件
x[0] = (int) event.getX(0); //單點觸控的座標
y[0] = (int) event.getY(0);
x[1] = (int) event.getX(1); //第二點觸控的座標
y[1] = (int) event.getY(1);
x[2] = (int) event.getX(2);//第三點觸控的座標
y[2] = (int) event.getY(2);

可惜的是我拿得是 DESIRE 最多兩點觸控,第三點是沒有作用的,且有交叉誤判的現象

抓取事件
if (event.getAction() == MotionEvent.ACTION_MOVE )
{
這個事件在模擬與實機有很大的出入,模擬器只會移動一下觸發一次,而實機摸下去之後不管有沒有移動 都會瘋狂的觸發。
}
if (event.getAction() == MotionEvent.ACTION_DOWN )
{
指尖按下
}
if (event.getAction() == MotionEvent.ACTION_OUTSIDE )

if (event.getAction() == MotionEvent.ACTION_POINTER_1_DOWN )
基本上 如果使用了 MotionEvent.ACTION_DOWN 這個事件永遠不會觸發
if (event.getAction() == MotionEvent.ACTION_POINTER_2_DOWN )
第二點按下
if (event.getAction() == MotionEvent.ACTION_POINTER_3_DOWN )
{
第三點按下
}
if (event.getAction() == MotionEvent.ACTION_POINTER_3_UP )
{
第三點放開 兩點觸控的 也不會產生這個事件
}



好了看完很多疑問,為什麼point 1 23 的 move
兩點原因
1.不管是哪個點移動 都是觸發 move 旗標
2.按下去之後就瘋狂觸發所以乾脆三點都檢查


move up 1 2
要由一系列動作解釋

按下第一鍵
觸發 down

按下第二鍵
觸發 point 2 down

放開第二鍵 觸發
觸發 point 2 up

放開第一鍵 觸發 up (注意不是point 1 up)



再次實驗

按下第一鍵
觸發 down

按下第二鍵
觸發 point 2 down

放開第一鍵 觸發
觸發 point 1 up
這時候 本來的point 2 變成point 1 座標 也變成由point 1取得雖然他應該是第二鍵

放開第一鍵 觸發 up (注意不是point 2 up)

Android 常用小技巧

//全螢幕
貼再 Activity 的onCreate
requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN , WindowManager.LayoutParams.FLAG_FULLSCREEN);


//強迫橫銀幕
在AndroidManifest.xml 中的 的 activity 屬性宣告


軟貓玩具
http://www.blogger.com/

//開啟使用網路權限
在AndroidManifest.xml 中的加入


//不讓手機自動進入休眠,持續背光
1.先在
在AndroidManifest.xml 中的加入


2.在程式中宣告
private PowerManager.WakeLock mWakeLock = mPowerManager.newWakeLock(mPowerManager.SCREEN_BRIGHT_WAKE_LOCK, "BackLight");
啟動持續背光模式
mWakeLock.acquire(); 通常加在 protected void onResume() 要不然
離開程式返回的時候效果就消失了

關閉持續背光
mWakeLock.release(); 通常加在 protected void onPause()
否則程式離開程式之後手機一直耗電

//使用GPS
在AndroidManifest.xml 中的加入



程式中宣告
LocationManager locationManger = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

註冊gps 觸發事件為 LocationListener
locationManger.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, LocationListener); //1000ms 跟新一次 時間越短越耗電

private final LocationListener LocationListener = new LocationListener()
{

@Override
public void onLocationChanged(Location location)
//GPS移動的時候
@Override
public void onProviderDisabled(String provider)
@Override
public void onProviderEnabled(String provider)

@Override
public void onStatusChanged(String provider, int status, Bundle extras)
//gps定位完成沒偵測
};