效果图:

一、实现思路

在列表中默认使用自定义的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;
}

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

源码地址:

https://github.com/loren325/CustomerView

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

Android 实现仿QQ拖拽气泡效果的示例的更多相关文章

  1. html5 canvas合成海报所遇问题及解决方案总结

    这篇文章主要介绍了html5 canvas合成海报所遇问题及解决方案总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Html5 video标签视频的最佳实践

    这篇文章主要介绍了Html5 video标签视频的最佳实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. HTML5在微信内置浏览器下右上角菜单的调整字体导致页面显示错乱的问题

    HTML5在微信内置浏览器下,在右上角菜单的调整字体导致页面显示错乱的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  4. iOS实现拖拽View跟随手指浮动效果

    这篇文章主要为大家详细介绍了iOS实现拖拽View跟随手指浮动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. ios – containerURLForSecurityApplicationGroupIdentifier:在iPhone和Watch模拟器上给出不同的结果

    我使用默认的XCode模板创建了一个WatchKit应用程序.我向iOSTarget,WatchkitAppTarget和WatchkitAppExtensionTarget添加了应用程序组权利.(这是应用程序组名称:group.com.lombax.fiveminutes)然后,我尝试使用iOSApp和WatchKitExtension访问共享文件夹URL:延期:iOS应用:但是,测试NSURL

  6. Ionic – Splash Screen适用于iOS,但不适用于Android

    我有一个离子应用程序,其中使用CLI命令离子资源生成的启动画面和图标iOS版本与正在渲染的启动画面完美配合,但在Android版本中,只有在加载应用程序时才会显示白屏.我检查了config.xml文件,所有路径看起来都是正确的,生成的图像出现在相应的文件夹中.(我使用了splash.psd模板来生成它们.我错过了什么?这是config.xml文件供参考,我觉得我在这里做错了–解决方法在config.xml中添加以下键:它对我有用!

  7. ios – 无法启动iPhone模拟器

    /Library/Developer/CoreSimulator/Devices/530A44CB-5978-4926-9E91-E9DBD5BFB105/data/Containers/Bundle/Application/07612A5C-659D-4C04-ACD3-D211D2830E17/ProductName.app/ProductName然后,如果您在Xcode构建设置中选择标准体系结构并再次构建和运行,则会产生以下结果:dyld:lazysymbolbindingFailed:Symbol

  8. Xamarin iOS图像在Grid内部重叠

    heyo,所以在Xamarin我有一个使用并在其中包含一对,所有这些都包含在内.这在Xamarin.Android中看起来完全没问题,但是在Xamarin.iOS中,图像与标签重叠.我不确定它的区别是什么–为什么它在Xamarin.Android中看起来不错但在iOS中它的全部都不稳定?

  9. 在iOS上向后播放HTML5视频

    我试图在iPad上反向播放HTML5视频.HTML5元素包括一个名为playbackRate的属性,它允许以更快或更慢的速率或相反的方式播放视频.根据Apple’sdocumentation,iOS不支持此属性.通过每秒多次设置currentTime属性,可以反复播放,而无需使用playbackRate.这种方法适用于桌面Safari,但似乎在iOS设备上的搜索限制为每秒1次更新–在我的情况下太慢了.有没有办法在iOS设备上向后播放HTML5视频?解决方法iOS6Safari现在支持playbackRat

  10. Swift 分享多媒体消息到 微信/新浪微博/QQ

    考虑到在项目可能会有多个地方调用,因此对该类功能代码进行了整合,方便开发中调用。在此分享到微博希望对大家有所帮助。

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部