1. 程式人生 > >Android實現可拖動的懸浮按鈕控制元件

Android實現可拖動的懸浮按鈕控制元件

有時候,我們需要一個可移動的選單,Android本身並沒有這樣的控制元件,小弟不才,最近研究了下,並參考了網上的一些資料,自己動手封裝了個,因為是第一個版本,可能會有一些bug,歡迎留言指導。我們先簡單來說一下原理:
隨著我們的手勢移動,控制元件就隨著移動到某個位置,關鍵點是我們怎麼處理控制元件的onTouch方法,這裡我們需要監聽MotionEvent的三個狀態,如下:

@Override
public boolean onTouch(View v, MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            //手勢按下
            break;
        case MotionEvent.ACTION_MOVE:
            //手勢移動
            break;
        case MotionEvent.ACTION_UP:
            //手勢擡起
            break;
    }
    return super.onTouchEvent(event);;
}

首先在手勢按下的時候,我們記住起始的位置:
mStartX = mLastX = (int) event.getRawX();
mStartY = mLastY = (int) event.getRawY();
因為剛按下,所以最後一次的座標也跟起始的座標一樣。
其次,在MotionEvent.ACTION_MOVE事件(手勢移動)中對選單控制元件進行移動,程式碼如下:
int left, top, right, bottom;
int dx = (int) event.getRawX() - mLastX;//記錄距上次move事件後手勢移動的x軸長度
int dy = (int) event.getRawY() - mLastY;//記錄距上次move事件後手勢移動的y軸長度
left = v.getLeft() + dx;//手勢位置變化後也變化控制元件的座標
if (left < 0) {
left = 0;//x軸的左邊不能小於0 防止控制元件無法全部顯示或不顯示
}
right = left + v.getWidth();
if (right > mScreenWidth) {
right = mScreenWidth;//x軸的右邊邊不能大於屏寬 防止控制元件無法全部顯示或不顯示
left = right - v.getWidth();
}
top = v.getTop() + dy;
if (top < mStatusBarHeight + 2) {
top = mStatusBarHeight + 2;//控制元件頂部不能小於狀態列的高度
}
bottom = top + v.getHeight();
if (bottom > mScreenHeight) {
bottom = mScreenHeight;//底部不能大於屏高
top = bottom - v.getHeight();
}
v.layout(left, top, right, bottom);//重置控制元件的位置
mLastX = (int) event.getRawX();//記錄最後一次x座標
mLastY = (int) event.getRawY();//記錄最後一次y座標
經過以上實現,我們就可以實現控制元件隨著手勢的移動而移動。到這裡會有一個問題,就是你按home鍵後程序回到後臺,然後再進入程式,控制元件會出現在起始的位置並不會出現在你手勢移動後的最後位置,這個問題我找了好久才發現原因,原因是因為我們呼叫v.layout方法設定位置後並沒有改變控制元件的LayoutParam裡面的引數,你重新進入後,頁面draw時是根據LayoutParam裡面的引數來繪製的,既然找到了問題,那就好辦了,簡單的處理方法就是在你手勢移動結束的時候設定LayoutParam,一般來講沒必要在move方法裡面設定,在up方法裡處理就好了,所以我們還需要在MotionEvent.ACTION_UP加上如下程式碼:
v.setLayoutParams(createLayoutParams(v.getLeft(), v.getTop(), 0, 0));
其實createLayoutParams方法就是建立控制元件的LayoutParam,如下:
private FrameLayout.LayoutParams createLayoutParams(int left, int top, int right, int bottom) {
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(mBuilder.size, mBuilder.size);
layoutParams.setMargins(left, top, right, bottom);
return layoutParams;
}
到此,基本上就差不多實現主要功能了,但是如果我們要為該控制元件新增點選事件呢?
其實我們可以這麼處理,當我們手指移動的距離小於某個資料時,我們不要消耗該手勢事件,這個時候就會觸發oncllick事件,而當我們的手指移動的距離大於某個資料時,我們才消耗手勢事件,這個時候onclick就不會觸發,程式碼如下:
v.setLayoutParams(createLayoutParams(v.getLeft(), v.getTop(), 0, 0));
//這裡需設定LayoutParams,不然按home後回再到頁面等view會回到原來的地方
float endX = event.getRawX();
float endY = event.getRawY();
if (Math.abs(endX - mStartX) > 5 || Math.abs(endY - mStartY) > 5) {
//防止點選的時候稍微有點移動點選事件被攔截了
mTouchResult = true;
}
if (mTouchResult && mBuilder.needNearEdge) {
//是否每次都移至螢幕邊沿
moveNearEdge();
}
最後的程式碼意思是(其中mTouchResult為onTouch方法的返回值),當前是實現移動控制元件的操作,並且需要控制元件移動螢幕的邊沿時,就把控制元件移至螢幕邊沿(這是另外實現的功能,在這裡就不多闡述了)。
好了,主要的原理和程式碼就是這些,我把程式碼放在github上,連結如下:

https://github.com/linqssonny/DrawView
上面附有使用的方法,如大家遇到什麼問題可以加我Q:252624617交流,如有bug之類的歡迎在github上提出,我會繼續完善。