安卓可拖拽懸浮按鈕二

幾個月前,我寫了一篇文章《Android 可拖拽懸浮吸附按鈕》這篇文章的實現方式有點影響性能,介于當時的能力不足也是有一定原因的。這幾天重新實現了一種效果更好的方式,這種方式的優點是,你可以就像使用普通的控件的一樣使用它(實際上它就是普通的控件)并且滿足按鈕點擊效果,代碼上也大大的比之前簡化了。記得之前的方式 應為事件被改寫了還得單獨寫一個接口來用來判斷點擊事件。
實現思路

  1. 通過重寫控件的onTouchEvent方法監聽觸摸效果。
  2. 通過ViewsetX()setY()方法實現移動。
  3. 使用屬性動畫實現邊緣吸附效果。

源代碼沒多少行,這里先把代碼線上。此處我是繼承了FloatingActionButton,使它擁有了拖拽移動的功能。

public class DragFloatActionButton extends FloatingActionButton{

    private int parentHeight;
    private int parentWidth;
    private int slop;

    public DragFloatActionButton(Context context) {
        this(context,null);
    }

    public DragFloatActionButton(Context context, AttributeSet attrs) {
        this(context, attrs,0);

    }

    public DragFloatActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        slop=ViewConfigurationCompat.getScaledPagingTouchSlop(ViewConfiguration.get(context));
    }


    private int lastX;
    private int lastY;

    private boolean isDrag;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int rawX = (int) event.getRawX();
        int rawY = (int) event.getRawY();
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                setPressed(true);
                isDrag=false;
                getParent().requestDisallowInterceptTouchEvent(true);
                lastX=rawX;
                lastY=rawY;
                ViewGroup parent;
                if(getParent()!=null){
                    parent= (ViewGroup) getParent();
                    parentHeight=parent.getHeight();
                    parentWidth=parent.getWidth();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if(parentHeight<=0||parentWidth<=0){
                    //如果不存在父類的寬高則無法拖動,默認直接返回false
                    isDrag=false;
                    break;
                }
                int dx=rawX-lastX;
                int dy=rawY-lastY;
                //這里修復一些華為手機無法觸發點擊事件
                int distance= (int) Math.sqrt(dx*dx+dy*dy);
                //此處稍微增加一些移動的偏移量,防止手指抖動,誤判為移動無法觸發點擊時間
                if(distance==0||distance<=slop){
                    isDrag=false;
                    break;
                }
                //程序到達此處一定是正在拖動了
                isDrag=true;
                float x=getX()+dx;
                float y=getY()+dy;
                //檢測是否到達邊緣 左上右下
                x=x<0?0:x>parentWidth-getWidth()?parentWidth-getWidth():x;
                y=getY()<0?0:getY()+getHeight()>parentHeight?parentHeight-getHeight():y;
                setX(x);
                setY(y);
                lastX=rawX;
                lastY=rawY;
                break;
            case MotionEvent.ACTION_UP:
                if(isDrag()){
                    //恢復按壓效果
                    setPressed(false);
                }
                welt(rawX);
                break;
        }
        //如果是拖拽則消耗事件,否則正常傳遞即可。
        return isDrag() || super.onTouchEvent(event);
    }

    private boolean isDrag(){
        return isDrag;
    }
    
    private boolean isLeftSide(){
        return getX()==0;
    }
    private boolean isRightSide(){
        return getX()==parentWidth-getWidth();
    }
    
    private void welt(int currentX){
        if(!isLeftSide()||!isRightSide()){
            if(currentX>=parentWidth/2){
            //靠右吸附
            animate().setInterpolator(new DecelerateInterpolator())
                .setDuration(500)
                .xBy(parentWidth-getWidth()-getX())
                .start();
            }else {
                //靠左吸附
                ObjectAnimator oa=ObjectAnimator.ofFloat(this,"x",getX(),0);
                oa.setInterpolator(new DecelerateInterpolator());
                oa.setDuration(500);
                oa.start();
            }
        }
        
    }
}

代碼很簡單,
手指按下
首先是處理手指按壓下的事件,這里我們把拖拽標識符設置為false并記錄當前點擊的屏幕坐標。然后我們在處理移動事件。
手指移動
這里我們把拖拽標識符設置為true,因為手指移動了。然后我們需要計算手指移動了多少偏移量

//計算手指移動了多少
int dx=rawX-lastX;
int dy=rawY-lastY;

而后的兩行代碼表示控件需要移動的具體距離,后面有一個簡單的邊緣檢測計算。最終通過調用setX以及setY方法實現控件的移動。
手指松開
這里如果是拖拽動作我們才需要處理自己的邏輯否則直接跳過即可。在這里我們首先恢復了按鈕的按壓效果,在源代碼中找到setPressed(boolean)方法,這是處理按鈕點擊效果用的,在這里當手指松開后我們需要恢復按鈕原來的效果。然后在判斷控件需要往哪邊吸附,吸附的過程就是做屬性動畫而已,原理還是不斷的改變setX方法讓按鈕靠邊移動

總結

這種實現方式,我們能正常的使用控件的單擊時間和長按事件,因為只有當控件拖拽的時候我們才自己消耗事件否則全部交給系統處理。這是一種比較好的實現方式,通過這個例子其實我們還能實現更多的控件移動效果。事實上只需要改變所繼承的控件類型就可以了

PS1:
最近發現在部分華為手機上無法觸發點擊事件,調試發現當我手指按壓的時候會一直觸發MotionEvent.ACTION_MOVE事件而事實上我手指一點都沒有動,且Log出現的數據顯示移動距離一直是0.坑爹。只能加一個距離判斷了。上面的代碼已經修復了這個問題。
PS2:
修復拖拽到中心點的時候長按不會吸附到邊緣的問題,
優化實現定義,準確的說是讓view在父控件中任意拖拽。

歡迎共同探討更多安卓,java,c/c++相關技術QQ群:392154157

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,003評論 2 374

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,666評論 25 708
  • 林小刀159閱讀 279評論 0 3
  • 今天有去鏈接到,我覺得自我管理做的很好的朋友,是通過重生星球 今晚上去上零起點課程,感覺好快樂,自己大聲講了英語,...
    思思培閱讀 176評論 0 0
  • 在火車上完成了新概念的作業,不在乎別人的眼光,做自己,感覺棒棒的。思維導圖如果深入鉆研下去,我覺得肯定會對英語學習...
    小花貓和多多閱讀 284評論 5 1