作者:梦露的殇_192 | 来源:互联网 | 2023-09-08 11:01
原标题:Android未读消息拖动气泡示例
文章目录
- 前言
- 最终效果图及思路
- 关键代码
- 1.定义,初始化等
- 2.onDraw中绘制包括三样绘制
- 3.onTouchEvent中
- 4.反弹和爆炸动画
- 总结
前言
拖动清除未读消息可以说在很多应用中都很常见,也被用户广泛接受。本文是一个可以供参考的Demo,希望能有帮助。
提示:以下是本篇文章正文内容,下面案例可供参考
最终效果图及思路
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/21e585a7e21fc7dc.png)
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/72fd2c126203a875.webp?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQxNTg3NDM=,size_16,color_FFFFFF,t_70)
实现关键:气泡中间的两条边,分别是以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;
float iBubFixedStartX = mBubFixedCenter.x - mBubFixedRadius * sinTheta;
float iBubFixedStartY = mBubFixedCenter.y + mBubFixedRadius * cosTheta;
float iBubMovableEndX = mBubMovableCenter.x - mBubMovableRadius * sinTheta;
float iBubMovableEndY = mBubMovableCenter.y + mBubMovableRadius * cosTheta;
float iBubFixedEndX = mBubFixedCenter.x + mBubFixedRadius * sinTheta;
float iBubFixedEndY = mBubFixedCenter.y - mBubFixedRadius * cosTheta;
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);
mBezierPat文章来源地址38100.htmlh.close();
canvas.drawPath(mBezierPath, mBubblePaint);
}
第三样:消失状态执行爆炸动画
if (mBubbleState == BUBBLE_STATE_DISMISS && mCurDrawableIndex < mBurstBitmapsArray.length) {
mBwww.yii666.comurstRect.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) {
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);
文章来源地址38100.html 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 ValueAnimatwww.yii666.comor.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurDrawableIndex = (int) animation.getAnimatedValue();
invalidate();
}
});
anima文章来源站点https://www.yii666.com/tor.start();
}
总结
注:①贝塞尔曲线参考博文
本文完,有需要参考的同学→文中Demo下载地址
本系列文章引导页点击这里
来源于:Android未读消息拖动气泡示例