此次的自定义View是仿qq消息列表,消息气泡拖拽效果。

1.原理介绍:自定义view,绘制原始点圆,touch点圆,然后将两圆用贝塞尔曲线连接并填充。

2.应用WindowManager,将自定义view添加到屏幕中区,全屏布局,根据相关坐标,计算出目标view在屏幕中的坐标,并将目标view(消息气泡view)绘制到自定义view中。

3,源码解析
1)按中目标view代码操作

/*** 开始拖动* @param target*/public void dragStart(View target){convertViewToBitmap(target);//将目标view转换成bitmap,ondraw绘制时用final int[] locations = new int[2];target.getLocationOnScreen(locations);//目标view在屏幕中的坐标windowManagerAddView(getContext(),this);//添加到屏幕中this.initPoints(locations[0],locations[1],target.getWidth(),target.getHeight());//初始化相关坐标}

2)开始拖动代码操作

/*** 用户移动view,并更新* @param x* @param y*/public void updatePoints(float x,float y){touchX = x;touchY = y - getStatusBarHeight();centerX = startCircleX/2 +touchX/2;centerY = startCircleY/2 +touchY/2;invalidate();}

3)拖动抬起代码操作

/*** 拖动结束*/public void dragFinish(){if (onDragFinishListener != null){onDragFinishListener.onDragFinish();//结束监听器}if (isArriveMaxDistance){//拖动超出最大距离windowManagerRemoveView();//移除自定view}else {startRollBackAnimation(1000);//回弹动画效果}}

4)ondraw方法解析

 @Overrideprotected void onDraw(Canvas canvas) {calculate();//计算贝塞尔曲线if (!isArriveMaxDistance){canvas.drawCircle(startCircleX,startCircleY,startRadius,mPaint);//起始圆canvas.drawCircle(touchX,touchY,endRadius,mPaint);//触摸点圆canvas.drawPath(mPath,mPaint);//两圆之间的路径绘制}canvas.drawBitmap(mDest,touchX - mDest.getWidth()/2f,touchY - mDest.getHeight()/2f,mPaint);//目标view的绘制}

5)贝塞尔曲线的绘制

 /*** 计算贝塞尔曲线*/private void calculate(){float distance = (float) Math.sqrt(Math.pow(touchX - startCircleX,2)+Math.pow(touchY - startCircleY,2));startRadius = -distance/15 +endRadius;if (startRadius <8){startRadius = 8;}if (distance > GraphicUtils.dip2px(getContext(),maxDistance)){isArriveMaxDistance = true;}else {//根据两个圆心算出三角函数角度double angle = Math.atan((touchY - startCircleY)/(touchX - startCircleX));float offsetX = (float) (startRadius*Math.sin(angle));float offsetY = (float) (startRadius*Math.cos(angle));float x1 = startCircleX - offsetX;float y1 = startCircleY + offsetY;float x4 = startCircleX + offsetX;float y4 = startCircleY - offsetY;offsetX = (float) (endRadius*Math.sin(angle));offsetY = (float) (endRadius*Math.cos(angle));float x2 = touchX - offsetX;float y2 = touchY + offsetY;float x3 = touchX + offsetX;float y3 = touchY - offsetY;mPath.reset();mPath.moveTo(x1,y1);mPath.quadTo(centerX,centerY,x2,y2);mPath.lineTo(x3,y3);mPath.quadTo(centerX,centerY,x4,y4);mPath.lineTo(x1,y1);}}

4.自定义view源代码

/*** TODO: document your custom view class.*/
public class BezierView extends View {private OnDragFinishListener onDragFinishListener;private Paint mPaint;//绘图路径private Path mPath;//private float maxRadius;//两圆的半径差private float diffRadius = 2;//起始圆半径private float startRadius;//拖动圆的半径private float endRadius;//起始圆心 在屏幕中的坐标起点private float startCircleX;private float startCircleY;//触摸点 相对viewprivate float touchX;private float touchY;//控制点 相对viewprivate float centerX;private float centerY;//最大距离private int maxDistance = 100;//是否超出最大距离private boolean isArriveMaxDistance;private Bitmap mDest;public void setMaxRadius(float maxRadius) {this.maxRadius = maxRadius;}public void setMaxDistance(int maxDistance) {this.maxDistance = maxDistance;}public BezierView(Context context) {super(context);init(null, 0);}public BezierView(Context context, AttributeSet attrs) {super(context, attrs);init(attrs, 0);}public BezierView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(attrs, defStyle);}private void init(AttributeSet attrs, int defStyle) {mPath = new Path();mPaint = new Paint();mPaint.setColor(Color.RED);mPaint.setAntiAlias(true);maxRadius = GraphicUtils.dip2px(getContext(),10);}/*** 用户按下并初始化* @param startX* @param startY* @param width* @param height*/public void  initPoints(float startX,float startY,int width,int height){endRadius = Math.min(width,height)/2;if (endRadius >maxRadius){endRadius = maxRadius;}startRadius = endRadius-diffRadius;startCircleX = startX + width/2;startCircleY = startY + height/2 - getStatusBarHeight();touchX = startCircleX;touchY = startCircleY;centerX = startCircleX/2 +touchX/2;centerY = startCircleY/2 +touchY/2;invalidate();}/*** 开始拖动* @param target*/public void dragStart(View target){convertViewToBitmap(target);final int[] locations = new int[2];target.getLocationOnScreen(locations);windowManagerAddView(getContext(),this);this.initPoints(locations[0],locations[1],target.getWidth(),target.getHeight());}/*** 用户移动view,并更新* @param x* @param y*/public void updatePoints(float x,float y){touchX = x;touchY = y - getStatusBarHeight();centerX = startCircleX/2 +touchX/2;centerY = startCircleY/2 +touchY/2;invalidate();}/*** 拖动结束*/public void dragFinish(){if (onDragFinishListener != null){onDragFinishListener.onDragFinish();}if (isArriveMaxDistance){windowManagerRemoveView();}else {startRollBackAnimation(1000);}}/*** 将view转换成bitmap* @param view* @return*/private Bitmap convertViewToBitmap(View view) {int width = view.getWidth();int height = view.getHeight();mDest = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);//定义一个指定位图的画布,来绘制内容Canvas canvas = new Canvas(mDest);//将view的内容绘制到bitmap上view.draw(canvas);return mDest;}/*** 回滚状态动画*/private void startRollBackAnimation(long duration) {//属性动画弹性效果 缓动函数ValueAnimator rollBackAnim = ObjectAnimator.ofObject(new PointEvaluator(duration),new Point(touchX,touchY),new Point(startCircleX,startCircleY));rollBackAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {Point point = (Point) animation.getAnimatedValue();touchX = point.getX();touchY = point.getY();centerX = startCircleX/2 +touchX/2;centerY = startCircleY/2 +touchY/2;postInvalidate();}});rollBackAnim.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {super.onAnimationEnd(animation);//可以自己定义监听,进行目标view的处理BezierView.this.clearAnimation();windowManagerRemoveView();}});rollBackAnim.setDuration(duration);rollBackAnim.start();}@Overrideprotected void onDraw(Canvas canvas) {calculate();if (!isArriveMaxDistance){canvas.drawCircle(startCircleX,startCircleY,startRadius,mPaint);canvas.drawCircle(touchX,touchY,endRadius,mPaint);canvas.drawPath(mPath,mPaint);}canvas.drawBitmap(mDest,touchX - mDest.getWidth()/2f,touchY - mDest.getHeight()/2f,mPaint);}/*** 计算贝塞尔曲线*/private void calculate(){float distance = (float) Math.sqrt(Math.pow(touchX - startCircleX,2)+Math.pow(touchY - startCircleY,2));startRadius = -distance/15 +endRadius;if (startRadius <8){startRadius = 8;}if (distance > GraphicUtils.dip2px(getContext(),maxDistance)){isArriveMaxDistance = true;}else {//根据两个圆心算出三角函数角度double angle = Math.atan((touchY - startCircleY)/(touchX - startCircleX));float offsetX = (float) (startRadius*Math.sin(angle));float offsetY = (float) (startRadius*Math.cos(angle));float x1 = startCircleX - offsetX;float y1 = startCircleY + offsetY;float x4 = startCircleX + offsetX;float y4 = startCircleY - offsetY;offsetX = (float) (endRadius*Math.sin(angle));offsetY = (float) (endRadius*Math.cos(angle));float x2 = touchX - offsetX;float y2 = touchY + offsetY;float x3 = touchX + offsetX;float y3 = touchY - offsetY;mPath.reset();mPath.moveTo(x1,y1);mPath.quadTo(centerX,centerY,x2,y2);mPath.lineTo(x3,y3);mPath.quadTo(centerX,centerY,x4,y4);mPath.lineTo(x1,y1);}}/*** 向窗口添加view*/WindowManager windowManager;private void windowManagerAddView(Context context,View view){WindowManager.LayoutParams params = new WindowManager.LayoutParams();//全屏MATCH_PARENTparams.format = PixelFormat.TRANSLUCENT;params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;//后面窗口仍然可以处理点设备事件windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);windowManager.addView(view, params);}//移除viewprivate void windowManagerRemoveView(){if (windowManager != null){windowManager.removeView(this);}}/*** 获取状态栏高度* @return*/public int getStatusBarHeight() {int result = 0;int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");if (resourceId > 0) {result = getResources().getDimensionPixelSize(resourceId);}return result;}/*** 拖动抬起监听* @param onDragFinishListener*/public void setOnDragFinishListener(OnDragFinishListener onDragFinishListener) {this.onDragFinishListener = onDragFinishListener;}/*** 自定义监听器*/public interface OnDragFinishListener{void onDragFinish();}
}

动画 缓动函数

/*** Created by Administrator on 2016/4/6.* 缓动函数*/
public class PointEvaluator implements TypeEvaluator {private float mDuration;public PointEvaluator(float mDuration) {this.mDuration = mDuration;}@Overridepublic Object evaluate(float fraction, Object startValue, Object endValue) {Point startPoint = (Point) startValue;Point endPoint = (Point) endValue;float t = mDuration * fraction;float x = calculate(t,startPoint.getX(),endPoint.getX() - startPoint.getX(),mDuration);float y = calculate(t,startPoint.getY(),endPoint.getY() - startPoint.getY(),mDuration);return new Point(x,y);}public Float calculate(float t, float b, float c, float d) {double s=1.70158;float a=c;if (t==0)return b;if ((t/=d)==1)return b+c;float p= (float) (d*.3);if (a < Math.abs(c)) {a=c;s=p/4;}else{s = p/(2*Math.PI) * Math.asin (c/a);}return a*(float) Math.pow(2,-10*t) * (float) Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;}
}

6.使用方法 mBezierViewTest 目标view

 mBezierViewTest.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:mBezierView = new BezierView(CustomViewsActivity.this);if (mParent != null){mParent.requestDisallowInterceptTouchEvent(true);//父view禁止滚动(scrollview,listview等)}mBezierView.dragStart(mBezierViewTest);break;case MotionEvent.ACTION_MOVE:mBezierViewTest.setVisibility(View.INVISIBLE);mBezierView.updatePoints(event.getRawX(),event.getRawY());break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:if (mParent != null){mParent.requestDisallowInterceptTouchEvent(false);}mBezierViewTest.setVisibility(View.VISIBLE);mBezierView.dragFinish();break;}return true;}});

仿QQ消息气泡拖拽效果相关推荐

  1. 微信小程序之『仿 QQ 消息气泡拖拽消失』

    转载:请写明掘金原文链接及作者名 '小小小' 一个潜心研究小程序QQ群:139128168 ← 点击加群 今天带来的是仿QQ消息气泡拖拽消失特效,源码中很多地方还是有很多不足,希望大家一起齐心协力,给 ...

  2. android气泡聊天消息背景,Android使用贝塞尔曲线仿QQ聊天消息气泡拖拽效果

    本文实例为大家分享了Android仿QQ聊天消息气泡拖拽效果展示的具体代码,供大家参考,具体内容如下 先画圆,都会吧.代码如下: public class Bezier extends View { ...

  3. Android qq消息气泡实现效果,BezierDemo源码解析-实现qq消息气泡拖拽消失的效果

    这篇文章中我们比较了DraggableFlagView和BezierDemo两个项目的区别,提到将对其中一个做源码分析,那么我们就来分析BezierDemo的源码吧,因为这个项目的源码最简单,可以更直 ...

  4. Android 贝塞尔曲线——类似QQ红点拖拽效果

    在Android绘制中,提供了更为丰富绘制api--Path类,包括直线,二阶贝塞尔曲线,三阶贝塞尔曲线,弧形,圆,椭圆,圆角矩形等等,path的绘制最终是调用了C中的绘制方法. 下面来看一下常用的几 ...

  5. android qq消息数 拖拽动画,史上最详细仿QQ未读消息拖拽粘性效果的实现

    好久没写文章了,前段时间由于项目代码重构忙了一段时间,现在终于有点时间了就为大家带来一篇关于动画学习的自定义View:类似QQ消息拖拽的效果. 其实QQ当时更新的时候我还没注意到这个小红点是可以拖拽的 ...

  6. vuedraggable能实现自由拖拽功能吗?_基于 vue.js 仿禅道主页拖拽效果

    今天给大家分享一个超不错的Vue仿禅道首页拖拽布局VueDndKon. vue-dnd-kon 基于vuedraggable实现的仿禅道首页拖拽项目.支持模块上下及左右自由拖动布局. 主页分为左右两栏 ...

  7. 仿百度地图抽屉拖拽效果

    ScrollLayout ScrollTo ScrollBy 事件传递 ScrollTo.ScrollBy说明 ScrollTo和ScrollBy滑动的是该View的显示内容(子View),并不改变该 ...

  8. Android qq消息气泡实现效果,Android 实现仿QQ拖拽气泡效果的示例

    效果图: 一.实现思路 在列表中默认使用自定义的TextView控件来展示消息气泡,在自定义的TextView控件中重写onTouchEvent方法,然后在DOWN.MOVE.UP事件中分别处理拖拽效 ...

  9. Android仿QQ消息拖拽效果(二)

    前言 本文参考辉哥贝塞尔曲线 - QQ消息汽包拖拽,前面我们使用二阶贝塞尔曲线绘制了拖拽圆点效果Android仿QQ消息拖拽效果(一)(二阶贝塞尔曲线使用),这里我们在此基础之上实现仿QQ消息拖拽爆炸 ...

最新文章

  1. systemd进程管理工具实战教程
  2. R语言基础与入门实践
  3. ES6 实用开发技巧
  4. ASP.NET基础教程-以查询字符串的方式在两个页面之间传递信息
  5. html、css、js注释,js,html,css注释大集合
  6. php中gd为什么是乱码的,php gd库中文乱码怎么解决?
  7. OpenCV学习1--介绍与环境搭建
  8. server取出多个最小值 sql_SQL汇总查询及分组查询
  9. 开源,才是科技巨头的最佳实践!| 技术头条
  10. 使用QGIS将文本坐标转换为矢量文件
  11. CAN FD协议描述
  12. 华为盒子-悦MEC6108V9C-强刷固件-4.4.2版本
  13. STM32 HAL库学习笔记3-HAL库外设驱动框架概述
  14. java 读写锁_Java 读写锁的实现
  15. powershell快捷键_Windows10 PowerShell快捷键大全
  16. Playrix Codescapes Cup (Codeforces Round #413, rated, Div. 1 + Div. 2) F. Beautiful fountains rows
  17. 1997年小学生计算机知识竞赛,2019年中小学优秀传统文化知识竞赛试题(小学组)97题附全答案...
  18. 52 jQuery-使用fadeIn()和fadeOut()方法实现淡入淡出效果
  19. linux微信原生版2.1.5,优麒麟版重新打包,适用于debian内核系统
  20. Atom汉化之Atom-Simplified-Chinese-Menu

热门文章

  1. 基于结构应力方法的焊接结构疲劳评估及实例分析(上篇)
  2. win10 sshfs 挂载linux文件夹
  3. 北京黑马面授java基础_北京顺义黑马JavaEE基础100期(20190324面授)——开班贴
  4. 三招教你如何选择企业网盘
  5. Vue3和Vue2对比,我们如何选用?
  6. 史上最全免费下载文献网站汇总!来吧!展示!
  7. 能在多种前端框架下使用的表格控件
  8. Javascript 教程/速成/全面 /总结【含代码】
  9. Android10.0 获取不到IMEI问题
  10. Java大型医院电子病历管理系统源码 B/S架构+MYSQL数据库 病历过四级 SaaS服务