类似QQ拖动气泡删除消息的气泡实现
2016-12-15 17:06
411 查看
类似QQ拖动气泡删除消息的气泡实现
本文将介绍使用二阶贝塞尔曲线实现类似QQ拖动气泡删除消息的气泡实现,本文中的部分内容参考自Yellow5A5。1. 贝塞尔曲线简介
一阶贝塞尔曲线如下图:
一阶贝塞尔曲线的公式为:B(t)=(1−t)∗P0+t∗P1, t∈[0,1];
二阶贝塞尔曲线如下图:
二阶贝塞尔曲线的公式为:
B(t)=(1−t)2∗P0+2∗t∗(1−t)∗P1+t2∗P2, t∈[0,1]; (P1看作是控制点)
拆分:
B(t)=(1−t)2∗P0+t∗(1−t)∗P1+t∗(1−t)∗P1+t2∗P2, t∈[0,1];
合并:
B(t)=(1−t)∗((1−t)∗P0+t∗P1)+t∗((1−t)∗P1+t∗P2), t∈[0,1];
换算,其中B0(t),B1(t)分别是一阶贝塞尔曲线:
B(t)=(1−t)∗B0(t)+t∗B1(t), t∈[0,1];
B0(t)=(1−t)∗P0+t∗P1, t∈[0,1];
B1(t)=(1−t)∗P1+t∗P2, t∈[0,1]。
2. 气泡实现
代码原理图如下:
代码如下:
package widget; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; /** * Created by cugyong on 2016/12/15. * 类似于qq的拖拉删除消息的圆球动画(使用二阶贝塞尔曲线) */ public class DragRoundView extends View { private final static int RADIUS = 25; // 固定圆的半径随着移动圆圆心和固定圆圆心之间距离变化而改变的参数因子 private final static int FACTOR = 8; // 固定不动的圆的半径和移动的圆的半径的总和,应当是一个定值, 在这里为RADIUS乘以屏幕密度 private float mRadiusSum; // 固定不动的圆的圆心 private float mCenterX; private float mCenterY; // 固定不动的圆的半径 private float mCenterRadius; // 移动的圆的圆心 private float mMovingX; private float mMovingY; // 移动的圆的半径 private float mMovingRadius; // 拖动结束的点 float mEndX; float mEndY; // 移动圆圆心和固定圆圆心之间距离的最大值 private float mLimit; // 两条直线和两条二阶贝塞尔曲线 private Path mPath; private Paint mPaint; // 拖动释放时圆球恢复的动画 private ValueAnimator mAnimator; public DragRoundView(Context context) { super(context); initParams(); } public DragRoundView(Context context, AttributeSet attrs) { super(context, attrs); initParams(); } public DragRoundView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initParams(); } private void initParams(){ // 屏幕密度 float density = getResources().getDisplayMetrics().density; mRadiusSum = density * RADIUS; int screenWidth = getResources().getDisplayMetrics().widthPixels; int screenHeight = getResources().getDisplayMetrics().heightPixels; // 固定不动的圆的圆心初始化为屏幕的中心 mCenterX = screenWidth / 2; mCenterY = screenHeight / 2; // 未拖动的时候移动的圆的圆心和固定不动的圆的圆心在同一位置 mMovingX = mCenterX; mMovingY = mCenterY; // 未拖动的时候移动的圆的半径为0,固定不动的圆的半径为mRadiusSum mCenterRadius = mRadiusSum; mMovingRadius = 0; mLimit = (screenWidth > screenHeight?screenHeight:screenWidth) / 2.2f; mPath = new Path(); mPaint = new Paint(); mPaint.setColor(Color.parseColor("#ff0000")); mAnimator = ValueAnimator.ofFloat(1, 0).setDuration(500); mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float animationValue = (float) animation.getAnimatedValue(); mMovingX = mCenterX + (mEndX - mCenterX) * animationValue; mMovingY = mCenterY + (mEndY - mCenterY) * animationValue; float distance = calculateDistanceBetweenPoints(mCenterX, mCenterY, mMovingX, mMovingY); mCenterRadius = mRadiusSum - distance / FACTOR; mMovingRadius = mRadiusSum - mCenterRadius; updatePath(); invalidate(); } }); } // 更新画贝塞尔曲线的五个点的坐标以及其构成的相应路径 private void updatePath(){ if (mMovingX == mCenterX && mMovingY == mCenterY){ return; } // theta角度 double corner = Math.atan((mMovingY - mCenterY)/(mMovingX - mCenterX)); // 计算点P1和P4 float offsetX = (float)(mMovingRadius * Math.sin(corner)); float offsetY = (float)(mMovingRadius * Math.cos(corner)); // 点P1 float xP1 = mMovingX - offsetX; float yP1 = mMovingY + offsetY; // 点P4 float xP4 = mMovingX + offsetX; float yP4 = mMovingY - offsetY; // 计算点P2和P3 offsetX = (float)(mCenterRadius * Math.sin(corner)); offsetY = (float)(mCenterRadius * Math.cos(corner)); // 点P2 float xP2 = mCenterX - offsetX; float yP2 = mCenterY + offsetY; // 点P3 float xP3 = mCenterX + offsetX; float yP3 = mCenterY - offsetY; // 移动的圆和固定不动的圆的圆心的连线的中点Mid作为贝塞尔曲线的控制点 float midPointX = (mCenterX + mMovingX) / 2; float midPointY = (mCenterY + mMovingY) / 2; mPath.reset(); // P1作为起点 mPath.moveTo(xP1, yP1); // 点P1、Mid以及P2构成的二阶贝塞尔曲线,Mid作为贝塞尔曲线的控制点 mPath.quadTo(midPointX, midPointY, xP2, yP2); // 直线连接点P2和P3 mPath.lineTo(xP3, yP3); // 点P3、Mid以及P4构成的二阶贝塞尔曲线,Mid作为贝塞尔曲线的控制点 mPath.quadTo(midPointX, midPointY, xP4, yP4); // 直线连接点P4和P1 mPath.lineTo(xP1, yP1); } private float calculateDistanceBetweenPoints(float pointX1, float pointY1, float pointX2, float pointY2){ return (float) Math.sqrt(Math.pow(pointX1 - pointX2, 2) + Math.pow(pointY1 - pointY2, 2)); } @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); // 当前触摸点坐标 int x = (int) event.getX(); int y = (int) event.getY(); switch (action){ case MotionEvent.ACTION_DOWN: // 如果按下点不在有效区域内,则不处理该事件 if (x < mCenterX - mCenterRadius || x > mCenterX + mCenterRadius || y < mCenterY - mCenterRadius || y > mCenterY + mCenterRadius){ return false; } break; case MotionEvent.ACTION_MOVE: mMovingX = x; mMovingY = y; float distance = calculateDistanceBetweenPoints(mCenterX, mCenterY, mMovingX, mMovingY); if (distance > mLimit){ float temp = mLimit / distance; mMovingX = (mMovingX - mCenterX) * temp + mCenterX; mMovingY = (mMovingY - mCenterY) * temp + mCenterY; distance = mLimit; } mCenterRadius = mRadiusSum - distance / FACTOR; mMovingRadius = mRadiusSum - mCenterRadius; updatePath(); invalidate(); break; case MotionEvent.ACTION_UP: mEndX = mMovingX; mEndY = mMovingY; mAnimator.start(); break; } return true; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawCircle(mCenterX, mCenterY, mCenterRadius, mPaint); canvas.drawCircle(mMovingX, mMovingY, mMovingRadius, mPaint); canvas.drawPath(mPath, mPaint); } }
运行效果图:
相关文章推荐
- 仿QQ拖动删除未读消息个数气泡
- 实现类似QQ气泡消息的样式
- 仿QQ拖动删除未读消息个数气泡之二
- android 仿qq5.0 消息气泡拖动删除效果
- MFC实现类似qq的在屏幕右下角动画显示消息窗口
- C#中实现类似QQ的气泡提示
- devexpress显示缓冲滚动条与实现类似QQ消息推送效果
- winforms实现类似QQ消息框一样的右下角消息提示窗口
- Android实现类似QQ的滑动删除效果
- Android 实现类似QQ的 左边滑动删除
- 自定义ListView实现仿QQ消息列表滑动item出现删除按钮
- MFC实现类似qq的在屏幕右下角动画显示消息窗口 .
- C#实现类似QQ的隐藏浮动窗体、消息闪动
- ExtJs 实现类似QQ的提示消息框
- 类似QQ消息条数拖拽消失动画效果的实现
- listview 弹窗效果实现 +类似qq的滑动删除效果
- winforms实现类似QQ消息框一样的右下角消息提示窗口
- MFC实现类似QQ消息弹出框置顶但不抢用户输入焦点
- ExtJs实现类似QQ的右下角消息弹出框 (2013-04-01 19:04:43)转载▼
- SwipeListView实现QQ消息侧滑删除功能