仿qq安卓客户端实现消息数目手势拖拽删除效果

先上个效果:

hehe

源码可以看这里

想试试效果的话可以直接通过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中点为控制点,做贝塞尔曲线
haha

这只是一种简单的平滑方案,个人认为另外一种平滑曲线更加好看一点也稍微麻烦一点,可以参考这里

这个计算过程放在重写的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();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值