效果图:

一、实现思路

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

整个拖拽效果我们可以拆分成以下几步来实现:

1.默认状态

2.两气泡相连状态

3.两气泡分离状态

4.气泡消失状态

二、功能实现

默认状态:用来做一个状态的标识,无需特别处理。

两气泡相连状态:绘制一个固定圆和一个移动圆,使用两条贝塞尔曲线来实现两气泡连接的曲线,两条贝塞尔曲线共用同一个控制点,然后根据MOVE事件中的坐标不断重绘移动圆。

实现两气泡连接的效果,需要先计算出一些点的坐标,这也是整个拖拽气泡效果的核心部分,具体如下图:

如图,A点到B点是一条二阶贝塞尔曲线,C点到D点也是一条二阶贝塞尔曲线,它们共用同一个控制点,所以我们要计算出A点、B点、C点、D点以及控制点的坐标。

首先来计算控制点的坐标,控制点的坐标和容易计算出,也就是固定圆的x坐标加上移动圆的x坐标,再除以2,固定圆的y坐标同理得出。

int controlX = (int) ((mBubStillCenter.x + mBubMoveCenter.x) / 2);

int controlY = (int) ((mBubStillCenter.y + mBubMoveCenter.y) / 2);

根据图中所标注的信息得知,∠a=∠d,∠b=∠c,∠a=∠θ,由此可知,我们求出∠θ所在的直角三角形的sin和cos值,就可以计算出A点、B点、C点、D点的坐标。

sin值可以通过移动圆的y坐标减去固定圆的y坐标,再除以两圆心的距离,也就是O1到O2的距离。

cos值可以通过移动圆的x坐标减去固定圆的x坐标,再除以两圆心的距离。

float sin = (mBubMoveCenter.y - mBubStillCenter.y) / mDist;

float cos = (mBubMoveCenter.x - mBubStillCenter.x) / mDist;

有了sin和cos值,对应的A点、B点、C点、D点的坐标就好计算了

// A点

float bubbleStillStartX = mBubStillCenter.x + mBubbleStillRadius * sin;

float bubbleStillStartY = mBubStillCenter.y - mBubbleStillRadius * cos;

// B点

float bubbleMoveStartX = mBubMoveCenter.x + mBubbleMoveRadius * sin;

float bubbleMoveStartY = mBubMoveCenter.y - mBubbleMoveRadius * cos;

// C点

float bubbleMoveEndX = mBubMoveCenter.x - mBubbleMoveRadius * sin;

float bubbleMoveEndY = mBubMoveCenter.y + mBubbleMoveRadius * cos;

// D点

float bubbleStillEndX = mBubStillCenter.x - mBubbleStillRadius * sin;

float bubbleStillEndY = mBubStillCenter.y + mBubbleStillRadius * cos;

接下来就是把这些贝塞尔曲线和直线连起来,就实现了两气泡相连的效果。

两气泡分离状态:当拖拽的移动圆超出固定圆一定范围时,就进入了两气泡分离状态,此时我们只需要绘制移动圆即可。当拖拽的移动圆回到固定圆一定范围时,此时会进入两气泡相连状态,并且需要实现一个气泡还原的效果。(这里会有个难点,就是移动圆我们可以在屏幕上任意拖动而不被遮挡,这里放到后面来实现。)

public void move(float curX, float curY) {

mBubMoveCenter.x = curX;

mBubMoveCenter.y = curY;

mDist = (float) Math.hypot(curX - mBubStillCenter.x, curY - mBubStillCenter.y);

if(mBubbleState == BUBBLE_STATE_CONNECT){

if(mDist < mMaxDist - MOVE_OFFSET){

mBubbleStillRadius = mBubbleRadius - mDist / 10;

}else {

mBubbleState = BUBBLE_STATE_APART;

}

}

invalidate();

}

mDist就是两圆心的距离。

/**

* 气泡还原动画

*/

private void startBubbleRestAnim() {

mBubbleStillRadius = mBubbleRadius;

ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(), new PointF(mBubMoveCenter.x, mBubMoveCenter.y), new PointF(mBubStillCenter.x, mBubStillCenter.y));

animator.setDuration(200);

animator.setInterpolator(input -> {

float factor = 0.4f;

return (float) (Math.pow(2, -10 * factor) * Math.sin((input - factor / 4) * (2 * Math.PI) / factor) + 1);

});

animator.addUpdateListener(animation -> {

mBubMoveCenter = (PointF) animation.getAnimatedValue();

invalidate();

});

animator.addListener(new AnimatorListenerAdapter() {

@Override

public void onAnimationEnd(Animator animation) {

mBubbleState = BUBBLE_STATE_DEFAULT;

removeDragView();

if(mDragListener != null){

mDragListener.onRestore();

}

}

});

animator.start();

}

分享一个可视化插值器的网站,其中内置了一些插值器公式,还可以查看动画演示效果。http://inloop.github.io/interpolator/

气泡消失状态:当拖拽的移动圆超出一定范围时,并且松开了手指后,此时进入气泡消失状态,此时我们需要实现一个爆炸的动画。

爆炸的动画通过绘制一组图片来实现

if(mBubbleState == BUBBLE_STATE_DISMISS){

if(mIsBurstAnimStart){

mBurstRect.set((int)(mBubMoveCenter.x - mBubbleMoveRadius), (int)(mBubMoveCenter.y - mBubbleMoveRadius),

(int)(mBubMoveCenter.x + mBubbleMoveRadius), (int)(mBubMoveCenter.y + mBubbleMoveRadius));

canvas.drawBitmap(mBurstBitmapArray[mCurDrawableIndex], null, mBurstRect, mBurstPaint);

}

}

mCurDrawableIndex是图片的索引,是通过属性动画来改变

/**

* 气泡爆炸动画

*/

private void startBubbleBurstAnim() {

ValueAnimator animator = ValueAnimator.ofInt(0, mBurstDrawablesArray.length);

animator.setInterpolator(new LinearInterpolator());

animator.setDuration(1000);

animator.addUpdateListener(animation -> {

mCurDrawableIndex = (int) animator.getAnimatedValue();

invalidate();

});

animator.addListener(new AnimatorListenerAdapter() {

@Override

public void onAnimationEnd(Animator animation) {

mIsBurstAnimStart = false;

if(mDragListener != null){

mDragListener.onDismiss();

}

}

});

animator.start();

}

三、全屏拖拽效果实现

首先在DOWN事件中获取当前触摸位置在全屏所在位置,然后将当前view缓存为bitmap,并把此bitmap添加到rootview中,拖动的时候直接绘制此bitmap。

//获得当前View在屏幕上的位置

int[] cLocation = new int[2];

getLocationOnScreen(cLocation);

if(rootView instanceof ViewGroup){

mDragDotView = new DragDotView(getContext());

//设置固定圆和移动圆的圆心坐标

mDragDotView.setDragPoint(cLocation[0] + mWidth / 2, cLocation[1] + mHeight / 2, mRawX, mRawY);

Bitmap bitmap = getBitmapFromView(this);

if(bitmap != null){

mDragDotView.setCacheBitmap(bitmap);

((ViewGroup) rootView).addView(mDragDotView);

setVisibility(INVISIBLE);

}

}

/**

* 将当前view缓存为bitmap,拖动的时候直接绘制此bitmap

* @param view

* @return

*/

public Bitmap getBitmapFromView(View view)

{

Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(bitmap);

view.draw(canvas);

return bitmap;

}

至此,整个消息气泡拖拽效果的核心部分就实现了

源码地址:

以上就是Android 实现仿QQ拖拽气泡效果的示例的详细内容,更多关于Android 实现仿QQ拖拽气泡效果的资料请关注脚本之家其它相关文章!

Android qq消息气泡实现效果,Android 实现仿QQ拖拽气泡效果的示例相关推荐

  1. Android仿Ios下拉回弹,Android ReboundScrollView仿IOS拖拽回弹效果

    初衷: 其实github上有很多这种ScrollView的项目,但是不得不说功能太多太乱了,我就只是想要一个简单效果的ScrollView,另外监听下滑动距离而已,想想还是自己写了个. 这里先说下思路 ...

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

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

  3. android开发之仿QQ拖拽界面效果(侧滑面板)

    仿QQ拖拽界面效果(侧滑面板),我们一般继承Layout,不会直接去继承ViewGroup,而是继承FrameLayout,为什么五大布局我们偏偏只继承FrameLayout呢? 第一,FrameLa ...

  4. Android 同步QQ消息,王者荣耀QQ消息同步功能是什么 王者荣耀QQ消息同步功能介绍...

    王者荣耀QQ消息同步功能是什么?王者荣耀QQ消息同步功能在4月8日起以逐步开启,不日将于各服务器中实装,但想必还有不少玩家对QQ消息同步功能不是很了解,不知道QQ消息同步功能是的作用和使用方法,下面小 ...

  5. Android自定义View-模仿QQ的拖拽气泡

    第一步:明白气泡的几个状态 第二步:明白大概的思路 第三步:初始化组价的属性,并计算组件的大小 第四步:通过点击,拖动,取消,分别获取到两点之间的距离,然后得到组件的各个状态 第五步:绘制图像 1.如 ...

  6. 贝塞尔曲线(Bezier)之 QQ 消息拖拽动画效果

    博主声明: 转载请在开头附加本文链接及作者信息,并标记为转载.本文由博主 威威喵 原创,请多支持与指教. 本文首发于此   博主:威威喵  |  博客主页:https://blog.csdn.net/ ...

  7. android列表实现置顶,Android利用RecyclerView实现全选、置顶和拖拽功能示例

    Android利用RecyclerView实现全选.置顶和拖拽功能示例 发布时间:2020-08-23 16:26:42 来源:脚本之家 阅读:159 作者:爱开发 前言 今天给大家分享是如何在Rec ...

  8. android rebound平移,Android 仿 IOS 拖拽回弹之进阶 ReboundFrameLayout

    Android 仿 IOS 拖拽回弹之进阶 ReboundFrameLayout 前言 IOS 拖拽回弹给用户的体验不得不赞然后 Android 原生的 API 各种不支持, 于是乎出现的很多仿 IO ...

  9. react实现的点击拖拽元素效果

    之前用vue做日程管理组件的时候,用到了点击拖拽的效果,即点击元素,鼠标移动到哪里,元素移动到哪里,鼠标松开,拖拽停止,现在在弄react,于是也在想实现这个效果,经过一番折腾,效果出来了,代码如下: ...

最新文章

  1. 苹果发布被拒绝的种种惨剧
  2. 路径前面加/和不加/
  3. [转载] 人工智能:一种现代方法——第1章 绪论
  4. RobotFramework自动化4-批量操作案例
  5. Wince5.0自定义工具条
  6. selenium 2定位方式实例
  7. bzoj 3208 花神的秒题计划I
  8. 手机号检测性别原理分析 微信男女 抖音ID检测原理
  9. wifi密码破解案列
  10. 《沉思录》读书精摘——对伦理学的古典思考
  11. 旧电脑利用:windows网页版魔镜【不需树莓派】
  12. 活着,我们都想要别人的理解
  13. 脑裂产生以及解决办法(转载)
  14. 计算机拼歌曲,抖音你这辈子有没有为别人拼过命是什么歌
  15. 稳定性、鲁棒性、与非脆弱性的区别
  16. FreeMarkerBase
  17. Discus论坛System Error界面修改标语
  18. 短信验证码登录,以及第三方登录
  19. Python 面向对象编程(一) —— 面向过程和面向对象的区别
  20. Frameworks Detected: Web framework is detected. // Configure (24 minutes ago)解决办法

热门文章

  1. 分枝限界法求解任务分配问题
  2. 我的世界1.18java,我的世界Java版1.16.5-rc1版本
  3. 求职与学历、考研与学历
  4. 【CCNA证书需要什么资格?】
  5. 我的世界java如何加好友进世界_我的世界中国版怎么添加好友 好友开黑联网方法...
  6. webmoney 接口
  7. android机器人方向,Android绘制机器人小实例
  8. Cocos 人才在线教育专场招聘 | 12月岗位
  9. 957 N 天后的牢房
  10. 怎么做品牌百度百科,品牌信息怎么上传到百度百科上