Android 实现仿QQ拖拽气泡效果的示例

目录
  • 效果图:
  • 一、实现思路
  • 二、功能实现
  • 三、全屏拖拽效果实现
  • 源码地址:

效果图:

一、实现思路

在列表中默认使用自定义的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拖拽气泡效果的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android自定义ListView实现仿QQ可拖拽列表功能

    我们大致的思路,其实是这样子的,也是我的设想,我们可以先去实现一个简单的ListView的数据,但是他的Adapter,我们可以用系统封装好的,然后传递进去一个实体类,最后自定义一个listview去操作,所以我们先把准备的工作做好,比如? list_item.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.a

  • Android仿QQ未读消息--红点拖拽删除【源代码】

    本Demo是一款仿qq未读消息拖拽删除的例子,继承RelativeLayout的WaterDrop实现了圆形图标功能.继承ImageView的CircleImageView圆形图片功能.效果非常不错.很适合有圆形设计的哥们.效果图片如下 CircleImageView核心代码 private void updateShaderMatrix() { float scale; float dx = 0; float dy = 0; mShaderMatrix.set(null); if (mBitm

  • Android Path绘制贝塞尔曲线实现QQ拖拽泡泡

    这两天学习了使用Path绘制贝塞尔曲线相关,然后自己动手做了一个类似QQ未读消息可拖拽的小气泡,效果图如下: 最终效果图 接下来一步一步的实现整个过程. 基本原理 其实就是使用Path绘制三点的二次方贝塞尔曲线来完成那个妖娆的曲线的.然后根据触摸点不断绘制对应的圆形,根据距离的改变改变原始固定圆形的半径大小.最后就是松手后返回或者爆裂的实现. Path介绍: 顾名思义,就是一个路径的意思,Path里面有很多的方法,本次设计主要用到的相关方法有 moveTo() 移动Path到一个指定的点 qua

  • android自定义view之模拟qq消息拖拽删除效果

    这个模拟功能的实现主要依靠了PATH和二阶贝塞尔曲线.首先上一张图来简单看一下: 这个模拟功能有以下几个特点: 在开始的时候点击圆以外的区域不会触发拖动事件 点击圆的时候可以拖拽,此时会有一个拉伸效果,连接大圆和小圆 拉伸到一定距离(自己设定)以后两个圆会断开,此时即使再拖拽进距离之内的时候也不会再产生已经断开的连接 在距离之内松手的时候会回弹会原位置,并伴有一个弹跳动画 介绍了这么多,看过我前边文章的朋友应该会有一个基本思路. 暴露接口 这个模拟功能共分为三部分,一个是那个小圆,固定的位置,一

  • Android使用贝塞尔曲线仿QQ聊天消息气泡拖拽效果

    本文实例为大家分享了Android仿QQ聊天消息气泡拖拽效果展示的具体代码,供大家参考,具体内容如下 先画圆,都会吧.代码如下: public class Bezier extends View { private final Paint mGesturePaint = new Paint(); private final Path mPath = new Path(); private float mX1 = 100, mY1 = 150; private float mX2 = 300, m

  • Android贝塞尔曲线初步学习第二课 仿QQ未读消息气泡拖拽黏连效果

    上一节初步了解了Android端的贝塞尔曲线,这一节就举个栗子练习一下,仿QQ未读消息气泡,是最经典的练习贝塞尔曲线的东东,效果如下 附上github源码地址:https://github.com/MonkeyMushroom/DragBubbleView 欢迎star~ 大体思路就是画两个圆,一个黏连小球固定在一个点上,一个气泡小球跟随手指的滑动改变坐标.随着两个圆间距越来越大,黏连小球半径越来越小.当间距小于一定值,松开手指气泡小球会恢复原来位置:当间距超过一定值之后,黏连小球消失,气泡小球

  • Android仿QQ消息提示点拖拽功能

    很久以前,发现QQ有一个很有趣的功能,就是未读消息的红点是可以拖拽的,而且在任何地方都可以随意拖拽,并且有一个弹性的动画,非常有趣,而且也是一个非常方便的功能,于是总想仿制一个,虽说仿制,但也只是他的拖拽功能,弹性效果还是能力有限. 不多说 先上效果 一个自定义的view 使用方式也很简单 <com.weizhenbin.show.widget.VanishView android:layout_width="30dp" android:layout_height="3

  • Android仿qq消息拖拽效果

    本文实例为大家分享了Android仿qq消息拖拽效果展示的具体代码,供大家参考,具体内容如下 这是一个仿qq消息拖拽效果,View和拖拽实现了分离,TextView.Button.Imageview等都可以实现相应的拖拽效果:在触发的地方调用 MessageBubbleView.attach(findViewById(R.id.text_view), new MessageBubbleView.BubbleDisappearListener() { @Override public void d

  • Android 实现仿QQ拖拽气泡效果的示例

    目录 效果图: 一.实现思路 二.功能实现 三.全屏拖拽效果实现 源码地址: 效果图: 一.实现思路 在列表中默认使用自定义的TextView控件来展示消息气泡,在自定义的TextView控件中重写onTouchEvent方法,然后在DOWN.MOVE.UP事件中分别处理拖拽效果. 整个拖拽效果我们可以拆分成以下几步来实现: 1.默认状态 2.两气泡相连状态 3.两气泡分离状态 4.气泡消失状态 二.功能实现 默认状态:用来做一个状态的标识,无需特别处理. 两气泡相连状态:绘制一个固定圆和一个移

  • Android ReboundScrollView仿IOS拖拽回弹效果

    初衷: 其实github上有很多这种ScrollView的项目,但是不得不说功能太多太乱了,我就只是想要一个简单效果的ScrollView,另外监听下滑动距离而已,想想还是自己写了个. 这里先说下思路吧,如果不愿意看的朋友可以直接跳过这一步,看下面的代码: Android 原生的ScrollView是不支持拉出屏幕外,并且也没有回弹效果的,用户友好度却不不太好,不知道为什么不那么设计. 我想做的事情正如上面所述: 1.希望能拉出屏幕外 2.松手后希望控件回弹 我的思路是对ScrollView的子

  • Android自定义View实现拖拽效果

    腾讯QQ有那种红点拖动效果,今天就来实现一个简单的自定义View拖动效果,再回到原处,并非完全仿QQ红点拖动. 先来看一下效果图 简单说一下实现步骤 1.创建一个类继承View 2.绘制出一个小球 3.重写onTouchEvent,来根据手指放下,移动,抬起,来控制小球 4.直接在布局中引用 先贴一张图看下View的坐标系 下面就贴一下代码,最后会给出源码 public class CustomView extends View { private int lastX; private int

  • Android开发仿QQ空间根据位置弹出PopupWindow显示更多操作效果

    我们打开QQ空间的时候有个箭头按钮点击之后弹出PopupWindow会根据位置的变化显示在箭头的上方还是下方,比普通的PopupWindow弹在屏幕中间显示好看的多. 先看QQ空间效果图: 这个要实现这个效果可以分几步进行 1.第一步自定义PopupWindow,实现如图的样式,这个继承PopupWindow自定义布局很容易实现 2.得到点击按钮的位置,根据位置是否在屏幕的中间的上方还是下方,将PopupWindow显示在控件的上方或者下方 3.适配问题,因为PopupWindow上面的操作列表

  • Android UI仿QQ好友列表分组悬浮效果

    本文实例为大家分享了Android UI仿QQ好友列表分组悬浮效果的具体代码,供大家参考,具体内容如下 楼主是在平板上測试的.图片略微有点大,大家看看效果就好 接下来贴源代码: PinnedHeaderExpandableListView.java 要注意的是 在 onGroupClick方法中parent.setSelectedGroup(groupPosition)这句代码的作用是点击分组置顶, 我这边不须要这个效果.QQ也没实用到,所以给凝视了.大家假设须要能够解开凝视 package c

  • Android仿qq顶部消息栏效果

    android仿照qq的顶部栏效果,主要就是利用fragment manager把fragment设置显示内容 (1)在activity_main.xml布局中添加控件 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="

  • Android RecycleView实现Item拖拽效果

    基于公司产品的优化需求,其中一个需求涉及到RecycleView的拖拽,以及拖拽后item位置的持久化,目的是可以用户自定义界面偏好,并在用户下次进入本界面后,之前设置的偏好仍然有效.我写了一个小Demo用作演示效果. 先看效果(只看效果,不看颜值) 步骤1.建接口文件ItemTouchHelperViewHolder,该接口文件中描述的是选中和放开当前Item调用的方法. public interface ItemTouchHelperViewHolder { void onItemSelec

  • Android高仿QQ小红点功能

    先给大家展示下效果图: 代码已上传至Github:高仿QQ小红点,如对您有帮助,欢迎star~感谢 绘制贝塞尔曲线: 主要是当在一定范围内拖拽时算出固定圆和拖拽圆的外切直线以及对应的切点,就可以通过path.quadTo()来绘制二阶贝塞尔曲线了~ 整体思路: 1.当小红点静止时,什么都不做,只需要给自定义小红点QQBezierView(extends TextView)添加一个.9文件当背景即可 2.当滑动时,通过getRootView()获得顶级根View,然后new一个DragView (

随机推荐