輕松自制flyme懸浮球

CouCanady 8年前發布 | 26K 次閱讀 安卓開發 Android開發 移動開發

去年用了一整年的MX4Pro,魅族留給我最大的印象就是懸浮球了(質量問題我就不說了),左右滑動切換應用、上拉返回桌面、下拉打開通知欄、輕觸返回…,一切都那么絲滑。然而自從上半年換成了s7dege,我感覺怎么也習慣不了沒有懸浮球的生活了。

三星自己也有一個類似于懸浮球的功能,不過太過復雜,不易用,懸浮球本來就該是一個一步操作的產品,看來三星在軟件設計方面還是任重而道遠。于是乎我便在各大應用市場上找懸浮球,把所有排名靠前的懸浮球應用都安裝試了一下,最后終于讓我找到了一款幾乎和flyme懸浮球相仿的app。

這款app在我手機里呆了好幾個月,是我手機里除了微信之外,唯一允許自啟動的應用了。很感謝這款app的開發者,不僅沒有任何廣告,還非常好用,完美移植了flyme自帶的懸浮球功能。

然而漸漸的,我便感覺到了一絲不舒服,那就是我每次安裝了一個新app,打開后提示要賦予權限(存儲、拍照)的時候,6.0的系統總會溫馨的彈出一個框:

然后我就必須到設置頁面,花半天找到懸浮球,關掉它的 “可出現在頂部的應用程” 權限,然后才能回到app,授予權限。最后,我還得再次跑到設置頁面,再花半天找到懸浮球,打開它的 “可出現在頂部的應用程” 權限。朋友啊朋友,這種體驗,一次就夠了,然而硬是讓我體驗了N次啊!

然而有什么能難得倒程序員的呢?剛好這個周末在家無事,我決定按照自己的習慣,打造一個心目中最易用的懸浮球。

設計

1.UI

UI很簡單,直接用sketch切了三個圓,一個是作為背景的灰色半透明的圓,一個是中心的小圓,另外還有一個默認隱藏的大圓。

2.功能

因為自己的操作習慣是固定的,所以也就不需要給懸浮球添加自定義操作的功能了,直接將操作對應的功能寫死即可。

(1)單擊:返回

(2)長按:移動懸浮球

(3)左滑右滑:打開最近應用程序

(4)上拉:返回桌面

(5)下拉:

這塊我最先開始定義的很簡單,就是下拉通知欄,但是經過一天的使用,我又給它加了一個功能,就是保持下拉狀態1.5秒,將移除懸浮球。這樣你便可以很簡單的移除掉懸浮球了。

實現

1.如何添加懸浮球到桌面

這里首先要感謝郭霖大神的 《 Android桌面懸浮窗效果實現,仿360手機衛士懸浮窗效果》 ,這部分我參考了這篇文章,成功的將懸浮球添加到了桌面。

public static void addBallView(Context context) {
    if (mBallView == null) {
        WindowManager windowManager = getWindowManager(context);
        int screenWidth = windowManager.getDefaultDisplay().getWidth();
        int screenHeight = windowManager.getDefaultDisplay().getHeight();
        mBallView = new FloatBallView(context);
        LayoutParams params = new LayoutParams();
        params.x = screenWidth;
        params.y = screenHeight / 2;
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        params.gravity = Gravity.LEFT | Gravity.TOP;
        params.type = LayoutParams.TYPE_PHONE;
        params.format = PixelFormat.RGBA_8888;
        params.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
                | LayoutParams.FLAG_NOT_FOCUSABLE;
        mBallView.setLayoutParams(params);
        windowManager.addView(mBallView, params);
    }
}

2.手勢判斷

這是最重要的部分了,承擔著懸浮球的主要功能。

(1)手指按下時

按下時,隱藏小球,展現大球,并記錄按下位置和按下時間。

case MotionEvent.ACTION_DOWN:
       mIsTouching = true;
       mImgBall.setVisibility(INVISIBLE);
       mImgBigBall.setVisibility(VISIBLE);
       mLastDownTime = System.currentTimeMillis();
       mLastDownX = event.getX();
       mLastDownY = event.getY();
       postDelayed(new Runnable() {
          @Override
          public void run() {
            if (!mIsLongTouch && mIsTouching && mCurrentMode == MODE_NONE) {
                    mIsLongTouch = isLongClick(event);
               }
          }
       }, LONG_CLICK_LIMIT);
       break;

代碼最后的postDealy時干嘛使的呢?就是通過延遲300毫秒,判斷是否是長按模式。其中判斷長按的方法代碼如下:

private boolean isLongClick(MotionEvent event) {
    float offsetX = Math.abs(event.getX() - mLastDownX);
    float offsetY = Math.abs(event.getY() - mLastDownY);
    long time = System.currentTimeMillis() - mLastDownTime;

    if (offsetX < mTouchSlop && offsetY < mTouchSlop && time >= LONG_CLICK_LIMIT) {
        //震動提醒
        mVibrator.vibrate(mPattern, -1);
        return true;
    } else {
        return false;
    }
}

(2)手指移動時

這時要判斷是否是處于長按狀態,如果是,那么進入MOVE模式,移動懸浮球,如果不是,則判斷操作手勢,即下拉還是上拉等其他手勢。

case MotionEvent.ACTION_MOVE:
      if (!mIsLongTouch && isTouchSlop(event)) {
              return true;
      }
      if (mIsLongTouch && (mCurrentMode == MODE_NONE || mCurrentMode == MODE_MOVE)) {
              mLayoutParams.x = (int) (event.getRawX() - mOffsetToParent);
              mLayoutParams.y = (int) (event.getRawY() - mOffsetToParentY);
              mWindowManager.updateViewLayout(FloatBallView.this, mLayoutParams);
              mBigBallX = mImgBigBall.getX();
              mBigBallY = mImgBigBall.getY();
              mCurrentMode = MODE_MOVE;
      } else {
              doGesture(event);
      }
      break;

進行手勢操作的代碼如下,主要是根據當前坐標與按下時記錄的坐標進行計算,判斷手勢,并更新大球位置。

private void doGesture(MotionEvent event) {
    float offsetX = event.getX() - mLastDownX;
    float offsetY = event.getY() - mLastDownY;

    if (Math.abs(offsetX) < mTouchSlop && Math.abs(offsetY) < mTouchSlop) {
        return;
    }
    if (Math.abs(offsetX) > Math.abs(offsetY)) {
        if (offsetX > 0) {
            if (mCurrentMode == MODE_RIGHT) {
                return;
            }
            mCurrentMode = MODE_RIGHT;
            mImgBigBall.setX(mBigBallX + OFFSET);
            mImgBigBall.setY(mBigBallY);
        } else {
            if (mCurrentMode == MODE_LEFT) {
                return;
            }
            mCurrentMode = MODE_LEFT;
            mImgBigBall.setX(mBigBallX - OFFSET);
            mImgBigBall.setY(mBigBallY);
        }
    } else {
        if (offsetY > 0) {
            if (mCurrentMode == MODE_DOWN || mCurrentMode == MODE_GONE) {
                return;
            }
            mCurrentMode = MODE_DOWN;
            mImgBigBall.setX(mBigBallX);
            mImgBigBall.setY(mBigBallY + OFFSET);

            //如果長時間保持下拉狀態,將會觸發移除懸浮球功能
            postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (mCurrentMode == MODE_DOWN && mIsTouching) {
                        toRemove();
                        mCurrentMode = MODE_GONE;
                    }
                }
            }, TO_APP_INDEX_LIMIT);
        } else {
            if (mCurrentMode == MODE_UP) {
                return;
            }
            mCurrentMode = MODE_UP;
            mImgBigBall.setX(mBigBallX);
            mImgBigBall.setY(mBigBallY - OFFSET);
        }
    }
}

(3)手指抬起時

手指抬起后,先要判斷是否是長按模式,不是的話再判斷是否是單擊,都不是的話就根據當前狀態觸發對應功能。

case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
       mIsTouching = false;
       if (mIsLongTouch) {
           mIsLongTouch = false;
       } else if (isClick(event)) {
           AccessibilityUtil.doBack(mService);
       } else {
           doUp();
       }
       mImgBall.setVisibility(VISIBLE);
       mImgBigBall.setVisibility(INVISIBLE);
       mCurrentMode = MODE_NONE;
       break;

效果

到目前為止,懸浮球的功能就實現了,來看看使用效果如何。

最后再說兩句

花了大半天,總算是大功告成了,程序員,最大的好處就是自己可以定制應用:joy:,

 

 

 本文由用戶 CouCanady 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!