處理觸控事件與其優先權
onTouchEvent是View類別提供的方法之一,主要用來處理「觸控」事件,我們只要繼承View,就可以將其覆寫並設計自己需要的功能。覆寫onTouchEvent的時後,必須將MotionEvent這個物件帶入,使其能夠找到對應的Case,然後執行相對應的動作。

簡單的範例程式碼如下所示:
MotionEvent是一個用來回報觸控動作(滑鼠,觸控筆,手指)的物件,其定義了許多觸控事件的類型和方法,常見的有下列幾項:
01.ACTION_UP:按壓的手勢結束
02.ACTION_DOWN:按壓的手勢開始
03.ACTION_POINTER_UP:非主要點抬起(離開面板)
04.ACTION_POINTER_DOWN:非主要點下來(觸及面板)
05.ACTION_MOVE:按壓的過程當中位置有變化(例如:手指在屏幕上滑動)
06.getX():回傳X座標
07.getY():回傳Y座標
08.getSize():回傳觸控點的尺寸(回傳值介於0和1之間)
09.getPressure():回傳觸控壓力大小(回傳值介於0和1之間)
10.getPointerId():回傳觸控點的ID(每一個觸控點的ID,在ACTION_UP完成之前是唯一值)
在Android作業系統,任何輸入(MotionEvent, KeyEvent, TrackEvent)都會先存放至EventHub,接著WindowManagerServicec會透過InputManager所提供的介面來啟動一個執行緒(Thread)執行InputReader,InputReader裡面的looponce方法會去呼叫mEventHub->getEvents(),取得最原始的RawEvent,再經由InputDispatcher分類並分發......(略),有興趣的朋友可以參考下列文章。
參考資料:
Android的KeyEvent:從EventHub到PhoneWindowManager
Android輸入事件流程中的EventHub分析
簡而言之,這些觸控事件都是從Android的底層,經過一層層關卡向上回報至Framework,讓上層的應用程式和Framework可以針對不同的情況做出調整。
那本篇文章的主題「處理觸控事件的優先權」,泰勞先說明一下我實作的情境背景,我遇到的問題跟「觸控筆」有關,簡單的說就是希望當觸控筆觸及屏幕時,能夠以觸控筆為優先,並且在過程中避免受到其他新加入觸控事件的干擾。
其實這有很多開發上的限制,舉例來說,觸控筆本身沒有發出任何訊號,裝置跟筆之間無法溝通,因此屏幕無法精準判斷是否為觸控筆。像是三星的Note系列,在觸控筆這一塊就設計的不錯,只要觸控筆「接近」屏幕時,螢幕會出現焦點,此時任何手指觸及屏幕都是沒有作用的。
在一般裝置上,我能想到的方法只有先經由getSize()粗略的區分手指和觸控筆,再透過getPointerId()來針對特定觸及點做客製,盡可能的去實現觸控筆優先的策略。
註:以下範例要感謝好夥伴Sam和Annie的幫忙與指導。
簡單的範例程式碼如下所示:
上面特別註明觸控筆的Size為0.02,其實經過多次的測試結果發現,雖然手指的Size大約落在0.06~0.899之間,但是在手指幾乎要離開屏幕的瞬間或是使用指甲觸摸,都有可能會出現0.02左右的Size,很容易造成誤判。
雖然此方法可以實作出效果,但並沒有辦法精準判斷,就當作是一個練習吧!

簡單的範例程式碼如下所示:
@Override
public boolean onTouchEvent(MotionEvent event) {
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL);
switch(event.getAction())
{
case MotionEvent.ACTION_MOVE:
canvas.drawLine(mov_x, mov_y, event.getX(), event.getY(), paint);
invalidate();
break;
case MotionEvent.ACTION_DOWN:
mov_x=(int) event.getX();
mov_y=(int) event.getY();
canvas.drawPoint(mov_x, mov_y, paint);
invalidate();
break;
......
......
}
return true;
}
public boolean onTouchEvent(MotionEvent event) {
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL);
switch(event.getAction())
{
case MotionEvent.ACTION_MOVE:
canvas.drawLine(mov_x, mov_y, event.getX(), event.getY(), paint);
invalidate();
break;
case MotionEvent.ACTION_DOWN:
mov_x=(int) event.getX();
mov_y=(int) event.getY();
canvas.drawPoint(mov_x, mov_y, paint);
invalidate();
break;
......
......
}
return true;
}
MotionEvent是一個用來回報觸控動作(滑鼠,觸控筆,手指)的物件,其定義了許多觸控事件的類型和方法,常見的有下列幾項:
01.ACTION_UP:按壓的手勢結束
02.ACTION_DOWN:按壓的手勢開始
03.ACTION_POINTER_UP:非主要點抬起(離開面板)
04.ACTION_POINTER_DOWN:非主要點下來(觸及面板)
05.ACTION_MOVE:按壓的過程當中位置有變化(例如:手指在屏幕上滑動)
06.getX():回傳X座標
07.getY():回傳Y座標
08.getSize():回傳觸控點的尺寸(回傳值介於0和1之間)
09.getPressure():回傳觸控壓力大小(回傳值介於0和1之間)
10.getPointerId():回傳觸控點的ID(每一個觸控點的ID,在ACTION_UP完成之前是唯一值)
在Android作業系統,任何輸入(MotionEvent, KeyEvent, TrackEvent)都會先存放至EventHub,接著WindowManagerServicec會透過InputManager所提供的介面來啟動一個執行緒(Thread)執行InputReader,InputReader裡面的looponce方法會去呼叫mEventHub->getEvents(),取得最原始的RawEvent,再經由InputDispatcher分類並分發......(略),有興趣的朋友可以參考下列文章。
參考資料:
Android的KeyEvent:從EventHub到PhoneWindowManager
Android輸入事件流程中的EventHub分析
簡而言之,這些觸控事件都是從Android的底層,經過一層層關卡向上回報至Framework,讓上層的應用程式和Framework可以針對不同的情況做出調整。
那本篇文章的主題「處理觸控事件的優先權」,泰勞先說明一下我實作的情境背景,我遇到的問題跟「觸控筆」有關,簡單的說就是希望當觸控筆觸及屏幕時,能夠以觸控筆為優先,並且在過程中避免受到其他新加入觸控事件的干擾。
其實這有很多開發上的限制,舉例來說,觸控筆本身沒有發出任何訊號,裝置跟筆之間無法溝通,因此屏幕無法精準判斷是否為觸控筆。像是三星的Note系列,在觸控筆這一塊就設計的不錯,只要觸控筆「接近」屏幕時,螢幕會出現焦點,此時任何手指觸及屏幕都是沒有作用的。
在一般裝置上,我能想到的方法只有先經由getSize()粗略的區分手指和觸控筆,再透過getPointerId()來針對特定觸及點做客製,盡可能的去實現觸控筆優先的策略。
註:以下範例要感謝好夥伴Sam和Annie的幫忙與指導。
簡單的範例程式碼如下所示:
@Override
public boolean onTouchEvent(MotionEvent event){
float inputSize = event.getSize();
int index = event.getActionIndex();
int inputId = event.getPointerID(index);
boolean isStylus = false;
int stylusId = -1;
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
//觸控筆的Size回傳值為0.02
if(inputSize==0.02 && isStylus==false){
isStylus = true;
stylusId = inputId; //紀錄觸控筆的PointerId
}
if((isStylus==true) && (inputId==stylusId)){
mov_x = event.getX();
mov_y = event.getY();
//If the new input event is stylus, then move to new coordinate.
mPath.moveTo(mov_x,mov_y);
}
......
break;
case MotionEvent.ACTION_MOVE:
......
}
}
public boolean onTouchEvent(MotionEvent event){
float inputSize = event.getSize();
int index = event.getActionIndex();
int inputId = event.getPointerID(index);
boolean isStylus = false;
int stylusId = -1;
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
//觸控筆的Size回傳值為0.02
if(inputSize==0.02 && isStylus==false){
isStylus = true;
stylusId = inputId; //紀錄觸控筆的PointerId
}
if((isStylus==true) && (inputId==stylusId)){
mov_x = event.getX();
mov_y = event.getY();
//If the new input event is stylus, then move to new coordinate.
mPath.moveTo(mov_x,mov_y);
}
......
break;
case MotionEvent.ACTION_MOVE:
......
}
}
上面特別註明觸控筆的Size為0.02,其實經過多次的測試結果發現,雖然手指的Size大約落在0.06~0.899之間,但是在手指幾乎要離開屏幕的瞬間或是使用指甲觸摸,都有可能會出現0.02左右的Size,很容易造成誤判。
雖然此方法可以實作出效果,但並沒有辦法精準判斷,就當作是一個練習吧!
留言
張貼留言