Android未读消息拖动气泡示例

文章目录

  • 前言
  • 最终效果图及思路
  • 关键代码
    • 1.定义,初始化等
    • 2.onDraw中绘制包括三样绘制
    • 3.onTouchEvent中
    • 4.反弹和爆炸动画
  • 总结


前言

拖动清除未读消息可以说在很多应用中都很常见,也被用户广泛接受。本文是一个可以供参考的Demo,希望能有帮助。


提示:以下是本篇文章正文内容,下面案例可供参考

最终效果图及思路

Android未读消息拖动气泡示例_第1张图片
Android未读消息拖动气泡示例_第2张图片

实现关键:气泡中间的两条边,分别是以ab,cd为数据点,G为控制点的贝塞尔曲线。

步骤:绘制圆背景以及文本;连接情况绘制贝塞尔曲线;另外端点绘制一个圆

关键代码

1.定义,初始化等

状态:静止、连接、分离、消失
在onSizeChanged中初始化状态,固定气泡以及可动气泡的圆心

代码如下(示例):

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    init(w, h);
}

private void init(int w, int h) {
    mBubbleState = BUBBLE_STATE_DEFAULT;

    //设置固定气泡圆心初始坐标
    if (mBubFixedCenter == null) {
        mBubFixedCenter = new PointF(w / 2, h / 2);
    } else {
        mBubFixedCenter.set(w / 2, h / 2);
    }
    //设置可动气泡圆心初始坐标
    if (mBubMovableCenter == null) {
        mBubMovableCenter = new PointF(w / 2, h / 2);
    } else {
        mBubMovableCenter.set(w / 2, h / 2);
    }
}

2.onDraw中绘制包括三样绘制

第一样:静止,连接,分离状态都需要绘制圆背景以及文本:

//静止,连接,分离状态都需要绘制圆背景以及文本
if (mBubbleState != BUBBLE_STATE_DISMISS) {
     
    canvas.drawCircle(mBubMovableCenter.x, mBubMovableCenter.y, mBubMovableRadius, mBubblePaint);
    mTextPaint.getTextBounds(mTextStr, 0, mTextStr.length(), mTextRect);
    canvas.drawText(mTextStr, mBubMovableCenter.x - mTextRect.width() / 2, mBubMovableCenter.y + mTextRect.height() / 2, mTextPaint);
}	

第二样:连接状态绘制贝塞尔曲线①。

if (mBubbleState == BUBBLE_STATE_CONNECT) {
    //绘制静止的气泡
    canvas.drawCircle(mBubFixedCenter.x, mBubFixedCenter.y, mBubFixedRadius, mBubblePaint);
    //计算控制点的坐标
    int iAnchorX = (int) ((mBubMovableCenter.x + mBubFixedCenter.x) / 2);
    int iAnchorY = (int) ((mBubMovableCenter.y + mBubFixedCenter.y) / 2);

    float sinTheta = (mBubMovableCenter.y - mBubFixedCenter.y) / mDist;
    float cosTheta = (mBubMovableCenter.x - mBubFixedCenter.x) / mDist;

    //D
    float iBubFixedStartX = mBubFixedCenter.x - mBubFixedRadius * sinTheta;
    float iBubFixedStartY = mBubFixedCenter.y + mBubFixedRadius * cosTheta;
    //C
    float iBubMovableEndX = mBubMovableCenter.x - mBubMovableRadius * sinTheta;
    float iBubMovableEndY = mBubMovableCenter.y + mBubMovableRadius * cosTheta;

    //A
    float iBubFixedEndX = mBubFixedCenter.x + mBubFixedRadius * sinTheta;
    float iBubFixedEndY = mBubFixedCenter.y - mBubFixedRadius * cosTheta;
    //B
    float iBubMovableStartX = mBubMovableCenter.x + mBubMovableRadius * sinTheta;
    float iBubMovableStartY = mBubMovableCenter.y - mBubMovableRadius * cosTheta;

    mBezierPath.reset();
    mBezierPath.moveTo(iBubFixedStartX, iBubFixedStartY);
    mBezierPath.quadTo(iAnchorX, iAnchorY, iBubMovableEndX, iBubMovableEndY);

    mBezierPath.lineTo(iBubMovableStartX, iBubMovableStartY);
    mBezierPath.quadTo(iAnchorX, iAnchorY, iBubFixedEndX, iBubFixedEndY);
    mBezierPath.close();
    canvas.drawPath(mBezierPath, mBubblePaint);
}

第三样:消失状态执行爆炸动画

// 认为是消失状态,执行爆炸动画
if (mBubbleState == BUBBLE_STATE_DISMISS && mCurDrawableIndex < mBurstBitmapsArray.length) {
    mBurstRect.set(
            (int) (mBubMovableCenter.x - mBubMovableRadius),
            (int) (mBubMovableCenter.y - mBubMovableRadius),
            (int) (mBubMovableCenter.x + mBubMovableRadius),
            (int) (mBubMovableCenter.y + mBubMovableRadius));
    canvas.drawBitmap(mBurstBitmapsArray[mCurDrawableIndex], null, mBurstRect, mBubblePaint);
}

3.onTouchEvent中

按下:区分静止状态和连接状态

case MotionEvent.ACTION_DOWN:
   if (mBubbleState != BUBBLE_STATE_DISMISS) {
        mDist = (float) Math.hypot(event.getX() - mBubFixedCenter.x, event.getY() - mBubFixedCenter.y);
        if (mDist < mBubbleRadius + MOVE_OFFSET) {
            //加上MOVE_OFFSET是为了方便拖拽
            mBubbleState = BUBBLE_STATE_CONNECT;
        } else {
            mBubbleState = BUBBLE_STATE_DEFAULT;
        }
    }
    break;

移动:判断是否到了分离状态

case MotionEvent.ACTION_MOVE:
    if (mBubbleState != BUBBLE_STATE_DEFAULT) {
        mDist = (float) Math.hypot(event.getX() - mBubFixedCenter.x, event.getY() - mBubFixedCenter.y);
        mBubMovableCenter.x = event.getX();
        mBubMovableCenter.y = event.getY();
        if (mBubbleState == BUBBLE_STATE_CONNECT) {
            if (mDist < mMaxDist - MOVE_OFFSET) {
                mBubFixedRadius = mBubbleRadius - mDist / 8;
            } else {
                mBubbleState = BUBBLE_STATE_APART;
            }
        }
        invalidate();
    }
    break;

弹起:判断是否已经到了分离状态,分离状态爆炸,未分离反弹

case MotionEvent.ACTION_UP:
    if (mBubbleState == BUBBLE_STATE_CONNECT) {
        // 橡皮筋动画
        startBubbleRestAnim();
    } else if (mBubbleState == BUBBLE_STATE_APART) {
        if (mDist < 2 * mBubbleRadius){
            //反弹动画
            startBubbleRestAnim();
        }else{
            // 爆炸动画
            startBubbleBurstAnim();
        }
    }
    break;

4.反弹和爆炸动画

/**
 * 连接状态下松开手指,执行类似橡皮筋动画
 */
private void startBubbleRestAnim() {
    ValueAnimator anim = ValueAnimator.ofObject(new PointFEvaluator(),
            new PointF(mBubMovableCenter.x, mBubMovableCenter.y),
            new PointF(mBubFixedCenter.x, mBubFixedCenter.y));
    anim.setDuration(200);
    anim.setInterpolator(new OvershootInterpolator(5f));
    anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mBubMovableCenter = (PointF) animation.getAnimatedValue();
            invalidate();
        }
    });
    anim.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            mBubbleState = BUBBLE_STATE_DEFAULT;
        }
    });
    anim.start();
}
/**
  * 爆炸动画
  */
 private void startBubbleBurstAnim() {
     //将气泡改成消失状态
     mBubbleState = BUBBLE_STATE_DISMISS;
     ValueAnimator animator = ValueAnimator.ofInt(0, mBurstBitmapsArray.length);
     animator.setInterpolator(new LinearInterpolator());
     animator.setDuration(500);
     animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
         @Override
         public void onAnimationUpdate(ValueAnimator animation) {
             mCurDrawableIndex = (int) animation.getAnimatedValue();
             invalidate();
         }
     });
     animator.start();
 }

总结

注:①贝塞尔曲线参考博文

本文完,有需要参考的同学→文中Demo下载地址

本系列文章引导页点击这里

你可能感兴趣的:(Android开发,android,自定义view,拖动气泡)