天天看點

仿qq安卓用戶端實作消息數目手勢拖拽删除效果先上個效果:

先上個效果:

仿qq安卓用戶端實作消息數目手勢拖拽删除效果先上個效果:

源碼可以看這裡

想試試效果的話可以直接通過gradle引入:

allprojects {
    repositories {
        jcenter()
        maven { url "https://jitpack.io" }
    }
}

compile 'com.github.nanyi5452:viewpagerDotIndicator:4c0a7bcf83'
           

然後布局裡面加入就可以了,注意父布局屬性裡要加上 android:clipChildren=”false” 這樣view才能在範圍外繪制。

<com.nanyi545.www.bounceindicatorlib.BounceIndicator
    android:id="@+id/indicator"
    android:layout_margin="50dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    />
           

可以調用 mBounceIndicator.addCount(); 方法來變化計數器數字。

下面介紹下我實作的思路。

第一步

畫出拖拽點和固定點之間的平滑曲線。

簡單起見,我用了下面的這個方案。

1. 連接配接兩圓圓心,做線A

2. 分别過兩圓圓心,做A的垂線,交于圓周

3. 以在同一側的兩個圓周交點為終點,以線A中點為控制點,做貝塞爾曲線

仿qq安卓用戶端實作消息數目手勢拖拽删除效果先上個效果:

這隻是一種簡單的平滑方案,個人認為另外一種平滑曲線更加好看一點也稍微麻煩一點,可以參考這裡。

這個計算過程放在重寫的onTouchEvent方法裡面,根據手指觸點計算出這個平滑曲線的路徑,以供draw方法繪制。

第二步

實作松手之後的回彈效果。

邏輯上很簡單,如果拖拽的距離>某固定值,起點的圓彈向松手點,并且爆炸消失(爆炸消失如何實作最後介紹);如果拖拽的距離<某固定值,松手點的數字彈回到起點。在第一步的基礎上隻要知道回彈過程中的起點終點的位置就行了,而這兩個位置可以利用sdk中的Scroller類來輕松算出。

以松手點的數字彈回到起點的效果為例,稍微講解下核心代碼:

1 初始化Scroller,第二個參數OvershootInterpolator顧名思義,是讓回彈的時候稍微彈過頭一點。

toStartScroller=new Scroller(getContext(),new OvershootInterpolator(2));
           

2 在onTouchEvent裡判斷當手指擡起的時候(MotionEvent.ACTION_UP),調用startScroll方法。

float dx = stickX - mLastX;
float dy = stickY - mLastY;
if (toStartScroller.isFinished())
toStartScroller.startScroll((int)mLastX, (int)mLastY, (int)dx, (int)dy, 300);
           

(int)mLastX 和 (int)mLastY 表示從這個位置開始, (int)dx 和 (int)dy 分别表示水準/垂直的移動距離,最後一個參數表示移動過程的時長。

3 單單這樣使用Scroller不會有任何視覺效果,Scroller類隻是計算了位置,需要根據計算的位置自己來更新view。基本的套路就重寫computeScroll方法,在computeScroll裡更新view。

if (toStartScroller.computeScrollOffset()) {
        mLastX=toStartScroller.getCurrX();
        mLastY=toStartScroller.getCurrY();
        endX=mLastX;
        endY=mLastY;
        if (needToDrawConnectingPath()) initConnectingPath();
        invalidate();
    }
           

這其中getCurrX()、getCurrY()就是擷取計算的位置。 initConnectingPath()方法就是第一步裡說的計算平滑曲線的函數。

起點的圓彈向松手點的效果也是一樣的套路,可以參考源碼。

第三步:

實作最後的爆炸消失效果。

爆炸消失的效果,我實作的方法應該和qq的不太一樣。我隻是簡單粗暴地在手指擡起位置畫了15個小圓圈向随機方向移動,同時線性增加透明度。

private float[] disolveXseed=new float[15];
private float[] disolveYseed=new float[15];
private float[] disolveX=new float[15];
private float[] disolveY=new float[15];
           

這其中 disolveSeed 純粹就是 -0.5到0.5之間的随機數,disolveX disolveY 是爆炸過程中小圓圈的圓心位置(相對于手指觸摸點位置)。

private void initDisolve(){
    for (int ii=0;ii<disolveXseed.length;ii++){
        disolveXseed[ii]= (float) Math.random()-0.5f;
        disolveYseed[ii]= (float) Math.random()-0.5f;
    }
}
           

disolveController也是一個Scroller,來控制爆炸消失的進度,我隻需要一個标量來控制進度就可以,是以x的起始值終值都是0

disolveController.startScroll(0,0,0,MAX_DISOLVE_STAGE,1000);
           

最後是老套路了,在重寫的computeScroll方法裡面加上擷取爆炸進度,根據爆炸進度來計算各個小圓圈的圓心位置,以及透明度(爆炸剛開始時不透明,爆炸完成時候完全透明)。 這裡MAX_DISOLVE_STAGE*2*(2*endR)什麼亂起八糟的其實就是一個常數,而progress是線性變化的,這樣看上去的效果就是15個小圓圈同時遠離手指擡起點。

if (disolveController.computeScrollOffset()){
        int alpha=MAX_DISOLVE_STAGE-disolveController.getCurrY();
        int progress=disolveController.getCurrY();
        disolvePaint.setAlpha(alpha);
        for (int ii=0;ii<disolveX.length;ii++){
            disolveX[ii]= disolveXseed[ii]*progress/MAX_DISOLVE_STAGE*2*(2*endR);
            disolveY[ii]= disolveYseed[ii]*progress/MAX_DISOLVE_STAGE*2*(2*endR);
        }
        invalidate();
    }