安卓上層程式碼通過onTouch事件模擬實現"onClick"的動作,以及觸屏"靈敏度"的設定
阿新 • • 發佈:2019-02-18
PS:以下通過開發時遇到的問題來具體介紹。
關於安卓裝置懸浮球不能響應”Click”事件的總結
背景如下:安卓電視紅外觸屏框不能響應onClick事件,觸屏框底層驅動不支援onclick事件。
以下通過對專案從分析到解決問題過程作一個簡單的介紹:
一、遇到的問題:
1、 安卓應用懸浮球不能響應Click事件。
2、 觸控框邊緣獲取資料異常。
3、 對於如何區分懸浮球被點選還是觸控移動不清晰,以及如何通過觸控框規定的事件進行準確響應。
二、解決思路:
1:底層修改驅動(造成問題1的原因)。
2:在java層模擬特定事件的發生。
以下通過java層模擬Click的DOWN/UP事件作為解決思路進行擴充套件。
對於問題(DOWN意為 OnTouch中的事件,“Click”表狀態,依此類推)
① 首先檢視不可移動按鈕是否會響應OnClick事件,通過溝通以及Log發現觸控框響應事件驅動不支援Onclick事件,而是通過OnTouch事件進行響應。
② 通過Log發現,觸控框對於判斷DOWN、MOVE、UP事件存在問題,當手指按下時,即使手指不動,事件的經過仍是先觸發DOWN事件並馬上判斷事件為MOVE,這也是通過OnTouch事件無法準確響應事件為”Click”還是”Move”即造成問題3的直接原因。
③ 規定了懸浮球移動的條件為“長按”,這為解決②中提到的問題提供了思路。
④ 利用②提到的觸控框的特性,設定了焦點移動距離大小(程式碼中命名為靈敏度)判斷焦點是發生了”Click”or”Move”。同時設定DOWN後事件非“Move”的時間要求,以使懸浮球可被移動。
⑤ 如果懸浮球可被移動,這時在上層OnTouch事件中觸發的必定是MOVE事件部分(原因如②述),這時通過觸控移動就可以使懸浮球移動。
⑥ 最後是最為關鍵的UP事件,這時判斷是否是”Click”。判斷條件為:當觸控焦點移動距離小於靈敏設定值(認為焦點未移動)且當按下的時長小於長按時間時(這時懸浮球不能移動),最終將觸控動作判斷為”Click”,即OnTouch中模擬焦點的DOWN/UP。否則不響應為”Click”。
⑦ 最後通過Log發現觸控框邊緣接收的資料異常,將懸浮球移動的操作限制在一定的範圍內最終解決。
三、最終核心虛擬碼:
// 註冊觸碰事件監聽器
floatImage.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
不斷判斷按住時間是否大於額定值,如果是懸浮球可被移動
break;
case MotionEvent.ACTION_MOVE:
if (移動大於靈敏度) {
焦點是移動狀態
}
if (可被移動) {
懸浮球移動
}
break;
case MotionEvent.ACTION_UP:
if (焦點判斷未移動 && 懸浮球不能移動) {
//Click
}
break;
}
}
});
具體實現Demo如下(程式碼是剛工作不久寫的,有很多瑕疵- -):
floatImage.setOnTouchListener(new OnTouchListener() {
int firstx = 0;
int firsty = 0;
Thread touchthThread = null;
private long firsttime;
private long secondtime;
boolean ifmoveable;
boolean ismousemove;
boolean ifstopthread;
public boolean onTouch(View v, MotionEvent event) {
x = event.getRawX();
y = event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
firsttime = System.currentTimeMillis();
// Log.i("TopFloatService", "ACTION_DOWN");
ifstopthread = false;
ismousemove = false;
ismoving = false;
ifmoveable = false;
touchthThread = new Thread(new Runnable() {//這裡可以使用執行緒池,是個優化點
public void run() {
while (!ismoving) {
if (ifstopthread)
break;
secondtime = System.currentTimeMillis();
if (secondtime - firsttime >= 600
&& !ismousemove) {
mHandler.sendEmptyMessage(START_ANIMATION);//啟動抖動動畫,表示長按事件,懸浮球可被移動!
ifmoveable = true;
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
touchthThread.start();
firstx = ballWmParams.x;
firsty = ballWmParams.y;
mTouchStartX = (int) event.getX();
mTouchStartY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
// Log.i("TopFloatService", "ACTION_MOVE");
int nowx = (x - mTouchStartX) >= (float) 0.00 ? (int) (x - mTouchStartX)
: 0;
int nowy = (y - mTouchStartY) >= (float) 0.00 ? (int) (y - mTouchStartY)
: 0;
if (Math.sqrt((nowx - firstx) * (nowx - firstx)
+ (nowy - firsty) * (nowy - firsty)) >= sensitivity) {
ismousemove = true;
}
// Log.i("TopFloatService ismousemove:nowx - firstx,nowy - firsty",
// ismousemove+","+(nowx - firstx)+","+(nowy - firsty));
if (ifmoveable) {
ismoving = true;
updateViewPosition();
if (touchthThread != null && !touchthThread.isAlive())
touchthThread = null;
}
// Log.i("TopFloatService ifmoveable:", ifmoveable+"");
break;
case MotionEvent.ACTION_UP:
// Log.i("TopFloatService", "ACTION_UP");
ifstopthread = true;
// Log.i("TopFloatService ifmoveable:,ismousemove:",
// ifmoveable+","+ismousemove);
if (!ifmoveable && !ismousemove) {
int mx;
int my;
if ((int) ballWmParams.y
+ ballView.getLayoutParams().height
+ layout_mainLayout.getLayoutParams().height < screenheight) {
my = (int) ballWmParams.y
+ ballView.getLayoutParams().height;
} else {
my = screenheight
- layout_mainLayout.getLayoutParams().height
- 5;
}
if ((int) ballWmParams.x
+ ballView.getLayoutParams().width
+ layout_mainLayout.getLayoutParams().width < screenwidth) {
mx = (int) ballWmParams.x
+ ballView.getLayoutParams().width;
} else {
mx = screenwidth
- layout_mainLayout.getLayoutParams().width
- 5;
}
RelativeLayout.LayoutParams layoutParams = (LayoutParams) layout_mainLayout
.getLayoutParams();
layoutParams.setMargins(
mx,
my,
screenwidth
- mx
+ layout_mainLayout.getLayoutParams().width,
screenheight
- my
+ layout_mainLayout.getLayoutParams().height);
layout_mainLayout.setLayoutParams(layoutParams);
pop = new PopupWindow(menuView, screenwidth,
screenheight);
pop.showAtLocation(ballView, Gravity.NO_GRAVITY, mx, my);
pop.update();
floatImage.setVisibility(View.INVISIBLE);
}
mTouchStartX = mTouchStartY = 0;
if (touchthThread != null && !touchthThread.isAlive())
touchthThread = null;
break;
}
// 如果拖動則返回false,否則返回true
if (ifmoveable && ismousemove) {
return false;
} else {
return true;
}
}
});