1. 程式人生 > >安卓上層程式碼通過onTouch事件模擬實現"onClick"的動作,以及觸屏"靈敏度"的設定

安卓上層程式碼通過onTouch事件模擬實現"onClick"的動作,以及觸屏"靈敏度"的設定

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;
                }
            }
        });