今天要寫的是自已處理多點觸碰的功能,繼承android.view.View,覆寫onTouchEvent。
普通要辯使user multitouch的行為的話,有更方便的Gesture可以用,但今天不討論這一塊。
簡略的onTouchEvent覆寫:
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: //第一個觸碰點觸碰螢幕時觸發 case MotionEvent.ACTION_POINTER_DOWN: //第二個以上觸碰點觸碰螢幕時觸發 case MotionEvent.ACTION_MOVE: //只要在螢幕上的觸碰點有移動時就會觸發 case MotionEvent.ACTION_POINTER_UP: //觸碰點離開時,且螢幕上還有其他觸碰點時觸發 case MotionEvent.ACTION_UP: //最後一個觸碰點離開螢幕時觸發 } return true; }
在一個完整的Multitouch中,每個觸碰點(pointer)都有一個獨一無二的ID,我們可以借由 event.getX(int)、 event.getY(int) 來取得觸碰點(pointer)的x、y座標。getX和getY裡面傳的int是pointer ID。
但pointer ID是android自動assign的,我們怎麼知道pointer ID是多少呢?
原來event裡面就告訴我們啦
int index = event.getActionIndex();
int ID = event.getPointerId(index);
第一行告訴我們現在是哪一個index的觸碰點產生的事件
第二行就是根據這個index去查它的ID是多少
有了這些工具,我們就能偵測user什麼時候手指碰到螢幕、划動、離開,及動作的x、y位置了!
最後,只要把程式邏輯寫出來,就可以實作多點觸控了!
例如:兩指的縮放功能的演算法:
@Override public boolean onTouchEvent(MotionEvent event) { int index = event.getActionIndex(); int ID = event.getPointerId(index); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_DOWN: //紀錄新加入的點的ID、xy座標 //紀錄此時所有在螢幕上的pointer的中心 //紀錄此時所有在螢幕上的pointer所圍成的面積 //紀錄此時target的原xy座標、原寬及高 case MotionEvent.ACTION_MOVE: //計算新的所有pointer的中心 //計算新的所有pointer的面積 //計算位移量 = 新的中心 - 原中心 //計算縮放量 = 新的面積 - 原面積 //將target的位置移到位移量、大小放到縮放量去 case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: //將離開的點從紀錄中刪掉 //更新目前所有還在螢幕上的所有pointer的中心 //更新目前所有還在螢幕上的所有pointer所圍成的面積 //更新此時target的xy座標、寬高 } return true; }
後記:這次讓我花最多時間的是…如果將移動、縮放功能的detection直接寫在target view上的話,且在MotionEvent.ACTION_MOVE的時候直接去update target的話,那麼之後所有抓到的getX() getY() 都會不是預期的。因為getX()和 getY() 抓的是相對座標,是相對於那個target的。在移的過程中若把target的位置移走了,那麼未來抓到的getX()和getY()就不會是相對於原本那個座標了。
解決方法是在MotionEvent.ACTION_MOVE時,先add一個假的view,所有移動、縮放都作用在這個假view上,這樣原view不動,且touch event會繼續作用在原view上!就不會影響座標了 :)
以後有空的話再把完整的source code整理放上來 ~
沒有留言:
張貼留言