首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

Android 懸浮窗 OnTouchListener 移動歸零 Bug 修復:座標累加邏輯錯誤

     編輯:程式設計疑難解答

問題現象

實現一個跟隨手指移動的懸浮圖標。首次移動正常,第二次按下移動時,圖標先瞬間跳回初始位置 (0,0),再跟隨移動。

錯誤代碼分析


// 錯誤邏輯
case MotionEvent.ACTION_MOVE:
    mx = event.getRawX();
    my = event.getRawY();
    rx = mx - x; // x,y 是 ACTION_DOWN 時的原始觸摸座標
    ry = my - y;
    mParams.x = (int) rx; // 直接用相對偏移量作為窗口絕對座標!
    mParams.y = (int) ry;
    mWM.updateViewLayout(ivCursor, mParams);
    break;

根因: mParams.x/y 是窗口在屏幕上的**絕對座標**。代碼中直接將**手指相對按下點的偏移量**賦給絕對座標。首次按下時 x,y ≈ 初始窗口座標,偏移量恰好近似絕對座標,看似正常。二次按下時 x,y 為新按下點座標,偏移量變小,導致窗口跳回原點附近。

修復:記錄「按下瞬間窗口座標」+ 「手指偏移量」= 新窗口座標


private int mWindowStartX, mWindowStartY; // 記錄按下時窗口座標
private float mTouchStartX, mTouchStartY; // 記錄按下時手指屏幕座標

relativeLayout.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 1. 記錄按下時 手指屏幕座標
                mTouchStartX = event.getRawX();
                mTouchStartY = event.getRawY();
                // 2. 記錄按下時 窗口當前絕對座標
                mWindowStartX = mParams.x;
                mWindowStartY = mParams.y;
                break;

            case MotionEvent.ACTION_MOVE:
                // 3. 計算手指移動偏移量
                float dx = event.getRawX() - mTouchStartX;
                float dy = event.getRawY() - mTouchStartY;
                // 4. 新窗口座標 = 按下時窗口座標 + 手指偏移量
                mParams.x = mWindowStartX + (int) dx;
                mParams.y = mWindowStartY + (int) dy;
                mWM.updateViewLayout(ivCursor, mParams);
                break;

            case MotionEvent.ACTION_UP:
                // 可選:邊界檢查、吸附邊緣等
                break;
        }
        return true; // 消費事件
    }
});

關鍵變量對照表

變量含義賦值時機
mTouchStartX/Y手指按下時的屏幕絕對座標 (getRawX/Y)ACTION_DOWN
mWindowStartX/Y窗口按下時的屏幕絕對座標 (mParams.x/y)ACTION_DOWN
dx/dy手指相對按下點的移動偏移量ACTION_MOVE 計算
mParams.x/y窗口新的絕對座標 = mWindowStartX/Y + dx/dyACTION_MOVE 更新

進階優化

  • 邊界限制: 更新 mParams 前檢查是否超出屏幕可見區 (DisplayMetrics),防止移出屏幕。
  • 邊緣吸附: ACTION_UP 時判斷距離左右邊緣閾值,動畫平滑移至邊緣 (ValueAnimator 修改 mParams.x)。
  • 權限適配: Android 6.0+ 需 SYSTEM_ALERT_WINDOW 權限,引導用戶開啟。
  • 生命週期: Service 中創建窗口,onDestroy 移除 mWM.removeView(ivCursor)。

總結

Bug 核心:將「手指相對偏移量」誤作為「窗口絕對座標」。 修復關鍵:**在 ACTION_DOWN 記錄窗口當前絕對座標,移動時新座標 = 記錄座標 + 手指偏移量。** 此邏輯適用於所有拖拽移動 View/Window 的場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved