自定义view——贝塞尔曲线之仿qq消息气泡拖拽让所有view拖动爆炸
2018-03-05 11:45
369 查看
之前写过一篇文章:自定义view——贝塞尔曲线之仿qq消息气泡拖拽
http://blog.csdn.net/qq_24675479/article/details/79430672
这里将完善上个项目,让其所有view都可以首先拖动爆炸
首先工具类
修改MessageBubbleView
BubbleMessageTouchListener:触摸事件终究处理类
http://blog.csdn.net/qq_24675479/article/details/79430672
这里将完善上个项目,让其所有view都可以首先拖动爆炸
首先工具类
public class BubbleUtils { /** * dip 转换成 px * * @param dip * @param context * @return */ public static int dip2px(float dip, Context context) { DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics); } /** * 获取状态栏的高度 */ public static int getStatusBarHeight(Context context) { int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { return context.getResources().getDimensionPixelOffset(resourceId); } return dip2px(25, context); } /** * 根据百分比获取两点之间的某个点坐标 */ public static PointF getPointByPercent(PointF p1,PointF p2,float percent){ return new PointF(evaluateValue(percent,p1.x,p2.x) ,evaluateValue(percent,p1.y,p2.y)); } /** * 根据分度值,计算从start到end中,fraction位置的值。fraction范围为0 -> 1 * * @param fraction * = 1 * @param start * = 10 * @param end * = 3 * @return */ public static float evaluateValue(float fraction, Number start, Number end) { // start = 10 end = 2 //fraction = 0.5 // result = 10 + (-8) * fraction = 6 return start.floatValue()+(end.floatValue()-start.floatValue())*fraction; } }
修改MessageBubbleView
public class MessageBubbleView extends View { // 两个圆的圆形 private PointF mFixationPoint, mDragPoint; // 拖拽圆的半径 private int mDragRadius = 10; private Paint mPaint; // 固定圆的最大半径(初始半径) private int mFixationRadiusMax = 7; private int mFixationRadiusMin = 3; private int mFixationRadius; private Bitmap mDragBitmap; public MessageBubbleView(Context context) { this(context, null); } public MessageBubbleView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public MessageBubbleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaint = new Paint(); mPaint.setColor(Color.RED); mPaint.setAntiAlias(true); mPaint.setDither(true); mDragRadius = dp2x(mDragRadius); mFixationRadiusMax = dp2x(mFixationRadiusMax); mFixationRadiusMin = dp2x(mFixationRadiusMin); } private int dp2x(int dpValue) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, getResources().getDisplayMetrics()); } @Override protected void onDraw(Canvas canvas) { if (mDragPoint == null || mFixationPoint == null) { return; } //绘制拖拽的圆 canvas.drawCircle(mDragPoint.x, mDragPoint.y, mDragRadius, mPaint); //获得两个圆之间的距离 double distance = getDistance(mDragPoint, mFixationPoint); mFixationRadius = (int) (mFixationRadiusMax - distance / 14); Path bezeierPath = getBezeierPath(); if (bezeierPath != null) {//当大于最小的时候才绘制 //绘制固定的圆 canvas.drawCircle(mFixationPoint.x, mFixationPoint.y, mFixationRadius, mPaint); //绘制贝塞尔曲线 canvas.drawPath(bezeierPath, mPaint); } if (mDragBitmap != null) { //绘制一个bitmap canvas.drawBitmap(mDragBitmap, mDragPoint.x - mDragBitmap.getWidth() / 2 , mDragPoint.y - mDragBitmap.getHeight() / 2, null); } } /** * 获取两个圆之间的距离 */ private double getDistance(PointF point1, PointF point2) { return Math.sqrt((point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y)); } /* @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: float downX = event.getX(); float downY = event.getY(); initPoint(downX, downY); break; case MotionEvent.ACTION_MOVE: float moveX = event.getX(); float moveY = event.getY(); updatePoint(moveX, moveY); break; case MotionEvent.ACTION_UP: break; } invalidate(); return true; }*/ /** * 更新拖拽点的坐标 */ public void updatePoint(float moveX, float moveY) { mDragPoint.x = moveX; mDragPoint.y = moveY; invalidate(); } /** * 初始化点 */ public void initPoint(float downX, float downY) { mFixationPoint = new PointF(downX, downY); mDragPoint = new PointF(downX, downY); invalidate(); } /** * 获得贝塞尔的曲线 * * @return */ public Path getBezeierPath() { if (mFixationRadius < mFixationRadiusMin) { // 超过一定距离 贝塞尔和固定圆都不要画了 return null; } Path bezeierPath = new Path(); //求角a //求斜率 float dy = mDragPoint.y - mFixationPoint.y; float dx = mDragPoint.x - mFixationPoint.x; float tanA = dy / dx; //求出角a double arcTanA = Math.atan(tanA); //p0 float p0x = (float) (mFixationRadius * Math.sin(arcTanA) + mFixationPoint.x); float p0y = (float) (mFixationPoint.y - mFixationRadius * Math.cos(arcTanA)); //p1 float p1x = (float) (mDragRadius * Math.sin(arcTanA) + mDragPoint.x); float p1y = (float) (mDragPoint.y - mDragRadius * Math.cos(arcTanA)); //p2 float p2x = (float) (mDragPoint.x - mDragRadius * Math.sin(arcTanA)); float p2y = (float) (mDragPoint.y + mDragRadius * Math.cos(arcTanA)); //p3 float p3x = (float) (mFixationPoint.x - mFixationRadius * Math.sin(arcTanA)); float p3y = (float) (mFixationPoint.y + mFixationRadius * Math.cos(arcTanA)); //绘制贝塞尔曲线 bezeierPath.moveTo(p0x, p0y); //绘制第一条线 PointF controlPoint = getControlPoint(); bezeierPath.quadTo(controlPoint.x, controlPoint.y, p1x, p1y);//控制点+目标坐标 //绘制到第二条 bezeierPath.lineTo(p2x, p2y); bezeierPath.quadTo(controlPoint.x, controlPoint.y, p3x, p3y); //最后闭合 bezeierPath.close(); return bezeierPath; } /** * 获取控制点 */ public PointF getControlPoint() { return new PointF((mDragPoint.x + mFixationPoint.x) / 2, (mDragPoint.y + mFixationPoint.y) / 2); } /** * 设置可拖拽的事件 */ public static void attach(View view, BubbleMessageTouchListener.BubbleDisappearListener disappearListener) { view.setOnTouchListener(new BubbleMessageTouchListener(view, view.getContext(),disappearListener)); } public void setDragBitmap(Bitmap dragBitmap) { this.mDragBitmap = dragBitmap; } //手指放开 public void handleActionUp() { if (mFixationRadius > mFixationRadiusMin) { //回弹 ValueAnimator 值变化的动画 0 变化到 1 ValueAnimator animator = ValueAnimator.ofFloat(0, 1); animator.setDuration(250); final PointF start=new PointF(mDragPoint.x,mDragPoint.y); final PointF end=new PointF(mFixationPoint.x,mFixationPoint.y); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float precent = (float) animation.getAnimatedValue();//获得当前百分比 PointF pointF=BubbleUtils.getPointByPercent(start,end,precent); //PointF pointF=BubbleUtils.getPointByPercent(mDragPoint,mFixationPoint,precent);//会没有回弹效果 updatePoint(pointF.x,pointF.y); } }); //差值器 回弹的时候弹一下 animator.setInterpolator(new OvershootInterpolator(3f)); animator.start(); // 还要通知 TouchListener 移除当前View 然后显示静态的 View animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { //动画结束,移除 if(mListener!=null){ mListener.restore(); } } }); } else { // 爆炸 if(mListener != null){ mListener.dismiss(mDragPoint); } } } private BubbleDisappearListener mListener; public void setListener(BubbleDisappearListener mListener) { this.mListener = mListener; } public interface BubbleDisappearListener { //恢复 void restore(); //爆炸消失 void dismiss(PointF mDragPoint); } }
BubbleMessageTouchListener:触摸事件终究处理类
public class BubbleMessageTouchListener implements View.OnTouchListener, MessageBubbleView.BubbleDisappearListener { // 原来需要拖动爆炸的View private View mStaticView; //消失后创建一个view private MessageBubbleView mMessageBubbleView; //创建一个windowManager private WindowManager mWindowManager; private WindowManager.La cba2 youtParams mParams; private Context mContext; //爆炸 private FrameLayout mBmobFrameLayout; private ImageView mBombImageView; BubbleDisappearListener mDisappearListener; public BubbleMessageTouchListener(View view, Context context, BubbleDisappearListener disappearListener) { this.mStaticView = view; mMessageBubbleView = new MessageBubbleView(context); mMessageBubbleView.setListener(this); mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); mParams = new WindowManager.LayoutParams(); //设置背景透明 mParams.format = PixelFormat.TRANSPARENT; this.mContext = context; this.mDisappearListener = disappearListener; //初始化爆炸 mBmobFrameLayout = new FrameLayout(mContext); mBombImageView = new ImageView(mContext); mBombImageView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT , ViewGroup.LayoutParams.WRAP_CONTENT)); mBmobFrameLayout.addView(mBombImageView); } @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mWindowManager.addView(mMessageBubbleView, mParams); //应该是中心位置而不是按下的位置 // mMessageBubbleView.initPoint(event.getRawX(), event.getRawY() - BubbleUtils.getStatusBarHeight(mContext)); int[] location = new int[2]; mStaticView.getLocationOnScreen(location); mMessageBubbleView.initPoint(location[0] + mStaticView.getWidth(), location[1] + mStaticView.getWidth() / 2 - BubbleUtils.getStatusBarHeight(mContext)); Bitmap bitmap = getBitmapByView(mStaticView); //给mMessageBubbleView添加一个拖拽的view mMessageBubbleView.setDragBitmap(bitmap); //控件消失 mStaticView.setVisibility(View.INVISIBLE); break; case MotionEvent.ACTION_MOVE: mMessageBubbleView.updatePoint(event.getRawX(), event.getRawY() - BubbleUtils.getStatusBarHeight(mContext)); break; case MotionEvent.ACTION_UP: mMessageBubbleView.handleActionUp(); break; } return true; } /** * 从view中获取bitmap */ private Bitmap getBitmapByView(View view) { view.buildDrawingCache(); Bitmap bitmap = view.getDrawingCache(); return bitmap; } //恢复 @Override public void restore() { mWindowManager.removeView(mMessageBubbleView); mStaticView.setVisibility(View.VISIBLE); } @Override public void dismiss(PointF pointF) { // 要去执行爆炸动画 (帧动画) // 原来的View的View肯定要移除 mWindowManager.removeView(mMessageBubbleView); mWindowManager.addView(mBmobFrameLayout, mParams); //添加一个动画效果 mBombImageView.setBackgroundResource(R.drawable.anim_bubble_pop); AnimationDrawable drawable = (AnimationDrawable) mBombImageView.getBackground(); //设置爆炸图片的位置 mBombImageView.setX(pointF.x - drawable.getIntrinsicWidth() / 2); mBombImageView.setY(pointF.y - drawable.getIntrinsicHeight() / 2); drawable.start(); // 等它执行完之后要移除掉这个 爆炸动画也就是 mBombFrame mBombImageView.postDelayed(new Runnable() { @Override public void run() { mWindowManager.removeView(mBmobFrameLayout); //通知外面要消失 mDisappearListener.dismiss(mStaticView); } }, getAnimationDrawableTime(drawable)); } private long getAnimationDrawableTime(AnimationDrawable drawable) { long time = 0; int frames = drawable.getNumberOfFrames(); for (int i = 0; i < frames; i++) { time += drawable.getDuration(i); } return time; } /** * 提供接口给外部 */ public interface BubbleDisappearListener { void dismiss(View view); } }
相关文章推荐
- 自定义view——贝塞尔曲线之仿qq消息气泡拖拽
- android自定义view之模拟qq消息拖拽删除效果
- Android自定义View-模仿QQ的拖拽气泡
- Android贝塞尔曲线初步学习第二课 仿QQ未读消息气泡拖拽黏连效果
- 贝塞尔曲线实现QQ未读消息气泡拖拽效果
- [置顶] 仿qq聊天消息长按弹窗(支持所有view及自定义属性扩展)
- 自定义View之仿QQ消息滑动删除
- Android 自定义实现类似QQ消息贝塞尔拖拽效果BezierView
- BezierDemo源码解析-实现qq消息气泡拖拽消失的效果
- 微信小程序之『仿 QQ 消息气泡拖拽消失』
- 仿QQ拖动删除未读消息个数气泡
- 自定义QQ消息红点拖动的效果
- android自定义View 之仿QQ消息头像
- android 仿qq5.0 消息气泡拖动删除效果
- 仿QQ消息导航栏RadioGroup里添加拖拽的TextView(未读消息)
- Android使用贝塞尔线高仿QQ聊天消息气泡的拖拽效果
- 高仿QQ未读消息气泡拖拽黏连效果
- 类似QQ拖动气泡删除消息的气泡实现
- 安卓仿手机QQ消息BadgeView气泡跟随手指移动,并实现进出动画效果。
- Android 仿QQ 5.0 气泡提示 拖动爆炸消除