1. 程式人生 > >62.懸浮所有頁面之上、可拖拽的互動按鈕FloatDragView

62.懸浮所有頁面之上、可拖拽的互動按鈕FloatDragView

一個專案在開發的過程中,網路請求一般使用的是測試地址,開發完成可能還有模擬環境地址,然後是正式地址。那麼在開發和測試的過程中,就需要動態切換請求地址。這就需要一個類似Spinner的下拉框來完成這個功能,一開始我是寫在Main頁面,就一個簡單的Spinner,但是限制是如果跳轉到更深層次的頁面,就切換不了連結地址了。這就需要使用WindowManager建立一個view,使之懸浮於所有activity之上,隨時隨地切換地址。效果如下:

這裡寫圖片描述

1.建立一個FloatDragViewManager來管理WindowManager新增刪除view

public class FloatDragViewManager
implements FloatDragView.OnClickListener, FloatDragView.OnScrollListener {
private WindowManager mWindowManager; private WindowManager.LayoutParams mFdvParams; private Context mContext; public void showFloatDragView() { mContext = BaseApplication.getInstance(); mWindowManager = (WindowManager) mContext.getSystemService(WINDOW_SERVICE); mFloatDragView = new
FloatDragView(mContext);//是一個自定義的可拖拽的view,下面會寫 mFloatDragView.setOnClickListener(this); mFloatDragView.setOnScrollListener(this); mFloatDragView.setText(mUrlArr[0]); mFdvParams = new WindowManager.LayoutParams(); mFdvParams.type = WindowManager.LayoutParams.TYPE_PHONE;//級別在activity之上
mFdvParams.format = PixelFormat.TRANSPARENT;//背景透明 mFdvParams.gravity = Gravity.LEFT | Gravity.TOP;//位置 mFdvParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mFdvParams.width = WindowManager.LayoutParams.WRAP_CONTENT;//寬高 mFdvParams.height = WindowManager.LayoutParams.WRAP_CONTENT; mWindowManager.addView(mFloatDragView, mFdvParams); } }

別忘了要有許可權

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

並在設定中也要找到該應用,開啟懸浮窗許可權。

我在Main頁中顯示懸浮窗:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mManager = new FloatDragViewManager();
        mManager.showFloatDragView();
    }

這時就應該可以看見了。

2.點選懸浮窗顯示一個RecyclerView,提供點選事件改變連結地址:

    private RecyclerView mSpinnerRv;
    private WindowManager.LayoutParams mRvParams;

    private String[] mUrlArr = new String[]{"測試", "正式", "模擬"};
    private boolean mIsSpinnerShow;//列表是否顯示的狀態

    @Override
    public void onClick() {
        if (mSpinnerRv == null) {
            mSpinnerRv = new RecyclerView(mContext);
            mSpinnerRv.setBackgroundResource(R.drawable.corner_cyan_bg);
            mSpinnerRv.setLayoutManager(new LinearLayoutManager(mContext));
            mSpinnerRv.addOnItemTouchListener(new OnItemClickListener() {
                @Override
                public void onSimpleItemClick(BaseQuickAdapter adapter, View view, int position) {
                    mFloatDragView.setText(mUrlArr[position]);
                    Constant.BASE_URL = Constant.URL_ARR[position];
                    Toast.makeText(mContext, Constant.BASE_URL, Toast.LENGTH_SHORT).show();
                    mWindowManager.removeView(mSpinnerRv);//移除列表
                    mIsSpinnerShow = false;
                }
            });
            mSpinnerRv.setAdapter(new DataAdapter());
        }
        if (mIsSpinnerShow) {//移除列表
            mWindowManager.removeView(mSpinnerRv);
        } else {//新增列表
            mRvParams = new WindowManager.LayoutParams();
            mRvParams.type = WindowManager.LayoutParams.TYPE_PHONE;
            mRvParams.gravity = Gravity.LEFT | Gravity.TOP;
            mRvParams.format = PixelFormat.TRANSPARENT;
            mRvParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
            mRvParams.x = mFdvParams.x;//列表位置在FloatDragView之下
            mRvParams.y = mFdvParams.y + mFloatDragView.getHeight();
            mRvParams.width = mFloatDragView.getWidth();
            mRvParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
            mWindowManager.addView(mSpinnerRv, mRvParams);
        }
        mIsSpinnerShow = !mIsSpinnerShow;
    }

這時就已經可以實現點選切換地址了,但是不能拖動的話會擋住其他view

3.FloatDragView實現拖拽:

    private float mTouchX;
    private float mTouchY;
    private float mStartX;
    private float mStartY;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getRawX();
        float y = event.getRawY() - getStatusBarHeight(getContext());
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mTouchX = event.getX();
                mTouchY = event.getY();
                mStartX = x;
                mStartY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                if (mOnScrollListener != null) {
                    mOnScrollListener.onScroll((int) (x - mTouchX), (int) (y - mTouchY));
                }
                break;
            case MotionEvent.ACTION_UP:
                mTouchX = mTouchY = 0;
                if (Math.abs(x - mStartX) < 5 && Math.abs(y - mStartY) < 5) {// 用於區別是滑動了還是點選了
                    if (mOnClickListener != null) {
                        mOnClickListener.onClick();
                    }
                }
                break;
        }
        return true;
    }

    /**
     * 滑動監聽,供外部呼叫 
     */
    public void setOnScrollListener(OnScrollListener onScrollListener) {
        mOnScrollListener = onScrollListener;
    }

    public interface OnScrollListener {
        void onScroll(int x, int y);
    }

在manager中:

    /**
     * 滑動監聽,動態改變按鈕和列表的位置
     */
    @Override
    public void onScroll(int x, int y) {
        mFdvParams.x = x;
        mFdvParams.y = y;
        mWindowManager.updateViewLayout(mFloatDragView, mFdvParams);//更新位置
        if (mIsSpinnerShow) {//如果列表顯示的話
            mRvParams.x = mFdvParams.x;
            mRvParams.y = mFdvParams.y + mFloatDragView.getHeight();
            mWindowManager.updateViewLayout(mSpinnerRv, mRvParams);
        }
    }

4.最後在FloatDragViewManager中新增消除view的方法:


    public void removeFloatDragView() {
        mWindowManager.removeView(mFloatDragView);
        if (mSpinnerRv != null && mIsSpinnerShow) {
            mWindowManager.removeView(mSpinnerRv);
        }
    }

在Main中呼叫:

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mManager.removeFloatDragView();
    }