Android未读消息拖动气泡示例代码详解(附源码)
时间:2021-07-14 07:56:39|栏目:Android代码|点击: 次
前言
拖动清除未读消息可以说在很多应用中都很常见,也被用户广泛接受。本文是一个可以供参考的Demo,希望能有帮助。
提示:以下是本篇文章正文内容,下面案例可供参考
最终效果图及思路
实现关键:
气泡中间的两条边,分别是以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下载地址
本系列文章引导页点击这里