Android未读消息拖动气泡示例代码详解(附源码)

前言

拖动清除未读消息可以说在很多应用中都很常见,也被用户广泛接受。本文是一个可以供参考的Demo,希望能有帮助。

提示:以下是本篇文章正文内容,下面案例可供参考

最终效果图及思路


实现关键

气泡中间的两条边,分别是以ab,cd为数据点,G为控制点的贝塞尔曲线。

步骤

绘制圆背景以及文本;连接情况绘制贝塞尔曲线;另外端点绘制一个圆

关键代码

1.定义,初始化等

状态:静止、连接、分离、消失
在onSizeChanged中初始化状态,固定气泡以及可动气泡的圆心

代码如下(示例):

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
 super.onSizeChanged(w, h, oldw, oldh);

 init(w, h);
}

private void init(int w, int h) {
 mBubbleState = BUBBLE_STATE_DEFAULT;

 //设置固定气泡圆心初始坐标
 if (mBubFixedCenter == null) {
 mBubFixedCenter = new PointF(w / 2, h / 2);
 } else {
 mBubFixedCenter.set(w / 2, h / 2);
 }
 //设置可动气泡圆心初始坐标
 if (mBubMovableCenter == null) {
 mBubMovableCenter = new PointF(w / 2, h / 2);
 } else {
 mBubMovableCenter.set(w / 2, h / 2);
 }
}

2.onDraw中绘制包括三样绘制

第一样:静止,连接,分离状态都需要绘制圆背景以及文本:

//静止,连接,分离状态都需要绘制圆背景以及文本
if (mBubbleState != BUBBLE_STATE_DISMISS) {
 canvas.drawCircle(mBubMovableCenter.x, mBubMovableCenter.y, mBubMovableRadius, mBubblePaint);
 mTextPaint.getTextBounds(mTextStr, 0, mTextStr.length(), mTextRect);
 canvas.drawText(mTextStr, mBubMovableCenter.x - mTextRect.width() / 2, mBubMovableCenter.y + mTextRect.height() / 2, mTextPaint);
}	

第二样:连接状态绘制贝塞尔曲线①。

if (mBubbleState == BUBBLE_STATE_CONNECT) {
 //绘制静止的气泡
 canvas.drawCircle(mBubFixedCenter.x, mBubFixedCenter.y, mBubFixedRadius, mBubblePaint);
 //计算控制点的坐标
 int iAnchorX = (int) ((mBubMovableCenter.x + mBubFixedCenter.x) / 2);
 int iAnchorY = (int) ((mBubMovableCenter.y + mBubFixedCenter.y) / 2);

 float sinTheta = (mBubMovableCenter.y - mBubFixedCenter.y) / mDist;
 float cosTheta = (mBubMovableCenter.x - mBubFixedCenter.x) / mDist;

 //D
 float iBubFixedStartX = mBubFixedCenter.x - mBubFixedRadius * sinTheta;
 float iBubFixedStartY = mBubFixedCenter.y + mBubFixedRadius * cosTheta;
 //C
 float iBubMovableEndX = mBubMovableCenter.x - mBubMovableRadius * sinTheta;
 float iBubMovableEndY = mBubMovableCenter.y + mBubMovableRadius * cosTheta;

 //A
 float iBubFixedEndX = mBubFixedCenter.x + mBubFixedRadius * sinTheta;
 float iBubFixedEndY = mBubFixedCenter.y - mBubFixedRadius * cosTheta;
 //B
 float iBubMovableStartX = mBubMovableCenter.x + mBubMovableRadius * sinTheta;
 float iBubMovableStartY = mBubMovableCenter.y - mBubMovableRadius * cosTheta;

 mBezierPath.reset();
 mBezierPath.moveTo(iBubFixedStartX, iBubFixedStartY);
 mBezierPath.quadTo(iAnchorX, iAnchorY, iBubMovableEndX, iBubMovableEndY);

 mBezierPath.lineTo(iBubMovableStartX, iBubMovableStartY);
 mBezierPath.quadTo(iAnchorX, iAnchorY, iBubFixedEndX, iBubFixedEndY);
 mBezierPath.close();
 canvas.drawPath(mBezierPath, mBubblePaint);
}

第三样:消失状态执行爆炸动画

// 认为是消失状态,执行爆炸动画
if (mBubbleState == BUBBLE_STATE_DISMISS && mCurDrawableIndex < mBurstBitmapsArray.length) {
 mBurstRect.set(
  (int) (mBubMovableCenter.x - mBubMovableRadius),
  (int) (mBubMovableCenter.y - mBubMovableRadius),
  (int) (mBubMovableCenter.x + mBubMovableRadius),
  (int) (mBubMovableCenter.y + mBubMovableRadius));
 canvas.drawBitmap(mBurstBitmapsArray[mCurDrawableIndex], null, mBurstRect, mBubblePaint);
}

3.onTouchEvent中

按下:区分静止状态和连接状态

case MotionEvent.ACTION_DOWN:
 if (mBubbleState != BUBBLE_STATE_DISMISS) {
 mDist = (float) Math.hypot(event.getX() - mBubFixedCenter.x, event.getY() - mBubFixedCenter.y);
 if (mDist < mBubbleRadius + MOVE_OFFSET) {
  //加上MOVE_OFFSET是为了方便拖拽
  mBubbleState = BUBBLE_STATE_CONNECT;
 } else {
  mBubbleState = BUBBLE_STATE_DEFAULT;
 }
 }
 break;

移动:判断是否到了分离状态

case MotionEvent.ACTION_MOVE:
 if (mBubbleState != BUBBLE_STATE_DEFAULT) {
 mDist = (float) Math.hypot(event.getX() - mBubFixedCenter.x, event.getY() - mBubFixedCenter.y);
 mBubMovableCenter.x = event.getX();
 mBubMovableCenter.y = event.getY();
 if (mBubbleState == BUBBLE_STATE_CONNECT) {
  if (mDist < mMaxDist - MOVE_OFFSET) {
  mBubFixedRadius = mBubbleRadius - mDist / 8;
  } else {
  mBubbleState = BUBBLE_STATE_APART;
  }
 }
 invalidate();
 }
 break;

弹起:判断是否已经到了分离状态,分离状态爆炸,未分离反弹

case MotionEvent.ACTION_UP:
 if (mBubbleState == BUBBLE_STATE_CONNECT) {
 // 橡皮筋动画
 startBubbleRestAnim();
 } else if (mBubbleState == BUBBLE_STATE_APART) {
 if (mDist < 2 * mBubbleRadius){
  //反弹动画
  startBubbleRestAnim();
 }else{
  // 爆炸动画
  startBubbleBurstAnim();
 }
 }
 break;

4.反弹和爆炸动画

/**
 * 连接状态下松开手指,执行类似橡皮筋动画
 */
private void startBubbleRestAnim() {
 ValueAnimator anim = ValueAnimator.ofObject(new PointFEvaluator(),
  new PointF(mBubMovableCenter.x, mBubMovableCenter.y),
  new PointF(mBubFixedCenter.x, mBubFixedCenter.y));
 anim.setDuration(200);
 anim.setInterpolator(new OvershootInterpolator(5f));
 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
 @Override
 public void onAnimationUpdate(ValueAnimator animation) {
  mBubMovableCenter = (PointF) animation.getAnimatedValue();
  invalidate();
 }
 });
 anim.addListener(new AnimatorListenerAdapter() {
 @Override
 public void onAnimationEnd(Animator animation) {
  super.onAnimationEnd(animation);
  mBubbleState = BUBBLE_STATE_DEFAULT;
 }
 });
 anim.start();
}
/**
 * 爆炸动画
 */
 private void startBubbleBurstAnim() {
 //将气泡改成消失状态
 mBubbleState = BUBBLE_STATE_DISMISS;
 ValueAnimator animator = ValueAnimator.ofInt(0, mBurstBitmapsArray.length);
 animator.setInterpolator(new LinearInterpolator());
 animator.setDuration(500);
 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
  mCurDrawableIndex = (int) animation.getAnimatedValue();
  invalidate();
  }
 });
 animator.start();
 }

总结

注:①贝塞尔曲线参考博文

本文完,有需要参考的同学→文中Demo下载地址

本系列文章引导页点击这里

到此这篇关于Android未读消息拖动气泡示例代码详解的文章就介绍到这了,更多相关Android未读消息拖动气泡内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

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

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

  • Android实现气泡布局/弹窗效果 气泡尖角方向及偏移量可控

    Android 自定义布局实现气泡弹窗,可控制气泡尖角方向及偏移量. 效果图 实现 首先自定义一个气泡布局. /** * 气泡布局 */ public class BubbleRelativeLayout extends RelativeLayout { /** * 气泡尖角方向 */ public enum BubbleLegOrientation { TOP, LEFT, RIGHT, BOTTOM, NONE } public static int PADDING = 30; public

  • android 仿微信聊天气泡效果实现思路

    微信聊天窗口的信息效果类似iphone上的短信效果,以气泡的形式展现,在Android上,实现这种效果主要用到ListView和BaseAdapter,配合布局以及相关素材,就可以自己做出这个效果,素材可以下一个微信的APK,然后把后缀名改成zip,直接解压,就可以得到微信里面的所有素材了.首先看一下我实现的效果: 以下是工程目录结构: 接下来就是如何实现这个效果的代码: main.xml,这个是主布局文件,显示listview和上下两部分内容. 复制代码 代码如下: <?xml version

  • Android气泡效果实现方法

    本文实例讲述了Android气泡效果实现方法.分享给大家供大家参考,具体如下: 最近在看以前在eoe上收藏的一些源代码,准备将这些代码加上一些自己的注释,然后贴出来,方便自己日后查阅,和刚入门的人来学习. 今天先看一个气泡窗口,先看一下效果图和目录结构,然后再上代码 通过第一幅图,我们可以看到一个气泡窗口,这个窗口的结构是里面有2个TextView和1个气泡的背景,这个2个TextView放在了overlay_pop.xml文件中,气泡窗口是这个layout的背景,overlay_pop.xml

  • Android未读消息拖动气泡示例代码详解(附源码)

    前言 拖动清除未读消息可以说在很多应用中都很常见,也被用户广泛接受.本文是一个可以供参考的Demo,希望能有帮助. 提示:以下是本篇文章正文内容,下面案例可供参考 最终效果图及思路 实现关键: 气泡中间的两条边,分别是以ab,cd为数据点,G为控制点的贝塞尔曲线. 步骤: 绘制圆背景以及文本:连接情况绘制贝塞尔曲线:另外端点绘制一个圆 关键代码 1.定义,初始化等 状态:静止.连接.分离.消失 在onSizeChanged中初始化状态,固定气泡以及可动气泡的圆心 代码如下(示例): @Overr

  • Android Studio实现仿微信APP门户界面详解及源码

    目录 前言 界面分析 界面动态实现代码 静态界面实现 总结 前言 你好! 本文章主要介绍如何用Android Studio制作简易的门户界面,主要说明框架的各部分功能与实现过程,结尾处附有源码. 界面分析 注:按钮图标是从阿里矢量图标库获取,保存在drawable文件中调用. 首先根据我们的大致规划布局,我们可以先建立三个核心XML文件: top.xml: <?xml version="1.0" encoding="utf-8"?> <Linear

  • Android开发实现可拖动排序的ListView功能【附源码下载】

    本文实例讲述了Android开发实现可拖动排序的ListView功能.分享给大家供大家参考,具体如下: 一.上图 二.简述 1.需要实现的效果是长按右侧可拖动部分布局实现列表项的拖动排序 2.当点击列表项前面的单选按钮时,在该条目右侧显示删除图标,点击该图标删除当前条目. 三.实现思路 借助github上的开源代码drag-sort-listview-master加以改造. 四.主要源码展示 1.Activity代码 package com.gengducun.dslvdemo; import

  • Android编程实现泡泡聊天界面实例详解(附源码)

    本文实例讲述了Android编程实现泡泡聊天界面的方法.分享给大家供大家参考,具体如下: 昨天写了个界面,实现了Android泡泡聊天界面.运行结果如下,点击发送按钮,屏幕就显示Text的内容. 我也是在网上的一份源码的基础上更改的,整个泡泡界面的实现要点: (1)主界面其实就是一个List View (2)文字显示界面其实就使用了android:background="@drawable/incoming"这个东西.背景图片的格式是xxx.9.png,专门用来缩放的,不然显示效果非常

  • Android编程单元测试实例详解(附源码)

    本文实例讲述了Android编程单元测试.分享给大家供大家参考,具体如下: 完整实例代码代码点击此处本站下载. 本文是在上一篇文章<java编程之单元测试(Junit)实例分析>的基础上继续讲解android的单元测试,android源码中引入了java单元测试的框架(android源码目录:libcore\junit\src\main\java\junit\framework中可见),然后在java单元测试框架的基础上扩展属于android自己的测试框架.android具体框架类的关系图如下

  • Android编程之ICS式下拉菜单PopupWindow实现方法详解(附源码下载)

    本文实例讲述了Android编程之ICS式下拉菜单PopupWindow实现方法.分享给大家供大家参考,具体如下: 运行效果截图如下: 右边这个就是下拉菜单啦,看见有的地方叫他 ICS式下拉菜单,哎哟,不错哦! 下面先讲一下实现原理: 这种菜单实际上就是一个弹出式的菜单,于是我们想到android PopupWindow 类,给他设置一个view 在弹出来不就OK了吗. PopupWindow 的用法也很简单 主要方法: 步骤1.new 一个实例出来,我们使用这个构造方法即可, 复制代码 代码如

  • android编程之XML文件解析方法详解(附源码)

    本文实例讲述了android编程之XML文件解析方法.分享给大家供大家参考,具体如下: 在android开发中,经常用到去解析xml文件,常见的解析xml的方式有一下三种:SAX.Pull.Dom解析方式.最近做了一个android版的CSDN阅读器,用到了其中的两种(sax,pull),今天对android解析xml的这三种方式进行一次总结. 今天解析的xml示例(channels.xml)如下: <?xml version="1.0" encoding="utf-8

  • Android 未读消息的红点显示

    在很多APP里面,经常会看到未读消息的小红点,如下图: 这个功能用到的是一个控件,叫做BadgeView. BadgeView的用法很简单,直接把jar文件导入包中就可以使用,使用也很简单, badgeView = new BadgeView(this); //btn是控件 badgeView.setTargetView(btn); //设置相对位置 badgeView.setBadgeMargin(0, 5, 15, 0); //设置显示未读消息条数 badgeView.setBadgeCou

  • android 限制某个操作每天只能操作指定的次数(示例代码详解)

    最近有个需求,要求启动页的拦截页每天只能显示3次,超过三次就显示别的页面,然后到第二天才可以再次显示,利用SharePreferences保存天数和每天的次数,大概是思路是:判断 如果是同一天,就去拿保存的次数,当次数小于3才执弹出拦截页,然后,每次弹出,次数就加1,并且保存次数和当天的时间:如果不是同一天,就把次数赋值为1,并且把当天赋值给最后访问的时间,然后保存当前的次数.具体实现如下: package com.example.demo1.test; import android.suppo

  • 在Java中操作Zookeeper的示例代码详解

    依赖 <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.6.0</version> </dependency> 连接到zkServer //连接字符串,zkServer的ip.port,如果是集群逗号分隔 String connectStr = "192.

随机推荐